am dff4ec87: am e16a21c6: resolved conflicts for merge of 4a59376d to stage-aosp-master
* commit 'dff4ec87e7b942b7b4410f639fc74c2cffb386d6':
Track libcore API change.
diff --git a/Android.mk b/Android.mk
index 331b6c1..db6a704 100644
--- a/Android.mk
+++ b/Android.mk
@@ -119,6 +119,14 @@
core/java/android/content/pm/IPackageMoveObserver.aidl \
core/java/android/content/pm/IPackageStatsObserver.aidl \
core/java/android/database/IContentObserver.aidl \
+ core/java/android/hardware/ICameraService.aidl \
+ core/java/android/hardware/ICameraServiceListener.aidl \
+ core/java/android/hardware/ICamera.aidl \
+ core/java/android/hardware/ICameraClient.aidl \
+ core/java/android/hardware/IProCameraUser.aidl \
+ core/java/android/hardware/IProCameraCallbacks.aidl \
+ core/java/android/hardware/photography/ICameraDeviceUser.aidl \
+ core/java/android/hardware/photography/ICameraDeviceCallbacks.aidl \
core/java/android/hardware/ISerialManager.aidl \
core/java/android/hardware/display/IDisplayManager.aidl \
core/java/android/hardware/display/IDisplayManagerCallback.aidl \
@@ -151,6 +159,15 @@
core/java/android/os/IUserManager.aidl \
core/java/android/os/IVibratorService.aidl \
core/java/android/service/notification/INotificationListener.aidl \
+ core/java/android/print/IPrinterDiscoveryObserver.aidl \
+ core/java/android/print/IPrintAdapter.aidl \
+ core/java/android/print/IPrintClient.aidl \
+ core/java/android/print/IPrintResultCallback.aidl \
+ core/java/android/print/IPrintManager.aidl \
+ core/java/android/print/IPrintSpoolerService.aidl \
+ core/java/android/print/IPrintSpoolerServiceCallbacks.aidl \
+ core/java/android/printservice/IPrintService.aidl \
+ core/java/android/printservice/IPrintServiceClient.aidl \
core/java/android/service/dreams/IDreamManager.aidl \
core/java/android/service/dreams/IDreamService.aidl \
core/java/android/service/wallpaper/IWallpaperConnection.aidl \
@@ -161,6 +178,7 @@
core/java/android/view/accessibility/IAccessibilityManager.aidl \
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
+ core/java/android/view/IAssetAtlas.aidl \
core/java/android/view/IMagnificationCallbacks.aidl \
core/java/android/view/IInputFilter.aidl \
core/java/android/view/IInputFilterHost.aidl \
@@ -186,6 +204,9 @@
core/java/com/android/internal/backup/IObbBackupService.aidl \
core/java/com/android/internal/policy/IFaceLockCallback.aidl \
core/java/com/android/internal/policy/IFaceLockInterface.aidl \
+ core/java/com/android/internal/policy/IKeyguardShowCallback.aidl \
+ core/java/com/android/internal/policy/IKeyguardExitCallback.aidl \
+ core/java/com/android/internal/policy/IKeyguardService.aidl \
core/java/com/android/internal/os/IDropBoxManagerService.aidl \
core/java/com/android/internal/os/IResultReceiver.aidl \
core/java/com/android/internal/statusbar/IStatusBar.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 0c3ccec..893daf1 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -161,6 +161,8 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/framework-res_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/current.txt b/api/current.txt
index ecf38aa..e5156e8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23,6 +23,7 @@
field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
+ field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
@@ -70,6 +71,7 @@
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
+ field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
@@ -253,6 +255,7 @@
field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
+ field public static final int addPrintersActivity = 16843747; // 0x10103e3
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
field public static final int alertDialogIcon = 16843605; // 0x1010355
@@ -513,6 +516,7 @@
field public static final int freezesText = 16843116; // 0x101016c
field public static final int fromAlpha = 16843210; // 0x10101ca
field public static final int fromDegrees = 16843187; // 0x10101b3
+ field public static final int fromScene = 16843741; // 0x10103dd
field public static final int fromXDelta = 16843206; // 0x10101c6
field public static final int fromXScale = 16843202; // 0x10101c2
field public static final int fromYDelta = 16843208; // 0x10101c8
@@ -627,6 +631,7 @@
field public static final int keyboardLayout = 16843691; // 0x10103ab
field public static final int keyboardMode = 16843341; // 0x101024d
field public static final int keycode = 16842949; // 0x10100c5
+ field public static final int keyset = 16843739; // 0x10103db
field public static final int killAfterRestore = 16843420; // 0x101029c
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
@@ -951,6 +956,9 @@
field public static final int spinnersShown = 16843595; // 0x101034b
field public static final int splitMotionEvents = 16843503; // 0x10102ef
field public static final int src = 16843033; // 0x1010119
+ field public static final int ssp = 16843744; // 0x10103e0
+ field public static final int sspPattern = 16843746; // 0x10103e2
+ field public static final int sspPrefix = 16843745; // 0x10103e1
field public static final int stackFromBottom = 16843005; // 0x10100fd
field public static final int starStyle = 16842882; // 0x1010082
field public static final int startColor = 16843165; // 0x101019d
@@ -1013,6 +1021,7 @@
field public static final int targetActivity = 16843266; // 0x1010202
field public static final int targetClass = 16842799; // 0x101002f
field public static final int targetDescriptions = 16843680; // 0x10103a0
+ field public static final int targetID = 16843740; // 0x10103dc
field public static final int targetPackage = 16842785; // 0x1010021
field public static final int targetSdkVersion = 16843376; // 0x1010270
field public static final int taskAffinity = 16842770; // 0x1010012
@@ -1101,6 +1110,7 @@
field public static final int titleTextStyle = 16843512; // 0x10102f8
field public static final int toAlpha = 16843211; // 0x10101cb
field public static final int toDegrees = 16843188; // 0x10101b4
+ field public static final int toScene = 16843742; // 0x10103de
field public static final int toXDelta = 16843207; // 0x10101c7
field public static final int toXScale = 16843203; // 0x10101c3
field public static final int toYDelta = 16843209; // 0x10101c9
@@ -1115,6 +1125,7 @@
field public static final int transcriptMode = 16843008; // 0x1010100
field public static final int transformPivotX = 16843552; // 0x1010320
field public static final int transformPivotY = 16843553; // 0x1010321
+ field public static final int transition = 16843743; // 0x10103df
field public static final int translationX = 16843554; // 0x1010322
field public static final int translationY = 16843555; // 0x1010323
field public static final int type = 16843169; // 0x10101a1
@@ -1133,6 +1144,7 @@
field public static final int valueTo = 16843487; // 0x10102df
field public static final int valueType = 16843488; // 0x10102e0
field public static final int variablePadding = 16843157; // 0x1010195
+ field public static final int vendor = 16843748; // 0x10103e4
field public static final int versionCode = 16843291; // 0x101021b
field public static final int versionName = 16843292; // 0x101021c
field public static final int verticalCorrection = 16843322; // 0x101023a
@@ -2685,6 +2697,8 @@
method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void closeContextMenu();
method public void closeOptionsMenu();
+ method public void convertFromTranslucent();
+ method public void convertToTranslucent(android.app.Activity.TranslucentConversionListener);
method public android.app.PendingIntent createPendingResult(int, android.content.Intent, int);
method public final deprecated void dismissDialog(int);
method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
@@ -2808,6 +2822,7 @@
method public void recreate();
method public void registerForContextMenu(android.view.View);
method public final deprecated void removeDialog(int);
+ method public void reportFullyDrawn();
method public final boolean requestWindowFeature(int);
method public final void runOnUiThread(java.lang.Runnable);
method public void setContentView(int);
@@ -2869,6 +2884,10 @@
field public static final int RESULT_OK = -1; // 0xffffffff
}
+ public static abstract interface Activity.TranslucentConversionListener {
+ method public abstract void onTranslucentConversionComplete(boolean);
+ }
+
public deprecated class ActivityGroup extends android.app.Activity {
ctor public ActivityGroup();
ctor public ActivityGroup(boolean);
@@ -2877,6 +2896,8 @@
}
public class ActivityManager {
+ method public boolean clearApplicationUserData();
+ method public void dumpPackageState(java.io.FileDescriptor, java.lang.String);
method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo();
method public int getLargeMemoryClass();
method public int getLauncherLargeIconDensity();
@@ -2891,6 +2912,7 @@
method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
method public java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
+ method public boolean isLowRamDevice();
method public static boolean isRunningInTestHarness();
method public static boolean isUserAMonkey();
method public void killBackgroundProcesses(java.lang.String);
@@ -2946,6 +2968,7 @@
field public int id;
field public android.content.ComponentName origActivity;
field public int persistentId;
+ field public int stackId;
}
public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
@@ -3116,6 +3139,32 @@
ctor public AliasActivity();
}
+ public class AppOpsManager {
+ method public int checkOp(int, int, java.lang.String);
+ method public int checkOpNoThrow(int, int, java.lang.String);
+ method public void finishOp(int, int, java.lang.String);
+ method public void finishOp(int);
+ method public int noteOp(int, int, java.lang.String);
+ method public int noteOpNoThrow(int, int, java.lang.String);
+ method public static java.lang.String opToName(int);
+ method public int startOp(int, int, java.lang.String);
+ method public int startOpNoThrow(int, int, java.lang.String);
+ method public void startWatchingMode(int, java.lang.String, android.app.AppOpsManager.Callback);
+ method public void stopWatchingMode(android.app.AppOpsManager.Callback);
+ field public static final int MODE_ALLOWED = 0; // 0x0
+ field public static final int MODE_ERRORED = 2; // 0x2
+ field public static final int MODE_IGNORED = 1; // 0x1
+ field public static final int OP_COARSE_LOCATION = 0; // 0x0
+ field public static final int OP_FINE_LOCATION = 1; // 0x1
+ field public static final int OP_GPS = 2; // 0x2
+ field public static final int OP_MONITOR_LOCATION = 41; // 0x29
+ field public static final int OP_NONE = -1; // 0xffffffff
+ }
+
+ public static abstract interface AppOpsManager.Callback {
+ method public abstract void opChanged(int, java.lang.String);
+ }
+
public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
ctor public Application();
method public void onConfigurationChanged(android.content.res.Configuration);
@@ -3839,6 +3888,23 @@
field public static final int DEFAULT_LIGHTS = 4; // 0x4
field public static final int DEFAULT_SOUND = 1; // 0x1
field public static final int DEFAULT_VIBRATE = 2; // 0x2
+ field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
+ field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
+ field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+ field public static final java.lang.String EXTRA_PEOPLE = "android.people";
+ field public static final java.lang.String EXTRA_PICTURE = "android.picture";
+ field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
+ field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+ field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
+ field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+ field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
+ field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
+ field public static final java.lang.String EXTRA_SUB_TEXT = "android.subText";
+ field public static final java.lang.String EXTRA_SUMMARY_TEXT = "android.summaryText";
+ field public static final java.lang.String EXTRA_TEXT = "android.text";
+ field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
+ field public static final java.lang.String EXTRA_TITLE = "android.title";
+ field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
@@ -3853,12 +3919,14 @@
field public static final int PRIORITY_MAX = 2; // 0x2
field public static final int PRIORITY_MIN = -2; // 0xfffffffe
field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+ field public android.app.Notification.Action[] actions;
field public int audioStreamType;
field public android.widget.RemoteViews bigContentView;
field public android.app.PendingIntent contentIntent;
field public android.widget.RemoteViews contentView;
field public int defaults;
field public android.app.PendingIntent deleteIntent;
+ field public android.os.Bundle extras;
field public int flags;
field public android.app.PendingIntent fullScreenIntent;
field public int icon;
@@ -3876,6 +3944,18 @@
field public long when;
}
+ public static class Notification.Action implements android.os.Parcelable {
+ ctor public Notification.Action();
+ ctor public Notification.Action(int, java.lang.CharSequence, android.app.PendingIntent);
+ method public android.app.Notification.Action clone();
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public android.app.PendingIntent actionIntent;
+ field public int icon;
+ field public java.lang.CharSequence title;
+ }
+
public static class Notification.BigPictureStyle extends android.app.Notification.Style {
ctor public Notification.BigPictureStyle();
ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
@@ -3908,6 +3988,7 @@
method public android.app.Notification.Builder setContentTitle(java.lang.CharSequence);
method public android.app.Notification.Builder setDefaults(int);
method public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
+ method public android.app.Notification.Builder setExtras(android.os.Bundle);
method public android.app.Notification.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
method public android.app.Notification.Builder setLargeIcon(android.graphics.Bitmap);
method public android.app.Notification.Builder setLights(int, int, int);
@@ -5697,6 +5778,7 @@
field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
field public static final java.lang.String BLUETOOTH_SERVICE = "bluetooth";
+ field public static final java.lang.String CAMERA_SERVICE = "camera";
field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard";
field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity";
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
@@ -5722,6 +5804,7 @@
field public static final java.lang.String NOTIFICATION_SERVICE = "notification";
field public static final java.lang.String NSD_SERVICE = "servicediscovery";
field public static final java.lang.String POWER_SERVICE = "power";
+ field public static final java.lang.String PRINT_SERVICE = "print";
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";
@@ -6043,6 +6126,7 @@
field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
+ field public static final java.lang.String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW";
@@ -6085,6 +6169,7 @@
field public static final java.lang.String ACTION_MEDIA_UNMOUNTED = "android.intent.action.MEDIA_UNMOUNTED";
field public static final java.lang.String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED";
field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
+ field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
@@ -6105,6 +6190,7 @@
field public static final java.lang.String ACTION_PROVIDER_CHANGED = "android.intent.action.PROVIDER_CHANGED";
field public static final java.lang.String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK";
field public static final java.lang.String ACTION_REBOOT = "android.intent.action.REBOOT";
+ field public static final java.lang.String ACTION_RESTRICTIONS_PIN_CHALLENGE = "android.intent.action.RESTRICTIONS_PIN_CHALLENGE";
field public static final java.lang.String ACTION_RUN = "android.intent.action.RUN";
field public static final java.lang.String ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF";
field public static final java.lang.String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON";
@@ -6129,6 +6215,7 @@
field public static final java.lang.String ACTION_USER_INITIALIZE = "android.intent.action.USER_INITIALIZE";
field public static final java.lang.String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT";
field public static final java.lang.String ACTION_VIEW = "android.intent.action.VIEW";
+ field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final java.lang.String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
field public static final deprecated java.lang.String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
field public static final java.lang.String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
@@ -6192,6 +6279,7 @@
field public static final java.lang.String EXTRA_INTENT = "android.intent.extra.INTENT";
field public static final java.lang.String EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT";
field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY";
+ field public static final java.lang.String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
@@ -6243,6 +6331,7 @@
field public static final int FLAG_GRANT_READ_URI_PERMISSION = 1; // 0x1
field public static final int FLAG_GRANT_WRITE_URI_PERMISSION = 2; // 0x2
field public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 32; // 0x20
+ field public static final int FLAG_PERSIST_GRANT_URI_PERMISSION = 64; // 0x40
field public static final int FLAG_RECEIVER_FOREGROUND = 268435456; // 0x10000000
field public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824; // 0x40000000
field public static final int FLAG_RECEIVER_REPLACE_PENDING = 536870912; // 0x20000000
@@ -6276,6 +6365,7 @@
method public final void addDataAuthority(java.lang.String, java.lang.String);
method public final void addDataPath(java.lang.String, int);
method public final void addDataScheme(java.lang.String);
+ method public final void addDataSchemeSpecificPart(java.lang.String, int);
method public final void addDataType(java.lang.String) throws android.content.IntentFilter.MalformedMimeTypeException;
method public final java.util.Iterator<android.content.IntentFilter.AuthorityEntry> authoritiesIterator();
method public final java.util.Iterator<java.lang.String> categoriesIterator();
@@ -6283,6 +6373,7 @@
method public final int countCategories();
method public final int countDataAuthorities();
method public final int countDataPaths();
+ method public final int countDataSchemeSpecificParts();
method public final int countDataSchemes();
method public final int countDataTypes();
method public static android.content.IntentFilter create(java.lang.String, java.lang.String);
@@ -6293,6 +6384,7 @@
method public final android.content.IntentFilter.AuthorityEntry getDataAuthority(int);
method public final android.os.PatternMatcher getDataPath(int);
method public final java.lang.String getDataScheme(int);
+ method public final android.os.PatternMatcher getDataSchemeSpecificPart(int);
method public final java.lang.String getDataType(int);
method public final int getPriority();
method public final boolean hasAction(java.lang.String);
@@ -6300,6 +6392,7 @@
method public final boolean hasDataAuthority(android.net.Uri);
method public final boolean hasDataPath(java.lang.String);
method public final boolean hasDataScheme(java.lang.String);
+ method public final boolean hasDataSchemeSpecificPart(java.lang.String);
method public final boolean hasDataType(java.lang.String);
method public final int match(android.content.ContentResolver, android.content.Intent, boolean, java.lang.String);
method public final int match(java.lang.String, java.lang.String, java.lang.String, android.net.Uri, java.util.Set<java.lang.String>, java.lang.String);
@@ -6309,6 +6402,7 @@
method public final int matchDataAuthority(android.net.Uri);
method public final java.util.Iterator<android.os.PatternMatcher> pathsIterator();
method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public final java.util.Iterator<android.os.PatternMatcher> schemeSpecificPartsIterator();
method public final java.util.Iterator<java.lang.String> schemesIterator();
method public final void setPriority(int);
method public final java.util.Iterator<java.lang.String> typesIterator();
@@ -6323,6 +6417,7 @@
field public static final int MATCH_CATEGORY_PATH = 5242880; // 0x500000
field public static final int MATCH_CATEGORY_PORT = 4194304; // 0x400000
field public static final int MATCH_CATEGORY_SCHEME = 2097152; // 0x200000
+ field public static final int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 5767168; // 0x580000
field public static final int MATCH_CATEGORY_TYPE = 6291456; // 0x600000
field public static final int NO_MATCH_ACTION = -3; // 0xfffffffd
field public static final int NO_MATCH_CATEGORY = -4; // 0xfffffffc
@@ -6600,6 +6695,59 @@
method public abstract void onStatusChanged(int);
}
+ public class UndoManager {
+ ctor public UndoManager();
+ method public void addOperation(android.content.UndoOperation<?>, int);
+ method public void beginUpdate(java.lang.CharSequence);
+ method public int commitState(android.content.UndoOwner);
+ method public int countRedos(android.content.UndoOwner[]);
+ method public int countUndos(android.content.UndoOwner[]);
+ method public void endUpdate();
+ method public int forgetRedos(android.content.UndoOwner[], int);
+ method public int forgetUndos(android.content.UndoOwner[], int);
+ method public int getHistorySize();
+ method public android.content.UndoOperation<?> getLastOperation(int);
+ method public android.content.UndoOperation<?> getLastOperation(android.content.UndoOwner, int);
+ method public T getLastOperation(java.lang.Class<T>, android.content.UndoOwner, int);
+ method public android.content.UndoOwner getOwner(java.lang.String, java.lang.Object);
+ method public java.lang.CharSequence getRedoLabel(android.content.UndoOwner[]);
+ method public java.lang.CharSequence getUndoLabel(android.content.UndoOwner[]);
+ method public int getUpdateNestingLevel();
+ method public boolean hasOperation(android.content.UndoOwner);
+ method public boolean isInUndo();
+ method public boolean isInUpdate();
+ method public int redo(android.content.UndoOwner[], int);
+ method public void restoreInstanceState(android.os.Parcelable);
+ method public android.os.Parcelable saveInstanceState();
+ method public void setHistorySize(int);
+ method public void setUndoLabel(java.lang.CharSequence);
+ method public void suggestUndoLabel(java.lang.CharSequence);
+ method public boolean uncommitState(int, android.content.UndoOwner);
+ method public int undo(android.content.UndoOwner[], int);
+ field public static final int MERGE_MODE_ANY = 2; // 0x2
+ field public static final int MERGE_MODE_NONE = 0; // 0x0
+ field public static final int MERGE_MODE_UNIQUE = 1; // 0x1
+ }
+
+ public abstract class UndoOperation implements android.os.Parcelable {
+ ctor public UndoOperation(android.content.UndoOwner);
+ ctor protected UndoOperation(android.os.Parcel, java.lang.ClassLoader);
+ method public boolean allowMerge();
+ method public abstract void commit();
+ method public int describeContents();
+ method public android.content.UndoOwner getOwner();
+ method public DATA getOwnerData();
+ method public boolean hasData();
+ method public boolean matchOwner(android.content.UndoOwner);
+ method public abstract void redo();
+ method public abstract void undo();
+ }
+
+ public class UndoOwner {
+ method public java.lang.Object getData();
+ method public java.lang.String getTag();
+ }
+
public class UriMatcher {
ctor public UriMatcher(int);
method public void addURI(java.lang.String, java.lang.String, int);
@@ -6744,6 +6892,7 @@
ctor public ComponentInfo(android.content.pm.ComponentInfo);
ctor protected ComponentInfo(android.os.Parcel);
method public final int getIconResource();
+ method public final int getLogoResource();
method public boolean isEnabled();
field public android.content.pm.ApplicationInfo applicationInfo;
field public int descriptionRes;
@@ -7159,7 +7308,7 @@
package android.content.res {
- public class AssetFileDescriptor implements android.os.Parcelable {
+ public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
ctor public AssetFileDescriptor(android.os.ParcelFileDescriptor, long, long);
method public void close() throws java.io.IOException;
method public java.io.FileInputStream createInputStream() throws java.io.IOException;
@@ -7571,6 +7720,7 @@
method public abstract float getFloat(int);
method public abstract int getInt(int);
method public abstract long getLong(int);
+ method public abstract android.net.Uri getNotificationUri();
method public abstract int getPosition();
method public abstract short getShort(int);
method public abstract java.lang.String getString(int);
@@ -7676,6 +7826,7 @@
method public float getFloat(int);
method public int getInt(int);
method public long getLong(int);
+ method public android.net.Uri getNotificationUri();
method public int getPosition();
method public short getShort(int);
method public java.lang.String getString(int);
@@ -8567,6 +8718,7 @@
method public void eraseColor(int);
method public android.graphics.Bitmap extractAlpha();
method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]);
+ method public final int getAllocationByteCount();
method public final int getByteCount();
method public final android.graphics.Bitmap.Config getConfig();
method public int getDensity();
@@ -8902,8 +9054,10 @@
field public static final int JPEG = 256; // 0x100
field public static final int NV16 = 16; // 0x10
field public static final int NV21 = 17; // 0x11
+ field public static final int RAW_SENSOR = 32; // 0x20
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
+ field public static final int YUV_420_888 = 35; // 0x23
field public static final int YUY2 = 20; // 0x14
field public static final int YV12 = 842094169; // 0x32315659
}
@@ -9031,12 +9185,16 @@
}
public class NinePatch {
+ ctor public NinePatch(android.graphics.Bitmap, byte[]);
ctor public NinePatch(android.graphics.Bitmap, byte[], java.lang.String);
method public void draw(android.graphics.Canvas, android.graphics.RectF);
method public void draw(android.graphics.Canvas, android.graphics.Rect);
method public void draw(android.graphics.Canvas, android.graphics.Rect, android.graphics.Paint);
+ method public android.graphics.Bitmap getBitmap();
method public int getDensity();
method public int getHeight();
+ method public java.lang.String getName();
+ method public android.graphics.Paint getPaint();
method public final android.graphics.Region getTransparentRegion(android.graphics.Rect);
method public int getWidth();
method public final boolean hasAlpha();
@@ -9698,7 +9856,6 @@
ctor public ColorDrawable();
ctor public ColorDrawable(int);
method public void draw(android.graphics.Canvas);
- method public int getAlpha();
method public int getColor();
method public int getOpacity();
method public void setAlpha(int);
@@ -9718,6 +9875,7 @@
method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static android.graphics.drawable.Drawable createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public abstract void draw(android.graphics.Canvas);
+ method public int getAlpha();
method public final android.graphics.Rect getBounds();
method public android.graphics.drawable.Drawable.Callback getCallback();
method public int getChangingConfigurations();
@@ -9791,6 +9949,7 @@
method public synchronized boolean canConstantState();
method protected void computeConstantSize();
method public int getChangingConfigurations();
+ method public final android.graphics.drawable.Drawable getChild(int);
method public final int getChildCount();
method public final android.graphics.drawable.Drawable[] getChildren();
method public final int getConstantHeight();
@@ -10432,6 +10591,7 @@
package android.hardware.display {
public final class DisplayManager {
+ method public android.hardware.display.VirtualDisplay createPrivateVirtualDisplay(java.lang.String, int, int, int, android.view.Surface);
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
method public android.view.Display[] getDisplays(java.lang.String);
@@ -10446,6 +10606,11 @@
method public abstract void onDisplayRemoved(int);
}
+ public final class VirtualDisplay {
+ method public android.view.Display getDisplay();
+ method public void release();
+ }
+
}
package android.hardware.input {
@@ -10525,6 +10690,135 @@
}
+package android.hardware.photography {
+
+ public class CameraAccessException extends android.util.AndroidException {
+ ctor public CameraAccessException(int);
+ ctor public CameraAccessException(int, java.lang.String);
+ ctor public CameraAccessException(int, java.lang.String, java.lang.Throwable);
+ ctor public CameraAccessException(int, java.lang.Throwable);
+ method public final int getReason();
+ field public static final int CAMERA_DISABLED = 3; // 0x3
+ field public static final int CAMERA_DISCONNECTED = 4; // 0x4
+ field public static final int CAMERA_IN_USE = 1; // 0x1
+ field public static final int MAX_CAMERAS_IN_USE = 2; // 0x2
+ }
+
+ public abstract interface CameraDevice implements java.lang.AutoCloseable {
+ method public abstract void capture(android.hardware.photography.CaptureRequest, android.hardware.photography.CameraDevice.CaptureListener) throws android.hardware.photography.CameraAccessException;
+ method public abstract void captureBurst(java.util.List<android.hardware.photography.CaptureRequest>, android.hardware.photography.CameraDevice.CaptureListener) throws android.hardware.photography.CameraAccessException;
+ method public abstract void close() throws java.lang.Exception;
+ method public abstract void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.photography.CameraAccessException;
+ method public abstract android.hardware.photography.CaptureRequest createCaptureRequest(int) throws android.hardware.photography.CameraAccessException;
+ method public abstract android.hardware.photography.CameraProperties getProperties() throws android.hardware.photography.CameraAccessException;
+ method public abstract void setErrorListener(android.hardware.photography.CameraDevice.ErrorListener);
+ method public abstract void setRepeatingBurst(java.util.List<android.hardware.photography.CaptureRequest>, android.hardware.photography.CameraDevice.CaptureListener) throws android.hardware.photography.CameraAccessException;
+ method public abstract void setRepeatingRequest(android.hardware.photography.CaptureRequest, android.hardware.photography.CameraDevice.CaptureListener) throws android.hardware.photography.CameraAccessException;
+ method public abstract void stopRepeating() throws android.hardware.photography.CameraAccessException;
+ method public abstract void waitUntilIdle() throws android.hardware.photography.CameraAccessException;
+ field public static final int TEMPLATE_MANUAL = 5; // 0x5
+ field public static final int TEMPLATE_PREVIEW = 1; // 0x1
+ field public static final int TEMPLATE_RECORD = 3; // 0x3
+ field public static final int TEMPLATE_STILL_CAPTURE = 2; // 0x2
+ field public static final int TEMPLATE_VIDEO_SNAPSHOT = 4; // 0x4
+ }
+
+ public static abstract interface CameraDevice.CaptureListener {
+ method public abstract void onCaptureComplete(android.hardware.photography.CameraDevice, android.hardware.photography.CaptureRequest, android.hardware.photography.CaptureResult);
+ method public abstract void onCaptureFailed(android.hardware.photography.CameraDevice, android.hardware.photography.CaptureRequest);
+ }
+
+ public static abstract interface CameraDevice.ErrorListener {
+ method public abstract void onCameraDeviceError(android.hardware.photography.CameraDevice, int);
+ field public static final int DEVICE_DISCONNECTED = 1; // 0x1
+ field public static final int DEVICE_ERROR = 2; // 0x2
+ field public static final int SERVICE_ERROR = 3; // 0x3
+ }
+
+ public final class CameraManager {
+ method public android.hardware.photography.CameraProperties getCameraProperties(java.lang.String) throws android.hardware.photography.CameraAccessException;
+ method public java.lang.String[] getDeviceIdList() throws android.hardware.photography.CameraAccessException;
+ method public android.hardware.photography.CameraDevice openCamera(java.lang.String) throws android.hardware.photography.CameraAccessException;
+ method public void registerCameraListener(android.hardware.photography.CameraManager.CameraListener);
+ method public void unregisterCameraListener(android.hardware.photography.CameraManager.CameraListener);
+ }
+
+ public static abstract interface CameraManager.CameraListener {
+ method public abstract void onCameraAvailable(java.lang.String);
+ method public abstract void onCameraUnavailable(java.lang.String);
+ }
+
+ public class CameraMetadata implements java.lang.AutoCloseable android.os.Parcelable {
+ ctor public CameraMetadata();
+ method public void close() throws java.lang.Exception;
+ method public int describeContents();
+ method public T get(android.hardware.photography.CameraMetadata.Key<T>);
+ method public void readFromParcel(android.os.Parcel);
+ method public void set(android.hardware.photography.CameraMetadata.Key<T>, T);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static class CameraMetadata.Key {
+ ctor public CameraMetadata.Key(java.lang.String);
+ method public final boolean equals(java.lang.Object);
+ method public final java.lang.String getName();
+ method public final int hashCode();
+ }
+
+ public final class CameraProperties extends android.hardware.photography.CameraMetadata {
+ ctor public CameraProperties();
+ field public static final android.hardware.photography.CameraMetadata.Key INFO_IDENTIFIER;
+ field public static final android.hardware.photography.CameraMetadata.Key INFO_MODEL;
+ field public static final android.hardware.photography.CameraMetadata.Key INFO_REMOVABLE;
+ field public static final android.hardware.photography.CameraMetadata.Key INFO_SUPPORTED_HARDWARE_LEVEL;
+ field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
+ field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
+ field public static final android.hardware.photography.CameraMetadata.Key SCALER_AVAILABLE_FORMATS;
+ field public static final android.hardware.photography.CameraMetadata.Key SCALER_AVAILABLE_JPEG_SIZES;
+ field public static final android.hardware.photography.CameraMetadata.Key SCALER_AVAILABLE_PROCESSED_SIZES;
+ field public static final android.hardware.photography.CameraMetadata.Key SCALER_AVAILABLE_RAW_SIZES;
+ field public static final android.hardware.photography.CameraMetadata.Key SENSOR_ACTIVE_ARRAY_SIZE;
+ field public static final android.hardware.photography.CameraMetadata.Key SENSOR_PIXEL_ARRAY_SIZE;
+ }
+
+ public final class CaptureRequest extends android.hardware.photography.CameraMetadata implements android.os.Parcelable {
+ method public void addTarget(android.view.Surface);
+ method public void removeTarget(android.view.Surface);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final android.hardware.photography.CameraMetadata.Key SENSOR_EXPOSURE_TIME;
+ field public static final android.hardware.photography.CameraMetadata.Key SENSOR_SENSITIVITY;
+ }
+
+ public final class CaptureResult extends android.hardware.photography.CameraMetadata {
+ field public static final android.hardware.photography.CameraMetadata.Key CONTROL_AE_STATE;
+ field public static final int CONTROL_AE_STATE_CONVERGED = 2; // 0x2
+ field public static final int CONTROL_AE_STATE_FLASH_REQUIRED = 4; // 0x4
+ field public static final int CONTROL_AE_STATE_INACTIVE = 0; // 0x0
+ field public static final int CONTROL_AE_STATE_LOCKED = 3; // 0x3
+ field public static final int CONTROL_AE_STATE_PRECAPTURE = 5; // 0x5
+ field public static final int CONTROL_AE_STATE_SEARCHING = 1; // 0x1
+ field public static final android.hardware.photography.CameraMetadata.Key SENSOR_TIMESTAMP;
+ field public static final android.hardware.photography.CameraMetadata.Key STATISTICS_DETECTED_FACES;
+ }
+
+ public static class CaptureResult.Face {
+ ctor public CaptureResult.Face();
+ method public android.graphics.Rect getBounds();
+ method public int getId();
+ method public android.graphics.Point getLeftEye();
+ method public android.graphics.Point getMouth();
+ method public android.graphics.Point getRightEye();
+ method public int getScore();
+ }
+
+ public final class Size {
+ method public final int getHeight();
+ method public final int getWidth();
+ }
+
+}
+
package android.hardware.usb {
public class UsbAccessory implements android.os.Parcelable {
@@ -11406,7 +11700,7 @@
method public static float getMaxVolume();
method public static int getMinBufferSize(int, int, int);
method public static float getMinVolume();
- method protected int getNativeFrameCount();
+ method protected deprecated int getNativeFrameCount();
method public static int getNativeOutputSampleRate(int);
method public int getNotificationMarkerPosition();
method public int getPlayState();
@@ -11428,7 +11722,7 @@
method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener, android.os.Handler);
method public int setPlaybackRate(int);
method public int setPositionNotificationPeriod(int);
- method protected void setState(int);
+ method protected deprecated void setState(int);
method public int setStereoVolume(float, float);
method public void stop() throws java.lang.IllegalStateException;
method public int write(byte[], int, int);
@@ -11561,6 +11855,38 @@
field public static final int EULER_Z = 2; // 0x2
}
+ public abstract interface Image implements java.lang.AutoCloseable {
+ method public abstract void close();
+ method public abstract int getFormat();
+ method public abstract int getHeight();
+ method public abstract android.media.Image.Plane[] getPlanes();
+ method public abstract long getTimestamp();
+ method public abstract int getWidth();
+ }
+
+ public static abstract interface Image.Plane {
+ method public abstract java.nio.ByteBuffer getBuffer();
+ method public abstract int getPixelStride();
+ method public abstract int getRowStride();
+ }
+
+ public final class ImageReader implements java.lang.AutoCloseable {
+ ctor public ImageReader(int, int, int, int);
+ method public void close();
+ method public int getHeight();
+ method public int getImageFormat();
+ method public int getMaxImages();
+ method public android.media.Image getNextImage();
+ method public android.view.Surface getSurface();
+ method public int getWidth();
+ method public void releaseImage(android.media.Image);
+ method public void setImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler);
+ }
+
+ public static abstract interface ImageReader.OnImageAvailableListener {
+ method public abstract void onImageAvailable(android.media.ImageReader);
+ }
+
public class JetPlayer {
method public boolean clearQueue();
method public java.lang.Object clone() throws java.lang.CloneNotSupportedException;
@@ -12414,7 +12740,7 @@
method public android.database.Cursor getCursor();
method public static int getDefaultType(android.net.Uri);
method public static android.net.Uri getDefaultUri(int);
- method public boolean getIncludeDrm();
+ method public deprecated boolean getIncludeDrm();
method public android.media.Ringtone getRingtone(int);
method public static android.media.Ringtone getRingtone(android.content.Context, android.net.Uri);
method public int getRingtonePosition(android.net.Uri);
@@ -12424,14 +12750,14 @@
method public int inferStreamType();
method public static boolean isDefault(android.net.Uri);
method public static void setActualDefaultRingtoneUri(android.content.Context, int, android.net.Uri);
- method public void setIncludeDrm(boolean);
+ method public deprecated void setIncludeDrm(boolean);
method public void setStopPreviousRingtone(boolean);
method public void setType(int);
method public void stopPreviousRingtone();
field public static final java.lang.String ACTION_RINGTONE_PICKER = "android.intent.action.RINGTONE_PICKER";
field public static final java.lang.String EXTRA_RINGTONE_DEFAULT_URI = "android.intent.extra.ringtone.DEFAULT_URI";
field public static final java.lang.String EXTRA_RINGTONE_EXISTING_URI = "android.intent.extra.ringtone.EXISTING_URI";
- field public static final java.lang.String EXTRA_RINGTONE_INCLUDE_DRM = "android.intent.extra.ringtone.INCLUDE_DRM";
+ field public static final deprecated java.lang.String EXTRA_RINGTONE_INCLUDE_DRM = "android.intent.extra.ringtone.INCLUDE_DRM";
field public static final java.lang.String EXTRA_RINGTONE_PICKED_URI = "android.intent.extra.ringtone.PICKED_URI";
field public static final java.lang.String EXTRA_RINGTONE_SHOW_DEFAULT = "android.intent.extra.ringtone.SHOW_DEFAULT";
field public static final java.lang.String EXTRA_RINGTONE_SHOW_SILENT = "android.intent.extra.ringtone.SHOW_SILENT";
@@ -16612,7 +16938,7 @@
}
public class Matrix {
- ctor public Matrix();
+ ctor public deprecated Matrix();
method public static void frustumM(float[], int, float, float, float, float, float, float);
method public static boolean invertM(float[], int, float[], int);
method public static float length(float, float, float);
@@ -16786,6 +17112,7 @@
field public static final int JELLY_BEAN = 16; // 0x10
field public static final int JELLY_BEAN_MR1 = 17; // 0x11
field public static final int JELLY_BEAN_MR2 = 18; // 0x12
+ field public static final int KEY_LIME_PIE = 10000; // 0x2710
}
public final class Bundle implements java.lang.Cloneable android.os.Parcelable {
@@ -16998,12 +17325,18 @@
ctor public Debug.MemoryInfo();
method public int describeContents();
method public static java.lang.String getOtherLabel(int);
+ method public int getOtherPrivateClean(int);
method public int getOtherPrivateDirty(int);
method public int getOtherPss(int);
+ method public int getOtherSharedClean(int);
method public int getOtherSharedDirty(int);
+ method public int getOtherSwappablePss(int);
+ method public int getTotalPrivateClean();
method public int getTotalPrivateDirty();
method public int getTotalPss();
+ method public int getTotalSharedClean();
method public int getTotalSharedDirty();
+ method public int getTotalSwappablePss();
method public void readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -17668,6 +18001,7 @@
method public java.lang.String getUserName();
method public android.os.Bundle getUserRestrictions();
method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
+ method public boolean hasRestrictionsPin();
method public boolean isUserAGoat();
method public boolean isUserRunning(android.os.UserHandle);
method public boolean isUserRunningOrStopping(android.os.UserHandle);
@@ -18095,6 +18429,300 @@
}
+package android.print {
+
+ public final class PageRange implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getEnd();
+ method public int getStart();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.print.PageRange ALL_PAGES;
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public abstract class PrintAdapter {
+ ctor public PrintAdapter();
+ method public abstract android.print.PrintAdapterInfo getInfo();
+ method public void onFinish();
+ method public abstract void onPrint(java.util.List<android.print.PageRange>, java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintAdapter.PrintResultCallback);
+ method public boolean onPrintAttributesChanged(android.print.PrintAttributes);
+ method public void onStart();
+ }
+
+ public static abstract class PrintAdapter.PrintResultCallback {
+ method public void onPrintFailed(java.lang.CharSequence);
+ method public void onPrintFinished(java.util.List<android.print.PageRange>);
+ }
+
+ public final class PrintAdapterInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getFlags();
+ method public int getPageCount();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int PAGE_COUNT_UNKNOWN = -1; // 0xffffffff
+ }
+
+ public static final class PrintAdapterInfo.Builder {
+ ctor public PrintAdapterInfo.Builder();
+ method public android.print.PrintAdapterInfo create();
+ method public android.print.PrintAdapterInfo.Builder setFlags(int);
+ method public android.print.PrintAdapterInfo.Builder setPageCount(int);
+ }
+
+ public final class PrintAttributes implements android.os.Parcelable {
+ method public void clear();
+ method public int describeContents();
+ method public int getColorMode();
+ method public int getCopies();
+ method public int getDuplexMode();
+ method public int getFittingMode();
+ method public android.print.PrintAttributes.Tray getInputTray();
+ method public android.print.PrintAttributes.Margins getMargins();
+ method public android.print.PrintAttributes.MediaSize getMediaSize();
+ method public int getOrientation();
+ method public android.print.PrintAttributes.Tray getOutputTray();
+ method public android.print.PrintAttributes.Resolution getResolution();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int COLOR_MODE_COLOR = 2; // 0x2
+ field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int DUPLEX_MODE_LONG_EDGE = 2; // 0x2
+ field public static final int DUPLEX_MODE_NONE = 1; // 0x1
+ field public static final int DUPLEX_MODE_SHORT_EDGE = 4; // 0x4
+ field public static final int FITTING_MODE_FIT_TO_PAGE = 2; // 0x2
+ field public static final int FITTING_MODE_NONE = 1; // 0x1
+ field public static final int ORIENTATION_LANDSCAPE = 2; // 0x2
+ field public static final int ORIENTATION_PORTRAIT = 1; // 0x1
+ }
+
+ public static final class PrintAttributes.Builder {
+ ctor public PrintAttributes.Builder();
+ method public android.print.PrintAttributes create();
+ method public android.print.PrintAttributes.Builder setColorMode(int);
+ method public android.print.PrintAttributes.Builder setCopyCount(int);
+ method public android.print.PrintAttributes.Builder setDuplexMode(int);
+ method public android.print.PrintAttributes.Builder setFittingMode(int);
+ method public android.print.PrintAttributes.Builder setInputTray(android.print.PrintAttributes.Tray);
+ method public android.print.PrintAttributes.Builder setMargins(android.print.PrintAttributes.Margins);
+ method public android.print.PrintAttributes.Builder setMediaSize(android.print.PrintAttributes.MediaSize);
+ method public android.print.PrintAttributes.Builder setOrientation(int);
+ method public android.print.PrintAttributes.Builder setOutputTray(android.print.PrintAttributes.Tray);
+ method public android.print.PrintAttributes.Builder setResolution(android.print.PrintAttributes.Resolution);
+ }
+
+ public static final class PrintAttributes.Margins {
+ ctor public PrintAttributes.Margins(int, int, int, int);
+ method public int getBottomMils();
+ method public int getLeftMils();
+ method public int getRightMils();
+ method public int getTopMils();
+ }
+
+ public static final class PrintAttributes.MediaSize {
+ ctor public PrintAttributes.MediaSize(java.lang.String, java.lang.String, int, int, int);
+ method public int getHeightMils();
+ method public java.lang.String getId();
+ method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
+ method public int getWidthMils();
+ field public static final android.print.PrintAttributes.MediaSize ISO_A0;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A1;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A10;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A2;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A3;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A4;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A5;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A6;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A7;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A8;
+ field public static final android.print.PrintAttributes.MediaSize ISO_A9;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B0;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B1;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B10;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B2;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B3;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B4;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B5;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B6;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B7;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B8;
+ field public static final android.print.PrintAttributes.MediaSize ISO_B9;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C0;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C1;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C10;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C2;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C3;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C4;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C5;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C6;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C7;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C8;
+ field public static final android.print.PrintAttributes.MediaSize ISO_C9;
+ field public static final android.print.PrintAttributes.MediaSize NA_GOVT_LETTER;
+ field public static final android.print.PrintAttributes.MediaSize NA_JUNIOR_LEGAL;
+ field public static final android.print.PrintAttributes.MediaSize NA_LEDGER;
+ field public static final android.print.PrintAttributes.MediaSize NA_LEGAL;
+ field public static final android.print.PrintAttributes.MediaSize NA_LETTER;
+ field public static final android.print.PrintAttributes.MediaSize NA_TBLOID;
+ }
+
+ public static final class PrintAttributes.Resolution {
+ ctor public PrintAttributes.Resolution(java.lang.String, java.lang.String, int, int, int);
+ method public int getHorizontalDpi();
+ method public java.lang.String getId();
+ method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
+ method public int getVerticalDpi();
+ }
+
+ public static final class PrintAttributes.Tray {
+ ctor public PrintAttributes.Tray(java.lang.String, java.lang.String, int);
+ method public java.lang.String getId();
+ method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
+ }
+
+ public final class PrintJob {
+ method public void cancel();
+ method public int getId();
+ method public android.print.PrintJobInfo getInfo();
+ }
+
+ public final class PrintJobInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.print.PrintAttributes getAttributes();
+ method public int getId();
+ method public java.lang.CharSequence getLabel();
+ method public android.print.PageRange[] getPages();
+ method public android.print.PrinterId getPrinterId();
+ method public int getState();
+ method public java.lang.String getTag();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int PRINT_JOB_ID_UNDEFINED = -1; // 0xffffffff
+ field public static final int STATE_CANCELED = 6; // 0x6
+ field public static final int STATE_COMPLETED = 4; // 0x4
+ field public static final int STATE_CREATED = 1; // 0x1
+ field public static final int STATE_FAILED = 5; // 0x5
+ field public static final int STATE_QUEUED = 2; // 0x2
+ field public static final int STATE_STARTED = 3; // 0x3
+ }
+
+ public final class PrintManager {
+ method public java.util.List<android.print.PrintJob> getPrintJobs();
+ method public android.print.PrintJob print(java.lang.String, java.io.File, android.print.PrintAttributes);
+ method public android.print.PrintJob print(java.lang.String, android.print.PrintAdapter, android.print.PrintAttributes);
+ }
+
+ public final class PrinterId implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getLocalId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public final class PrinterInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getColorModes();
+ method public void getDefaults(android.print.PrintAttributes);
+ method public int getDuplexModes();
+ method public int getFittingModes();
+ method public android.print.PrinterId getId();
+ method public java.util.List<android.print.PrintAttributes.Tray> getInputTrays();
+ method public java.lang.CharSequence getLabel();
+ method public java.util.List<android.print.PrintAttributes.MediaSize> getMediaSizes();
+ method public android.print.PrintAttributes.Margins getMinMargins();
+ method public int getOrientations();
+ method public java.util.List<android.print.PrintAttributes.Tray> getOutputTrays();
+ method public java.util.List<android.print.PrintAttributes.Resolution> getResolutions();
+ method public int getStatus();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int STATUS_READY = 1; // 0x1
+ }
+
+ public static final class PrinterInfo.Builder {
+ ctor public PrinterInfo.Builder(android.print.PrinterId, java.lang.CharSequence);
+ method public android.print.PrinterInfo.Builder addInputTray(android.print.PrintAttributes.Tray, boolean);
+ method public android.print.PrinterInfo.Builder addMediaSize(android.print.PrintAttributes.MediaSize, boolean);
+ method public android.print.PrinterInfo.Builder addOutputTray(android.print.PrintAttributes.Tray, boolean);
+ method public android.print.PrinterInfo.Builder addResolution(android.print.PrintAttributes.Resolution, boolean);
+ method public android.print.PrinterInfo create();
+ method public android.print.PrinterInfo.Builder setColorModes(int, int);
+ method public android.print.PrinterInfo.Builder setDuplexModes(int, int);
+ method public android.print.PrinterInfo.Builder setFittingModes(int, int);
+ method public android.print.PrinterInfo.Builder setMinMargins(android.print.PrintAttributes.Margins, android.print.PrintAttributes.Margins);
+ method public android.print.PrinterInfo.Builder setOrientations(int, int);
+ method public android.print.PrinterInfo.Builder setStatus(int);
+ }
+
+}
+
+package android.print.pdf {
+
+ public final class PdfDocument {
+ method public void close();
+ method public void finishPage(android.print.pdf.PdfDocument.Page);
+ method public java.util.List<android.print.pdf.PdfDocument.PageInfo> getPages();
+ method public static android.print.pdf.PdfDocument open();
+ method public android.print.pdf.PdfDocument.Page startPage(android.print.pdf.PdfDocument.PageInfo);
+ method public void writeTo(java.io.OutputStream);
+ }
+
+ public static final class PdfDocument.Page {
+ method public android.graphics.Canvas getCanvas();
+ method public android.print.pdf.PdfDocument.PageInfo getInfo();
+ }
+
+ public static final class PdfDocument.PageInfo {
+ method public android.graphics.Rect getContentSize();
+ method public int getDesity();
+ method public android.graphics.Matrix getInitialTransform();
+ method public int getPageNumber();
+ method public android.graphics.Rect getPageSize();
+ }
+
+ public static final class PdfDocument.PageInfo.Builder {
+ ctor public PdfDocument.PageInfo.Builder(android.graphics.Rect, int, int);
+ method public android.print.pdf.PdfDocument.PageInfo create();
+ method public android.print.pdf.PdfDocument.PageInfo.Builder setContentSize(android.graphics.Rect);
+ method public android.print.pdf.PdfDocument.PageInfo.Builder setInitialTransform(android.graphics.Matrix);
+ }
+
+}
+
+package android.printservice {
+
+ public final class PrintJob {
+ method public boolean cancel();
+ method public boolean complete();
+ method public boolean fail(java.lang.CharSequence);
+ method public final java.io.FileDescriptor getData();
+ method public int getId();
+ method public android.print.PrintJobInfo getInfo();
+ method public boolean isQueued();
+ method public boolean isStarted();
+ method public boolean setTag(java.lang.String);
+ method public boolean start();
+ }
+
+ public abstract class PrintService extends android.app.Service {
+ ctor public PrintService();
+ method public final void addDiscoveredPrinters(java.util.List<android.print.PrinterInfo>);
+ method public final android.print.PrinterId generatePrinterId(java.lang.String);
+ method public final java.util.List<android.printservice.PrintJob> getPrintJobs();
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method protected void onConnected();
+ method protected void onDisconnected();
+ method protected abstract void onPrintJobQueued(android.printservice.PrintJob);
+ method protected void onRequestCancelPrintJob(android.printservice.PrintJob);
+ method protected abstract void onStartPrinterDiscovery();
+ method protected abstract void onStopPrinterDiscovery();
+ method public final void removeDiscoveredPrinters(java.util.List<android.print.PrinterId>);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.printservice.PrintService";
+ field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
+ }
+
+}
+
package android.provider {
public final class AlarmClock {
@@ -18473,8 +19101,13 @@
field public static final int MISSED_TYPE = 3; // 0x3
field public static final java.lang.String NEW = "new";
field public static final java.lang.String NUMBER = "number";
+ field public static final java.lang.String NUMBER_PRESENTATION = "presentation";
field public static final java.lang.String OFFSET_PARAM_KEY = "offset";
field public static final int OUTGOING_TYPE = 2; // 0x2
+ field public static final int PRESENTATION_ALLOWED = 1; // 0x1
+ field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
+ field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
+ field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
field public static final java.lang.String TYPE = "type";
}
@@ -19511,6 +20144,28 @@
field public static final android.net.Uri CONTENT_URI;
}
+ public final class DocumentsContract {
+ ctor public DocumentsContract();
+ method public static android.net.Uri buildContentsUri(android.net.Uri);
+ method public static android.net.Uri buildDocumentUri(java.lang.String, java.lang.String);
+ method public static android.net.Uri buildSearchUri(java.lang.String, java.lang.String);
+ method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point);
+ method public static boolean renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
+ field public static final java.lang.String EXTRA_THUMBNAIL_SIZE = "thumbnail_size";
+ field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
+ field public static final int FLAG_SUPPORTS_RENAME = 2; // 0x2
+ field public static final int FLAG_SUPPORTS_THUMBNAIL = 4; // 0x4
+ field public static final java.lang.String MIME_TYPE_DIRECTORY = "vnd.android.cursor.dir/doc";
+ field public static final java.lang.String ROOT_GUID = "0";
+ }
+
+ public static abstract interface DocumentsContract.DocumentColumns implements android.provider.OpenableColumns {
+ field public static final java.lang.String FLAGS = "flags";
+ field public static final java.lang.String GUID = "guid";
+ field public static final java.lang.String LAST_MODIFIED = "last_modified";
+ field public static final java.lang.String MIME_TYPE = "mime_type";
+ }
+
public final deprecated class LiveFolders implements android.provider.BaseColumns {
field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER";
field public static final java.lang.String DESCRIPTION = "description";
@@ -21021,6 +21676,16 @@
method public void setInput(android.renderscript.Allocation);
}
+ public final class ScriptIntrinsicHistogram extends android.renderscript.ScriptIntrinsic {
+ method public static android.renderscript.ScriptIntrinsicHistogram create(android.renderscript.RenderScript, android.renderscript.Element);
+ method public void forEach(android.renderscript.Allocation);
+ method public void forEach_dot(android.renderscript.Allocation);
+ method public android.renderscript.Script.FieldID getFieldID_Input();
+ method public android.renderscript.Script.KernelID getKernelID_seperate();
+ method public void setDotCoefficients(float, float, float, float);
+ method public void setOutput(android.renderscript.Allocation);
+ }
+
public final class ScriptIntrinsicLUT extends android.renderscript.ScriptIntrinsic {
method public static android.renderscript.ScriptIntrinsicLUT create(android.renderscript.RenderScript, android.renderscript.Element);
method public void forEach(android.renderscript.Allocation, android.renderscript.Allocation);
@@ -21455,6 +22120,7 @@
public final class SynthesisRequest {
ctor public SynthesisRequest(java.lang.String, android.os.Bundle);
+ method public int getCallerUid();
method public java.lang.String getCountry();
method public java.lang.String getLanguage();
method public android.os.Bundle getParams();
@@ -22545,6 +23211,7 @@
method public float getFloat(int);
method public int getInt(int);
method public long getLong(int);
+ method public android.net.Uri getNotificationUri();
method public int getPosition();
method public short getShort(int);
method public java.lang.String getString(int);
@@ -23538,7 +24205,9 @@
method public static void clearMetaKeyState(android.text.Editable, int);
method public long clearMetaKeyState(long, int);
method public static final int getMetaState(java.lang.CharSequence);
+ method public static final int getMetaState(java.lang.CharSequence, android.view.KeyEvent);
method public static final int getMetaState(java.lang.CharSequence, int);
+ method public static final int getMetaState(java.lang.CharSequence, int, android.view.KeyEvent);
method public static final int getMetaState(long);
method public static final int getMetaState(long, int);
method public static long handleKeyDown(long, int, android.view.KeyEvent);
@@ -24740,6 +25409,7 @@
method public deprecated int getWidth();
method public boolean isValid();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
+ field public static final int FLAG_PRIVATE = 4; // 0x4
field public static final int FLAG_SECURE = 2; // 0x2
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
}
@@ -25466,6 +26136,7 @@
}
public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
+ method public static java.lang.String actionToString(int);
method public final void addBatch(long, float, float, float, float, int);
method public final void addBatch(long, android.view.MotionEvent.PointerCoords[], int);
method public static int axisFromString(java.lang.String);
@@ -25936,6 +26607,7 @@
method public java.lang.CharSequence getContentDescription();
method public final android.content.Context getContext();
method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo();
+ method public android.view.transition.Scene getCurrentScene();
method public static int getDefaultSize(int, int);
method public android.view.Display getDisplay();
method public final int[] getDrawableState();
@@ -26043,6 +26715,7 @@
method public float getY();
method public boolean hasFocus();
method public boolean hasFocusable();
+ method public boolean hasLayout();
method public boolean hasOnClickListeners();
method public boolean hasOverlappingRendering();
method public boolean hasTransientState();
@@ -26055,6 +26728,7 @@
method public void invalidate();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public boolean isActivated();
+ method public boolean isAttachedToWindow();
method public boolean isClickable();
method public boolean isDirty();
method public boolean isDrawingCacheEnabled();
@@ -26365,6 +27039,7 @@
field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000
field public static final deprecated int STATUS_BAR_HIDDEN = 1; // 0x1
field public static final deprecated int STATUS_BAR_VISIBLE = 0; // 0x0
+ field public static final int SYSTEM_UI_FLAG_ALLOW_OVERLAY = 2048; // 0x800
field public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4
field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
@@ -26940,7 +27615,9 @@
method public void setFlags(int, int);
method public void setFormat(int);
method public void setGravity(int);
+ method public void setIcon(int);
method public void setLayout(int, int);
+ method public void setLogo(int);
method public void setSoftInputMode(int);
method public abstract void setTitle(java.lang.CharSequence);
method public abstract void setTitleColor(int);
@@ -27176,6 +27853,7 @@
method public int describeContents();
method public static java.lang.String eventTypeToString(int);
method public int getAction();
+ method public int getContentChangeType();
method public long getEventTime();
method public int getEventType();
method public int getMovementGranularity();
@@ -27187,11 +27865,14 @@
method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
method public static android.view.accessibility.AccessibilityEvent obtain();
method public void setAction(int);
+ method public void setContentChangeType(int);
method public void setEventTime(long);
method public void setEventType(int);
method public void setMovementGranularity(int);
method public void setPackageName(java.lang.CharSequence);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CONTENT_CHANGE_TYPE_NODE = 1; // 0x1
+ field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 0; // 0x0
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int INVALID_POSITION = -1; // 0xffffffff
field public static final deprecated int MAX_TEXT_LENGTH = 500; // 0x1f4
@@ -27253,15 +27934,21 @@
method public int getActions();
method public void getBoundsInParent(android.graphics.Rect);
method public void getBoundsInScreen(android.graphics.Rect);
+ method public android.os.Bundle getBundle();
method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
method public int getChildCount();
method public java.lang.CharSequence getClassName();
+ method public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo getCollectionInfo();
+ method public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo getCollectionItemInfo();
method public java.lang.CharSequence getContentDescription();
+ method public int getInputType();
method public android.view.accessibility.AccessibilityNodeInfo getLabelFor();
method public android.view.accessibility.AccessibilityNodeInfo getLabeledBy();
method public int getMovementGranularities();
+ method public boolean getOpensPopup();
method public java.lang.CharSequence getPackageName();
method public android.view.accessibility.AccessibilityNodeInfo getParent();
+ method public android.view.accessibility.AccessibilityNodeInfo.RangeInfo getRangeInfo();
method public java.lang.CharSequence getText();
method public int getTextSelectionEnd();
method public int getTextSelectionStart();
@@ -27271,11 +27958,17 @@
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isClickable();
+ method public boolean isContentInvalid();
+ method public boolean isDismissable();
method public boolean isEditable();
method public boolean isEnabled();
+ method public boolean isExpandable();
+ method public boolean isExpanded();
method public boolean isFocusable();
method public boolean isFocused();
+ method public boolean isLiveRegion();
method public boolean isLongClickable();
+ method public boolean isMultiLine();
method public boolean isPassword();
method public boolean isScrollable();
method public boolean isSelected();
@@ -27295,21 +27988,32 @@
method public void setChecked(boolean);
method public void setClassName(java.lang.CharSequence);
method public void setClickable(boolean);
+ method public void setCollectionInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionInfo);
+ method public void setCollectionItemInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo);
method public void setContentDescription(java.lang.CharSequence);
+ method public void setContentInvalid(boolean);
+ method public void setDismissable(boolean);
method public void setEditable(boolean);
method public void setEnabled(boolean);
+ method public void setExpandable(boolean);
+ method public void setExpanded(boolean);
method public void setFocusable(boolean);
method public void setFocused(boolean);
+ method public void setInputType(int);
method public void setLabelFor(android.view.View);
method public void setLabelFor(android.view.View, int);
method public void setLabeledBy(android.view.View);
method public void setLabeledBy(android.view.View, int);
+ method public void setLiveRegion(boolean);
method public void setLongClickable(boolean);
method public void setMovementGranularities(int);
+ method public void setMultiLine(boolean);
+ method public void setOpensPopup(boolean);
method public void setPackageName(java.lang.CharSequence);
method public void setParent(android.view.View);
method public void setParent(android.view.View, int);
method public void setPassword(boolean);
+ method public void setRangeInfo(android.view.accessibility.AccessibilityNodeInfo.RangeInfo);
method public void setScrollable(boolean);
method public void setSelected(boolean);
method public void setSource(android.view.View);
@@ -27329,8 +28033,11 @@
field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
field public static final int ACTION_CLICK = 16; // 0x10
+ field public static final int ACTION_COLLAPSE = 524288; // 0x80000
field public static final int ACTION_COPY = 16384; // 0x4000
field public static final int ACTION_CUT = 65536; // 0x10000
+ field public static final int ACTION_DISMISS = 1048576; // 0x100000
+ field public static final int ACTION_EXPAND = 262144; // 0x40000
field public static final int ACTION_FOCUS = 1; // 0x1
field public static final int ACTION_LONG_CLICK = 32; // 0x20
field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
@@ -27352,6 +28059,33 @@
field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
}
+ public static final class AccessibilityNodeInfo.CollectionInfo {
+ method public int getHorizontalSize();
+ method public int getVerticalSize();
+ method public boolean isHierarchical();
+ method public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean);
+ }
+
+ public static final class AccessibilityNodeInfo.CollectionItemInfo {
+ method public int getHorizontalPosition();
+ method public int getHorizontalSpan();
+ method public int getVerticalPosition();
+ method public int getVerticalSpan();
+ method public boolean isHeading();
+ method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean);
+ }
+
+ public static final class AccessibilityNodeInfo.RangeInfo {
+ method public float getCurrent();
+ method public float getMax();
+ method public float getMin();
+ method public int getType();
+ method public static android.view.accessibility.AccessibilityNodeInfo.RangeInfo obtain(int, float, float, float);
+ field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+ field public static final int RANGE_TYPE_INT = 0; // 0x0
+ field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+ }
+
public abstract class AccessibilityNodeProvider {
ctor public AccessibilityNodeProvider();
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
@@ -27660,10 +28394,10 @@
method public void setAlpha(float);
method public void setTransformationType(int);
method public java.lang.String toShortString();
- field public static int TYPE_ALPHA;
- field public static int TYPE_BOTH;
- field public static int TYPE_IDENTITY;
- field public static int TYPE_MATRIX;
+ field public static final int TYPE_ALPHA = 1; // 0x1
+ field public static final int TYPE_BOTH = 3; // 0x3
+ field public static final int TYPE_IDENTITY = 0; // 0x0
+ field public static final int TYPE_MATRIX = 2; // 0x2
field protected float mAlpha;
field protected android.graphics.Matrix mMatrix;
field protected int mTransformationType;
@@ -28083,6 +28817,147 @@
}
+package android.view.transition {
+
+ public class AutoTransition extends android.view.transition.TransitionGroup {
+ ctor public AutoTransition();
+ }
+
+ public class Crossfade extends android.view.transition.Transition {
+ ctor public Crossfade();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method public int getFadeBehavior();
+ method public int getResizeBehavior();
+ method public void setFadeBehavior(int);
+ method public void setResizeBehavior(int);
+ field public static final int FADE_BEHAVIOR_CROSSFADE = 0; // 0x0
+ field public static final int FADE_BEHAVIOR_REVEAL = 1; // 0x1
+ field public static final int RESIZE_BEHAVIOR_NONE = 0; // 0x0
+ field public static final int RESIZE_BEHAVIOR_SCALE = 1; // 0x1
+ }
+
+ public class Fade extends android.view.transition.Visibility {
+ ctor public Fade();
+ ctor public Fade(int);
+ field public static final int IN = 1; // 0x1
+ field public static final int OUT = 2; // 0x2
+ }
+
+ public class Move extends android.view.transition.Transition {
+ ctor public Move();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method public void setReparent(boolean);
+ method public void setResizeClip(boolean);
+ }
+
+ public class Recolor extends android.view.transition.Transition {
+ ctor public Recolor();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ }
+
+ public class Rotate extends android.view.transition.Transition {
+ ctor public Rotate();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ }
+
+ public final class Scene {
+ ctor public Scene(android.view.ViewGroup);
+ ctor public Scene(android.view.ViewGroup, int, android.content.Context);
+ ctor public Scene(android.view.ViewGroup, android.view.ViewGroup);
+ method public void enter();
+ method public void exit();
+ method public android.view.ViewGroup getSceneRoot();
+ method public void setEnterAction(java.lang.Runnable);
+ method public void setExitAction(java.lang.Runnable);
+ }
+
+ public class Slide extends android.view.transition.Visibility {
+ ctor public Slide();
+ }
+
+ public class TextChange extends android.view.transition.Transition {
+ ctor public TextChange();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ }
+
+ public abstract class Transition implements java.lang.Cloneable {
+ ctor public Transition();
+ method public void addListener(android.view.transition.Transition.TransitionListener);
+ method protected void cancelTransition();
+ method protected abstract void captureValues(android.view.transition.TransitionValues, boolean);
+ method public android.view.transition.Transition clone();
+ method public long getDuration();
+ method public android.animation.TimeInterpolator getInterpolator();
+ method public java.util.ArrayList<android.view.transition.Transition.TransitionListener> getListeners();
+ method public long getStartDelay();
+ method public int[] getTargetIds();
+ method public android.view.View[] getTargets();
+ method protected android.view.transition.TransitionValues getTransitionValues(android.view.View, boolean);
+ method protected void onTransitionCancel();
+ method protected void onTransitionEnd();
+ method protected void onTransitionStart();
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method public void removeListener(android.view.transition.Transition.TransitionListener);
+ method public android.view.transition.Transition setDuration(long);
+ method public void setInterpolator(android.animation.TimeInterpolator);
+ method public void setStartDelay(long);
+ method public android.view.transition.Transition setTargetIds(int...);
+ method public android.view.transition.Transition setTargets(android.view.View...);
+ }
+
+ public static abstract interface Transition.TransitionListener {
+ method public abstract void onTransitionCancel(android.view.transition.Transition);
+ method public abstract void onTransitionEnd(android.view.transition.Transition);
+ method public abstract void onTransitionStart(android.view.transition.Transition);
+ }
+
+ public class TransitionGroup extends android.view.transition.Transition {
+ ctor public TransitionGroup();
+ ctor public TransitionGroup(int);
+ method public void addTransitions(android.view.transition.Transition...);
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method public void removeTransition(android.view.transition.Transition);
+ method public void setOrdering(int);
+ field public static final int SEQUENTIALLY = 1; // 0x1
+ field public static final int TOGETHER = 0; // 0x0
+ }
+
+ public class TransitionInflater {
+ method public static android.view.transition.TransitionInflater from(android.content.Context);
+ method public android.view.transition.Scene inflateScene(int, android.view.ViewGroup);
+ method public android.view.transition.Transition inflateTransition(int);
+ method public android.view.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
+ }
+
+ public class TransitionManager {
+ ctor public TransitionManager();
+ method public static void beginDelayedTransition(android.view.ViewGroup, android.view.transition.Transition);
+ method public android.view.transition.Transition getDefaultTransition();
+ method public static void go(android.view.transition.Scene);
+ method public static void go(android.view.transition.Scene, android.view.transition.Transition);
+ method public static void go(android.view.ViewGroup, java.lang.Runnable);
+ method public static void go(android.view.ViewGroup, java.lang.Runnable, android.view.transition.Transition);
+ method public void setDefaultTransition(android.view.transition.Transition);
+ method public void setTransition(android.view.transition.Scene, android.view.transition.Transition);
+ method public void setTransition(android.view.transition.Scene, android.view.transition.Scene, android.view.transition.Transition);
+ method public void transitionTo(android.view.transition.Scene);
+ }
+
+ public class TransitionValues {
+ ctor public TransitionValues();
+ field public final java.util.Map values;
+ field public android.view.View view;
+ }
+
+ public abstract class Visibility extends android.view.transition.Transition {
+ ctor public Visibility();
+ method protected android.animation.Animator appear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator disappear(android.view.ViewGroup, android.view.transition.TransitionValues, int, android.view.transition.TransitionValues, int);
+ }
+
+}
+
package android.webkit {
public class ConsoleMessage {
@@ -29422,6 +30297,7 @@
method public int getAlignmentMode();
method public int getColumnCount();
method public int getOrientation();
+ method public android.util.Printer getPrinter();
method public int getRowCount();
method public boolean getUseDefaultMargins();
method public boolean isColumnOrderPreserved();
@@ -29431,6 +30307,7 @@
method public void setColumnCount(int);
method public void setColumnOrderPreserved(boolean);
method public void setOrientation(int);
+ method public void setPrinter(android.util.Printer);
method public void setRowCount(int);
method public void setRowOrderPreserved(boolean);
method public void setUseDefaultMargins(boolean);
@@ -29728,6 +30605,8 @@
method public void addFooterView(android.view.View);
method public void addHeaderView(android.view.View, java.lang.Object, boolean);
method public void addHeaderView(android.view.View);
+ method public boolean areFooterDividersEnabled();
+ method public boolean areHeaderDividersEnabled();
method protected android.view.View findViewTraversal(int);
method protected android.view.View findViewWithTagTraversal(java.lang.Object);
method public android.widget.ListAdapter getAdapter();
@@ -30703,6 +31582,7 @@
method public int getTotalPaddingTop();
method public final android.text.method.TransformationMethod getTransformationMethod();
method public android.graphics.Typeface getTypeface();
+ method public final android.content.UndoManager getUndoManager();
method public android.text.style.URLSpan[] getUrls();
method public boolean hasSelection();
method public boolean isCursorVisible();
@@ -30801,6 +31681,7 @@
method public final void setTransformationMethod(android.text.method.TransformationMethod);
method public void setTypeface(android.graphics.Typeface, int);
method public void setTypeface(android.graphics.Typeface);
+ method public final void setUndoManager(android.content.UndoManager, java.lang.String);
method public void setWidth(int);
}
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 61fe340..87ea4fd 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -19,6 +19,7 @@
package com.android.commands.am;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackBoxInfo;
import android.app.ActivityManagerNative;
import android.app.IActivityController;
import android.app.IActivityManager;
@@ -75,6 +76,7 @@
(new Am()).run(args);
}
+ @Override
public void onShowUsage(PrintStream out) {
out.println(
"usage: am [subcommand] [options]\n" +
@@ -101,6 +103,11 @@
" am to-intent-uri [INTENT]\n" +
" am switch-user <USER_ID>\n" +
" am stop-user <USER_ID>\n" +
+ " am stack create <TASK_ID> <RELATIVE_STACK_BOX_ID> <POSITION> <WEIGHT>\n" +
+ " am stack movetask <STACK_ID> <TASK_ID> [true|false]\n" +
+ " am stack resize <STACK_ID> <WEIGHT>\n" +
+ " am stack boxes\n" +
+ " am stack box <STACK_BOX_ID>\n" +
"\n" +
"am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" +
@@ -146,7 +153,7 @@
" test runners.\n" +
" --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
" current user if not specified.\n" +
- " --no-window-animation: turn off window animations will running.\n" +
+ " --no-window-animation: turn off window animations while running.\n" +
"\n" +
"am profile: start and stop profiler on a process. The given <PROCESS> argument\n" +
" may be either a process name or pid. Options are:\n" +
@@ -186,6 +193,25 @@
"am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
" code until a later explicit switch to it.\n" +
"\n" +
+ "am stack create: create a new stack relative to an existing one.\n" +
+ " <TASK_ID>: the task to populate the new stack with. Must exist.\n" +
+ " <RELATIVE_STACK_BOX_ID>: existing stack box's id.\n" +
+ " <POSITION>: 0: before <RELATIVE_STACK_BOX_ID>, per RTL/LTR configuration,\n" +
+ " 1: after <RELATIVE_STACK_BOX_ID>, per RTL/LTR configuration,\n" +
+ " 2: to left of <RELATIVE_STACK_BOX_ID>,\n" +
+ " 3: to right of <RELATIVE_STACK_BOX_ID>," +
+ " 4: above <RELATIVE_STACK_BOX_ID>, 5: below <RELATIVE_STACK_BOX_ID>\n" +
+ " <WEIGHT>: float between 0.2 and 0.8 inclusive.\n" +
+ "\n" +
+ "am stack movetask: move <TASK_ID> from its current stack to the top (true) or" +
+ " bottom (false) of <STACK_ID>.\n" +
+ "\n" +
+ "am stack resize: change <STACK_ID> relative size to new <WEIGHT>.\n" +
+ "\n" +
+ "am stack boxes: list the hierarchy of stack boxes and their contents.\n" +
+ "\n" +
+ "am stack box: list the hierarchy of stack boxes rooted at <STACK_BOX_ID>.\n" +
+ "\n" +
"<INTENT> specifications include these flags and arguments:\n" +
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
" [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
@@ -218,6 +244,7 @@
);
}
+ @Override
public void onRun() throws Exception {
mAm = ActivityManagerNative.getDefault();
@@ -266,6 +293,8 @@
runSwitchUser();
} else if (op.equals("stop-user")) {
runStopUser();
+ } else if (op.equals("stack")) {
+ runStack();
} else {
showError("Error: unknown command '" + op + "'");
}
@@ -1036,7 +1065,7 @@
}
@Override
- public boolean activityResuming(String pkg) throws RemoteException {
+ public boolean activityResuming(String pkg) {
synchronized (this) {
System.out.println("** Activity resuming: " + pkg);
}
@@ -1044,7 +1073,7 @@
}
@Override
- public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
+ public boolean activityStarting(Intent intent, String pkg) {
synchronized (this) {
System.out.println("** Activity starting: " + pkg);
}
@@ -1053,7 +1082,7 @@
@Override
public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
- long timeMillis, String stackTrace) throws RemoteException {
+ long timeMillis, String stackTrace) {
synchronized (this) {
System.out.println("** ERROR: PROCESS CRASHED");
System.out.println("processName: " + processName);
@@ -1070,8 +1099,7 @@
}
@Override
- public int appEarlyNotResponding(String processName, int pid, String annotation)
- throws RemoteException {
+ public int appEarlyNotResponding(String processName, int pid, String annotation) {
synchronized (this) {
System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING");
System.out.println("processName: " + processName);
@@ -1084,8 +1112,7 @@
}
@Override
- public int appNotResponding(String processName, int pid, String processStats)
- throws RemoteException {
+ public int appNotResponding(String processName, int pid, String processStats) {
synchronized (this) {
System.out.println("** ERROR: PROCESS NOT RESPONDING");
System.out.println("processName: " + processName);
@@ -1101,8 +1128,7 @@
}
@Override
- public int systemNotResponding(String message)
- throws RemoteException {
+ public int systemNotResponding(String message) {
synchronized (this) {
System.out.println("** ERROR: PROCESS NOT RESPONDING");
System.out.println("message: " + message);
@@ -1361,7 +1387,7 @@
@Override
public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
+ boolean ordered, boolean sticky, int sendingUser) {
String line = "Broadcast completed: result=" + resultCode;
if (data != null) line = line + ", data=\"" + data + "\"";
if (extras != null) line = line + ", extras: " + extras;
@@ -1394,6 +1420,7 @@
mRawMode = rawMode;
}
+ @Override
public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
synchronized (this) {
// pretty printer mode?
@@ -1416,6 +1443,7 @@
}
}
+ @Override
public void instrumentationFinished(ComponentName name, int resultCode,
Bundle results) {
synchronized (this) {
@@ -1456,4 +1484,93 @@
return true;
}
}
+
+ private void runStack() throws Exception {
+ String op = nextArgRequired();
+ if (op.equals("create")) {
+ runStackCreate();
+ } else if (op.equals("movetask")) {
+ runStackMoveTask();
+ } else if (op.equals("resize")) {
+ runStackBoxResize();
+ } else if (op.equals("boxes")) {
+ runStackBoxes();
+ } else if (op.equals("box")) {
+ runStackBoxInfo();
+ } else {
+ showError("Error: unknown command '" + op + "'");
+ return;
+ }
+ }
+
+ private void runStackCreate() throws Exception {
+ String taskIdStr = nextArgRequired();
+ int taskId = Integer.valueOf(taskIdStr);
+ String relativeToStr = nextArgRequired();
+ int relativeTo = Integer.valueOf(relativeToStr);
+ String positionStr = nextArgRequired();
+ int position = Integer.valueOf(positionStr);
+ String weightStr = nextArgRequired();
+ float weight = Float.valueOf(weightStr);
+
+ try {
+ int stackId = mAm.createStack(taskId, relativeTo, position, weight);
+ System.out.println("createStack returned new stackId=" + stackId + "\n\n");
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void runStackMoveTask() throws Exception {
+ String taskIdStr = nextArgRequired();
+ int taskId = Integer.valueOf(taskIdStr);
+ String stackIdStr = nextArgRequired();
+ int stackId = Integer.valueOf(stackIdStr);
+ String toTopStr = nextArgRequired();
+ final boolean toTop;
+ if ("true".equals(toTopStr)) {
+ toTop = true;
+ } else if ("false".equals(toTopStr)) {
+ toTop = false;
+ } else {
+ System.err.println("Error: bad toTop arg: " + toTopStr);
+ return;
+ }
+
+ try {
+ mAm.moveTaskToStack(taskId, stackId, toTop);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void runStackBoxResize() throws Exception {
+ String stackBoxIdStr = nextArgRequired();
+ int stackBoxId = Integer.valueOf(stackBoxIdStr);
+ String weightStr = nextArgRequired();
+ float weight = Float.valueOf(weightStr);
+
+ try {
+ mAm.resizeStackBox(stackBoxId, weight);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void runStackBoxes() throws Exception {
+ try {
+ List<StackBoxInfo> stackBoxes = mAm.getStackBoxes();
+ for (StackBoxInfo info : stackBoxes) {
+ System.out.println(info);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void runStackBoxInfo() throws Exception {
+ try {
+ String stackBoxIdStr = nextArgRequired();
+ int stackBoxId = Integer.valueOf(stackBoxIdStr);
+ StackBoxInfo stackBoxInfo = mAm.getStackBoxInfo(stackBoxId);
+ System.out.println(stackBoxInfo);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/cmds/backup/Android.mk b/cmds/backup/Android.mk
index 73af0bc..42e5133 100644
--- a/cmds/backup/Android.mk
+++ b/cmds/backup/Android.mk
@@ -10,6 +10,6 @@
LOCAL_MODULE:= btool
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 80ac539..bb26443 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -57,14 +57,18 @@
}
} else if (command.equals("keyevent")) {
if (args.length >= 2) {
- for (int i=1; i < args.length; i++) {
- int keyCode = KeyEvent.keyCodeFromString(args[i]);
- if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
- keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]);
+ final boolean longpress = "--longpress".equals(args[1]);
+ final int start = longpress ? 2 : 1;
+ if (args.length > start) {
+ for (int i = start; i < args.length; i++) {
+ int keyCode = KeyEvent.keyCodeFromString(args[i]);
+ if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
+ keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]);
+ }
+ sendKeyEvent(keyCode, longpress);
}
- sendKeyEvent(keyCode);
+ return;
}
- return;
}
} else if (command.equals("tap")) {
if (args.length == 3) {
@@ -168,10 +172,15 @@
}
}
- private void sendKeyEvent(int keyCode) {
+ private void sendKeyEvent(int keyCode, boolean longpress) {
long now = SystemClock.uptimeMillis();
injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+ if (longpress) {
+ injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS,
+ InputDevice.SOURCE_KEYBOARD));
+ }
injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
}
@@ -251,7 +260,7 @@
private void showUsage() {
System.err.println("usage: input ...");
System.err.println(" input text <string>");
- System.err.println(" input keyevent <key code number or name> ...");
+ System.err.println(" input keyevent [--longpress] <key code number or name> ...");
System.err.println(" input [touchscreen|touchpad|touchnavigation] tap <x> <y>");
System.err.println(" input [touchscreen|touchpad|touchnavigation] swipe "
+ "<x1> <y1> <x2> <y2> [duration(ms)]");
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 224945a..d1ded10 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -16,8 +16,7 @@
package com.android.commands.pm;
-import com.android.internal.content.PackageHelper;
-
+import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
@@ -46,6 +45,7 @@
import android.os.UserManager;
import java.io.File;
+import java.io.FileDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.InvalidAlgorithmParameterException;
@@ -59,6 +59,8 @@
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import com.android.internal.content.PackageHelper;
+
public final class Pm {
IPackageManager mPm;
IUserManager mUm;
@@ -105,6 +107,11 @@
return;
}
+ if ("dump".equals(op)) {
+ runDump();
+ return;
+ }
+
if ("install".equals(op)) {
runInstall();
return;
@@ -140,6 +147,16 @@
return;
}
+ if ("block".equals(op)) {
+ runSetBlockedSetting(true);
+ return;
+ }
+
+ if ("unblock".equals(op)) {
+ runSetBlockedSetting(false);
+ return;
+ }
+
if ("grant".equals(op)) {
runGrantRevokePermission(true);
return;
@@ -672,6 +689,15 @@
displayPackageFilePath(pkg);
}
+ private void runDump() {
+ String pkg = nextArg();
+ if (pkg == null) {
+ System.err.println("Error: no package specified");
+ return;
+ }
+ ActivityManager.dumpPackageStateStatic(FileDescriptor.out, pkg);
+ }
+
class PackageInstallObserver extends IPackageInstallObserver.Stub {
boolean finished;
int result;
@@ -1240,6 +1266,36 @@
}
}
+ private void runSetBlockedSetting(boolean state) {
+ int userId = 0;
+ String option = nextOption();
+ if (option != null && option.equals("--user")) {
+ String optionData = nextOptionData();
+ if (optionData == null || !isNumber(optionData)) {
+ System.err.println("Error: no USER_ID specified");
+ showUsage();
+ return;
+ } else {
+ userId = Integer.parseInt(optionData);
+ }
+ }
+
+ String pkg = nextArg();
+ if (pkg == null) {
+ System.err.println("Error: no package or component specified");
+ showUsage();
+ return;
+ }
+ try {
+ mPm.setApplicationBlockedSettingAsUser(pkg, state, userId);
+ System.err.println("Package " + pkg + " new blocked state: "
+ + mPm.getApplicationBlockedSettingAsUser(pkg, userId));
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
private void runGrantRevokePermission(boolean grant) {
String pkg = nextArg();
if (pkg == null) {
@@ -1456,6 +1512,7 @@
System.err.println(" pm list libraries");
System.err.println(" pm list users");
System.err.println(" pm path PACKAGE");
+ System.err.println(" pm dump PACKAGE");
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");
@@ -1465,6 +1522,8 @@
System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT");
+ System.err.println(" pm block [--user USER_ID] PACKAGE_OR_COMPONENT");
+ System.err.println(" pm unblock [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm grant PACKAGE PERMISSION");
System.err.println(" pm revoke PACKAGE PERMISSION");
System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]");
@@ -1506,6 +1565,8 @@
System.err.println("");
System.err.println("pm path: print the path to the .apk of the given PACKAGE.");
System.err.println("");
+ System.err.println("pm dump: print system state associated w ith the given PACKAGE.");
+ System.err.println("");
System.err.println("pm install: installs a package to the system. Options:");
System.err.println(" -l: install the package with FORWARD_LOCK.");
System.err.println(" -r: reinstall an exisiting app, keeping its data.");
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index 745c34a..699996a 100644
--- a/cmds/system_server/library/system_init.cpp
+++ b/cmds/system_server/library/system_init.cpp
@@ -11,7 +11,7 @@
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
-#include <utils/TextOutput.h>
+#include <binder/TextOutput.h>
#include <utils/Log.h>
#include <SurfaceFlinger.h>
@@ -60,12 +60,6 @@
sm->asBinder()->linkToDeath(grim, grim.get(), 0);
char propBuf[PROPERTY_VALUE_MAX];
- property_get("system_init.startsurfaceflinger", propBuf, "1");
- if (strcmp(propBuf, "1") == 0) {
- // Start the SurfaceFlinger
- SurfaceFlinger::instantiate();
- }
-
property_get("system_init.startsensorservice", propBuf, "1");
if (strcmp(propBuf, "1") == 0) {
// Start the sensor service
@@ -94,12 +88,12 @@
if (methodId == NULL) {
return UNKNOWN_ERROR;
}
- env->CallStaticVoidMethod(clazz, methodId);
- ALOGI("System server: entering thread pool.\n");
+ ALOGI("System server: entering thread pool.");
ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
- ALOGI("System server: exiting thread pool.\n");
+
+ // This is the main thread of the system server, and will never exit.
+ env->CallStaticVoidMethod(clazz, methodId);
return NO_ERROR;
}
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 58eb66f..82c2159 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -29,6 +29,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
@@ -127,6 +128,7 @@
private int mCallingUid;
private String mCallingPackage;
private boolean mDisallowAddAccounts;
+ private boolean mDontShowPicker;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -189,11 +191,23 @@
mSetOfRelevantAccountTypes = getReleventAccountTypes(intent);
mAlwaysPromptForAccount = intent.getBooleanExtra(EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT, false);
mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE);
+
+ // Need to do this once here to request the window feature. Can't do it in onResume
+ mAccounts = getAcceptableAccountChoices(AccountManager.get(this));
+ if (mAccounts.isEmpty()
+ && mDisallowAddAccounts) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.app_not_authorized);
+ mDontShowPicker = true;
+ }
}
@Override
protected void onResume() {
super.onResume();
+
+ if (mDontShowPicker) return;
+
final AccountManager accountManager = AccountManager.get(this);
mAccounts = getAcceptableAccountChoices(accountManager);
@@ -206,11 +220,6 @@
// If there are no relevant accounts and only one relevant account type go directly to
// add account. Otherwise let the user choose.
if (mAccounts.isEmpty()) {
- if (mDisallowAddAccounts) {
- setContentView(R.layout.app_not_authorized);
- setTitle(R.string.error_message_title);
- return;
- }
if (mSetOfRelevantAccountTypes.size() == 1) {
runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next());
} else {
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 173ee73..9c88ccf 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -123,9 +123,37 @@
* in a call to the function <code>setFoo()</code> on the target object. If either
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
* also be derived and called.
+ *
+ * <p>If this animator was created with a {@link Property} object instead of the
+ * string name of a property, then this method will return the {@link
+ * Property#getName() name} of that Property object instead. If this animator was
+ * created with one or more {@link PropertyValuesHolder} objects, then this method
+ * will return the {@link PropertyValuesHolder#getPropertyName() name} of that
+ * object (if there was just one) or a comma-separated list of all of the
+ * names (if there are more than one).</p>
*/
public String getPropertyName() {
- return mPropertyName;
+ String propertyName = null;
+ if (mPropertyName != null) {
+ propertyName = mPropertyName;
+ } else if (mProperty != null) {
+ propertyName = mProperty.getName();
+ } else if (mValues != null && mValues.length > 0) {
+ for (int i = 0; i < mValues.length; ++i) {
+ if (i == 0) {
+ propertyName = "";
+ } else {
+ propertyName += ",";
+ }
+ propertyName += mValues[i].getPropertyName();
+ }
+ }
+ return propertyName;
+ }
+
+ @Override
+ String getNameForTrace() {
+ return "animator:" + getPropertyName();
}
/**
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index cb44264..e370e4a 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -869,7 +869,7 @@
*
* <p>If this ValueAnimator has only one set of values being animated between, this evaluator
* will be used for that set. If there are several sets of values being animated, which is
- * the case if PropertyValuesHOlder objects were set on the ValueAnimator, then the evaluator
+ * the case if PropertyValuesHolder objects were set on the ValueAnimator, then the evaluator
* is assigned just to the first PropertyValuesHolder object.</p>
*
* @param value the evaluator to be used this animation
@@ -1024,8 +1024,10 @@
mStarted = false;
mStartListenersCalled = false;
mPlayingBackwards = false;
- Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "animator",
- System.identityHashCode(this));
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
+ System.identityHashCode(this));
+ }
}
/**
@@ -1033,8 +1035,10 @@
* called on the UI thread.
*/
private void startAnimation(AnimationHandler handler) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "animator",
- System.identityHashCode(this));
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
+ System.identityHashCode(this));
+ }
initAnimation();
handler.mAnimations.add(this);
if (mStartDelay > 0 && mListeners != null) {
@@ -1045,6 +1049,14 @@
}
/**
+ * Returns the name of this animator for debugging purposes.
+ */
+ String getNameForTrace() {
+ return "animator";
+ }
+
+
+ /**
* Internal function called to process an animation frame on an animation that is currently
* sleeping through its <code>startDelay</code> phase. The return value indicates whether it
* should be woken up and put on the active animations queue.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6b5df7f..fa746ba 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -686,6 +686,7 @@
boolean mFinished;
boolean mStartedActivity;
private boolean mDestroyed;
+ private boolean mDoReportFullyDrawn = true;
/** true if the activity is going through a transient pause */
/*package*/ boolean mTemporaryPause = false;
/** true if the activity is being destroyed in order to recreate it with a new configuration */
@@ -744,6 +745,7 @@
// protected by synchronized (this)
int mResultCode = RESULT_CANCELED;
Intent mResultData = null;
+ private TranslucentConversionListener mTranslucentCallback;
private boolean mTitleReady = false;
@@ -1381,6 +1383,7 @@
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
getApplication().dispatchActivityStopped(this);
+ mTranslucentCallback = null;
mCalled = true;
}
@@ -1449,6 +1452,27 @@
}
/**
+ * Report to the system that your app is now fully drawn. This is only used
+ * to help instrument app launch times, so that the app can report when it is
+ * fully in a usable state; without this, all the system can determine is when
+ * its window is first drawn and displayed. To participate in app launch time
+ * measurement, you should always call this method after first launch (when
+ * {@link #onCreate(android.os.Bundle)} is called) at the point where you have
+ * entirely drawn your UI and populated with all of the significant data. You
+ * can safely call this method any time after first launch as well, in which case
+ * it will simply be ignored.
+ */
+ public void reportFullyDrawn() {
+ if (mDoReportFullyDrawn) {
+ mDoReportFullyDrawn = false;
+ try {
+ ActivityManagerNative.getDefault().reportActivityFullyDrawn(mToken);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
* Called by the system when the device configuration changes while your
* activity is running. Note that this will <em>only</em> be called if
* you have selected configurations you would like to handle with the
@@ -1877,9 +1901,12 @@
if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
-
+
mActionBar = new ActionBarImpl(this);
mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
+
+ mWindow.setDefaultIcon(mActivityInfo.getIconResource());
+ mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
}
/**
@@ -4859,6 +4886,64 @@
}
/**
+ * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} to a
+ * fullscreen opaque Activity.
+ * <p>
+ * Call this whenever the background of a translucent Activity has changed to become opaque.
+ * Doing so will allow the {@link android.view.Surface} of the Activity behind to be released.
+ * <p>
+ * This call has no effect on non-translucent activities or on activities with the
+ * {@link android.R.attr#windowIsFloating} attribute.
+ *
+ * @see #convertToTranslucent(TranslucentConversionListener)
+ * @see TranslucentConversionListener
+ */
+ public void convertFromTranslucent() {
+ try {
+ mTranslucentCallback = null;
+ ActivityManagerNative.getDefault().convertFromTranslucent(mToken);
+ } catch (RemoteException e) {
+ // pass
+ }
+ }
+
+ /**
+ * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} back from
+ * opaque to translucent following a call to {@link #convertFromTranslucent()}.
+ * <p>
+ * Calling this allows the Activity behind this one to be seen again. Once all such Activities
+ * have been redrawn {@link TranslucentConversionListener#onTranslucentConversionComplete} will
+ * be called indicating that it is safe to make this activity translucent again. Until
+ * {@link TranslucentConversionListener#onTranslucentConversionComplete} is called the image
+ * behind the frontmost Activity will be indeterminate.
+ * <p>
+ * This call has no effect on non-translucent activities or on activities with the
+ * {@link android.R.attr#windowIsFloating} attribute.
+ *
+ * @param callback the method to call when all visible Activities behind this one have been
+ * drawn and it is safe to make this Activity translucent again.
+ *
+ * @see #convertFromTranslucent()
+ * @see TranslucentConversionListener
+ */
+ public void convertToTranslucent(TranslucentConversionListener callback) {
+ try {
+ mTranslucentCallback = callback;
+ ActivityManagerNative.getDefault().convertToTranslucent(mToken);
+ } catch (RemoteException e) {
+ // pass
+ }
+ }
+
+ /** @hide */
+ void onTranslucentConversionComplete(boolean drawComplete) {
+ if (mTranslucentCallback != null) {
+ mTranslucentCallback.onTranslucentConversionComplete(drawComplete);
+ mTranslucentCallback = null;
+ }
+ }
+
+ /**
* Adjust the current immersive mode setting.
*
* Note that changing this value will have no effect on the activity's
@@ -4903,6 +4988,7 @@
* @return The new action mode, or <code>null</code> if the activity does not want to
* provide special handling for this action mode. (It will be handled by the system.)
*/
+ @Override
public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
initActionBar();
if (mActionBar != null) {
@@ -4917,6 +5003,7 @@
*
* @param mode The new action mode.
*/
+ @Override
public void onActionModeStarted(ActionMode mode) {
}
@@ -4926,6 +5013,7 @@
*
* @param mode The action mode that just finished.
*/
+ @Override
public void onActionModeFinished(ActionMode mode) {
}
@@ -5230,6 +5318,7 @@
}
final void performPause() {
+ mDoReportFullyDrawn = false;
mFragments.dispatchPause();
mCalled = false;
onPause();
@@ -5249,6 +5338,7 @@
}
final void performStop() {
+ mDoReportFullyDrawn = false;
if (mLoadersStarted) {
mLoadersStarted = false;
if (mLoaderManager != null) {
@@ -5327,4 +5417,26 @@
}
}
}
+
+ /**
+ * Interface for informing a translucent {@link Activity} once all visible activities below it
+ * have completed drawing. This is necessary only after an {@link Activity} has been made
+ * opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn
+ * translucent again following a call to {@link
+ * Activity#convertToTranslucent(TranslucentConversionListener)}.
+ */
+ public interface TranslucentConversionListener {
+ /**
+ * Callback made following {@link Activity#convertToTranslucent} once all visible Activities
+ * below the top one have been redrawn. Following this callback it is safe to make the top
+ * Activity translucent because the underlying Activity has been drawn.
+ *
+ * @param drawComplete True if the background Activity has drawn itself. False if a timeout
+ * occurred waiting for the Activity to complete drawing.
+ *
+ * @see Activity#convertFromTranslucent()
+ * @see Activity#convertToTranslucent(TranslucentConversionListener)
+ */
+ public void onTranslucentConversionComplete(boolean drawComplete);
+ }
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index bb9e19f..5fa874b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,8 +16,13 @@
package android.app;
+import android.R;
+import android.os.BatteryStats;
+import android.os.IBinder;
import com.android.internal.app.IUsageStats;
import com.android.internal.os.PkgUsageStats;
+import com.android.internal.os.TransferPipe;
+import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.MemInfoReader;
import android.content.ComponentName;
@@ -31,9 +36,8 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
-import android.hardware.display.DisplayManager;
+import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
@@ -50,6 +54,9 @@
import android.util.Slog;
import android.view.Display;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -371,7 +378,23 @@
String vmHeapSize = SystemProperties.get("dalvik.vm.heapsize", "16m");
return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length()-1));
}
-
+
+ /**
+ * Returns true if this is a low-RAM device. Exactly whether a device is low-RAM
+ * is ultimately up to the device configuration, but currently it generally means
+ * something in the class of a 512MB device with about a 800x480 or less screen.
+ * This is mostly intended to be used by apps to determine whether they should turn
+ * off certain features that require more RAM.
+ */
+ public boolean isLowRamDevice() {
+ return isLowRamDeviceStatic();
+ }
+
+ /** @hide */
+ public static boolean isLowRamDeviceStatic() {
+ return Resources.getSystem().getBoolean(com.android.internal.R.bool.config_lowRamDevice);
+ }
+
/**
* Used by persistent processes to determine if they are running on a
* higher-end device so should be okay using hardware drawing acceleration
@@ -379,43 +402,8 @@
* @hide
*/
static public boolean isHighEndGfx() {
- MemInfoReader reader = new MemInfoReader();
- reader.readMemInfo();
- if (reader.getTotalSize() >= (512*1024*1024)) {
- // If the device has at least 512MB RAM available to the kernel,
- // we can afford the overhead of graphics acceleration.
- return true;
- }
-
- Display display = DisplayManagerGlobal.getInstance().getRealDisplay(
- Display.DEFAULT_DISPLAY);
- Point p = new Point();
- display.getRealSize(p);
- int pixels = p.x * p.y;
- if (pixels >= (1024*600)) {
- // If this is a sufficiently large screen, then there are enough
- // pixels on it that we'd really like to use hw drawing.
- return true;
- }
- return false;
- }
-
- /**
- * Use to decide whether the running device can be considered a "large
- * RAM" device. Exactly what memory limit large RAM is will vary, but
- * it essentially means there is plenty of RAM to have lots of background
- * processes running under decent loads.
- * @hide
- */
- static public boolean isLargeRAM() {
- MemInfoReader reader = new MemInfoReader();
- reader.readMemInfo();
- if (reader.getTotalSize() >= (640*1024*1024)) {
- // Currently 640MB RAM available to the kernel is the point at
- // which we have plenty of RAM to spare.
- return true;
- }
- return false;
+ return !isLowRamDeviceStatic() &&
+ !Resources.getSystem().getBoolean(com.android.internal.R.bool.config_avoidGfxAccel);
}
/**
@@ -453,14 +441,21 @@
* Description of the task's last state.
*/
public CharSequence description;
-
+
+ /**
+ * The id of the ActivityStack this Task was on most recently.
+ */
+ public int stackId;
+
public RecentTaskInfo() {
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeInt(persistentId);
@@ -473,6 +468,7 @@
ComponentName.writeToParcel(origActivity, dest);
TextUtils.writeToParcel(description, dest,
Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ dest.writeInt(stackId);
}
public void readFromParcel(Parcel source) {
@@ -485,8 +481,9 @@
}
origActivity = ComponentName.readFromParcel(source);
description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ stackId = source.readInt();
}
-
+
public static final Creator<RecentTaskInfo> CREATOR
= new Creator<RecentTaskInfo>() {
public RecentTaskInfo createFromParcel(Parcel source) {
@@ -1229,7 +1226,169 @@
} catch (RemoteException e) {
}
}
-
+
+ /**
+ * Information you can retrieve about the WindowManager StackBox hierarchy.
+ * @hide
+ */
+ public static class StackBoxInfo implements Parcelable {
+ public int stackBoxId;
+ public float weight;
+ public boolean vertical;
+ public Rect bounds;
+ public StackBoxInfo[] children;
+ public int stackId;
+ public StackInfo stack;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(stackBoxId);
+ dest.writeFloat(weight);
+ dest.writeInt(vertical ? 1 : 0);
+ bounds.writeToParcel(dest, flags);
+ dest.writeInt(stackId);
+ if (children != null) {
+ children[0].writeToParcel(dest, flags);
+ children[1].writeToParcel(dest, flags);
+ } else {
+ stack.writeToParcel(dest, flags);
+ }
+ }
+
+ public void readFromParcel(Parcel source) {
+ stackBoxId = source.readInt();
+ weight = source.readFloat();
+ vertical = source.readInt() == 1;
+ bounds = Rect.CREATOR.createFromParcel(source);
+ stackId = source.readInt();
+ if (stackId == -1) {
+ children = new StackBoxInfo[2];
+ children[0] = StackBoxInfo.CREATOR.createFromParcel(source);
+ children[1] = StackBoxInfo.CREATOR.createFromParcel(source);
+ } else {
+ stack = StackInfo.CREATOR.createFromParcel(source);
+ }
+ }
+
+ public static final Creator<StackBoxInfo> CREATOR =
+ new Creator<ActivityManager.StackBoxInfo>() {
+
+ @Override
+ public StackBoxInfo createFromParcel(Parcel source) {
+ return new StackBoxInfo(source);
+ }
+
+ @Override
+ public StackBoxInfo[] newArray(int size) {
+ return new StackBoxInfo[size];
+ }
+ };
+
+ public StackBoxInfo() {
+ }
+
+ public StackBoxInfo(Parcel source) {
+ readFromParcel(source);
+ }
+
+ public String toString(String prefix) {
+ StringBuilder sb = new StringBuilder(256);
+ sb.append(prefix); sb.append("Box id=" + stackBoxId); sb.append(" weight=" + weight);
+ sb.append(" vertical=" + vertical); sb.append(" bounds=" + bounds.toShortString());
+ sb.append("\n");
+ if (children != null) {
+ sb.append(prefix); sb.append("First child=\n");
+ sb.append(children[0].toString(prefix + " "));
+ sb.append(prefix); sb.append("Second child=\n");
+ sb.append(children[1].toString(prefix + " "));
+ } else {
+ sb.append(prefix); sb.append("Stack=\n");
+ sb.append(stack.toString(prefix + " "));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return toString("");
+ }
+ }
+
+ /**
+ * Information you can retrieve about an ActivityStack in the system.
+ * @hide
+ */
+ public static class StackInfo implements Parcelable {
+ public int stackId;
+ public Rect bounds;
+ public int[] taskIds;
+ public String[] taskNames;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(stackId);
+ dest.writeInt(bounds.left);
+ dest.writeInt(bounds.top);
+ dest.writeInt(bounds.right);
+ dest.writeInt(bounds.bottom);
+ dest.writeIntArray(taskIds);
+ dest.writeStringArray(taskNames);
+ }
+
+ public void readFromParcel(Parcel source) {
+ stackId = source.readInt();
+ bounds = new Rect(
+ source.readInt(), source.readInt(), source.readInt(), source.readInt());
+ taskIds = source.createIntArray();
+ taskNames = source.createStringArray();
+ }
+
+ public static final Creator<StackInfo> CREATOR = new Creator<StackInfo>() {
+ @Override
+ public StackInfo createFromParcel(Parcel source) {
+ return new StackInfo(source);
+ }
+ @Override
+ public StackInfo[] newArray(int size) {
+ return new StackInfo[size];
+ }
+ };
+
+ public StackInfo() {
+ }
+
+ private StackInfo(Parcel source) {
+ readFromParcel(source);
+ }
+
+ public String toString(String prefix) {
+ StringBuilder sb = new StringBuilder(256);
+ sb.append(prefix); sb.append("Stack id="); sb.append(stackId);
+ sb.append(" bounds="); sb.append(bounds.toShortString()); sb.append("\n");
+ prefix = prefix + " ";
+ for (int i = 0; i < taskIds.length; ++i) {
+ sb.append(prefix); sb.append("taskId="); sb.append(taskIds[i]);
+ sb.append(": "); sb.append(taskNames[i]); sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return toString("");
+ }
+ }
+
/**
* @hide
*/
@@ -1241,7 +1400,18 @@
return false;
}
}
-
+
+ /**
+ * Permits an application to erase its own data from disk. This is equivalent to
+ * the user choosing to clear the app's data from within the device settings UI.
+ *
+ * @return {@code true} if the application successfully requested that the application's
+ * data be erased; {@code false} otherwise.
+ */
+ public boolean clearApplicationUserData() {
+ return clearApplicationUserData(mContext.getPackageName(), null);
+ }
+
/**
* Information you can retrieve about any processes that are in an error condition.
*/
@@ -1303,10 +1473,12 @@
public ProcessErrorStateInfo() {
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(condition);
dest.writeString(processName);
@@ -1317,7 +1489,7 @@
dest.writeString(longMsg);
dest.writeString(stackTrace);
}
-
+
public void readFromParcel(Parcel source) {
condition = source.readInt();
processName = source.readString();
@@ -1537,7 +1709,7 @@
public ComponentName importanceReasonComponent;
/**
- * When {@link importanceReasonPid} is non-0, this is the importance
+ * When {@link #importanceReasonPid} is non-0, this is the importance
* of the other pid. @hide
*/
public int importanceReasonImportance;
@@ -2009,4 +2181,58 @@
return false;
}
}
+
+ /**
+ * Perform a system dump of various state associated with the given application
+ * package name. This call blocks while the dump is being performed, so should
+ * not be done on a UI thread. The data will be written to the given file
+ * descriptor as text. An application must hold the
+ * {@link android.Manifest.permission#DUMP} permission to make this call.
+ * @param fd The file descriptor that the dump should be written to.
+ * @param packageName The name of the package that is to be dumped.
+ */
+ public void dumpPackageState(FileDescriptor fd, String packageName) {
+ dumpPackageStateStatic(fd, packageName);
+ }
+
+ /**
+ * @hide
+ */
+ public static void dumpPackageStateStatic(FileDescriptor fd, String packageName) {
+ FileOutputStream fout = new FileOutputStream(fd);
+ PrintWriter pw = new FastPrintWriter(fout);
+ dumpService(pw, fd, Context.ACTIVITY_SERVICE, new String[] { "package", packageName });
+ pw.println();
+ dumpService(pw, fd, "procstats", new String[] { packageName });
+ pw.println();
+ dumpService(pw, fd, "usagestats", new String[] { "--packages", packageName });
+ pw.println();
+ dumpService(pw, fd, "package", new String[] { packageName });
+ pw.println();
+ dumpService(pw, fd, BatteryStats.SERVICE_NAME, new String[] { packageName });
+ pw.flush();
+ }
+
+ private static void dumpService(PrintWriter pw, FileDescriptor fd, String name, String[] args) {
+ pw.print("DUMP OF SERVICE "); pw.print(name); pw.println(":");
+ IBinder service = ServiceManager.checkService(name);
+ if (service == null) {
+ pw.println(" (Service not found)");
+ return;
+ }
+ TransferPipe tp = null;
+ try {
+ pw.flush();
+ tp = new TransferPipe();
+ tp.setBufferPrefix(" ");
+ service.dump(tp.getWriteFd().getFileDescriptor(), args);
+ tp.go(fd);
+ } catch (Throwable e) {
+ if (tp != null) {
+ tp.kill();
+ }
+ pw.println("Failure dumping service:");
+ e.printStackTrace(pw);
+ }
+ }
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d4478bf..acfcb40 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -16,6 +16,7 @@
package android.app;
+import android.app.ActivityManager.StackBoxInfo;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
@@ -107,7 +108,8 @@
public ActivityManagerNative() {
attachInterface(this, descriptor);
}
-
+
+ @Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
@@ -197,7 +199,7 @@
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
IBinder resultTo = data.readStrongBinder();
- String resultWho = data.readString();
+ String resultWho = data.readString();
int requestCode = data.readInt();
int startFlags = data.readInt();
Configuration config = Configuration.CREATOR.createFromParcel(data);
@@ -223,7 +225,7 @@
}
String resolvedType = data.readString();
IBinder resultTo = data.readStrongBinder();
- String resultWho = data.readString();
+ String resultWho = data.readString();
int requestCode = data.readInt();
int flagsMask = data.readInt();
int flagsValues = data.readInt();
@@ -236,7 +238,7 @@
reply.writeInt(result);
return true;
}
-
+
case START_NEXT_MATCHING_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -267,7 +269,7 @@
case FINISH_SUB_ACTIVITY_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
- String resultWho = data.readString();
+ String resultWho = data.readString();
int requestCode = data.readInt();
finishSubActivity(token, resultWho, requestCode);
reply.writeNoException();
@@ -478,14 +480,13 @@
IThumbnailReceiver receiver = receiverBinder != null
? IThumbnailReceiver.Stub.asInterface(receiverBinder)
: null;
- List list = getTasks(maxNum, fl, receiver);
+ List<ActivityManager.RunningTaskInfo> list = getTasks(maxNum, fl, receiver);
reply.writeNoException();
int N = list != null ? list.size() : -1;
reply.writeInt(N);
int i;
for (i=0; i<N; i++) {
- ActivityManager.RunningTaskInfo info =
- (ActivityManager.RunningTaskInfo)list.get(i);
+ ActivityManager.RunningTaskInfo info = list.get(i);
info.writeToParcel(reply, 0);
}
return true;
@@ -535,14 +536,13 @@
data.enforceInterface(IActivityManager.descriptor);
int maxNum = data.readInt();
int fl = data.readInt();
- List list = getServices(maxNum, fl);
+ List<ActivityManager.RunningServiceInfo> list = getServices(maxNum, fl);
reply.writeNoException();
int N = list != null ? list.size() : -1;
reply.writeInt(N);
int i;
for (i=0; i<N; i++) {
- ActivityManager.RunningServiceInfo info =
- (ActivityManager.RunningServiceInfo)list.get(i);
+ ActivityManager.RunningServiceInfo info = list.get(i);
info.writeToParcel(reply, 0);
}
return true;
@@ -555,7 +555,7 @@
reply.writeTypedList(list);
return true;
}
-
+
case GET_RUNNING_APP_PROCESSES_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
List<ActivityManager.RunningAppProcessInfo> list = getRunningAppProcesses();
@@ -609,6 +609,67 @@
return true;
}
+ case CREATE_STACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int taskId = data.readInt();
+ int relativeStackId = data.readInt();
+ int position = data.readInt();
+ float weight = data.readFloat();
+ int res = createStack(taskId, relativeStackId, position, weight);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
+ case MOVE_TASK_TO_STACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int taskId = data.readInt();
+ int stackId = data.readInt();
+ boolean toTop = data.readInt() != 0;
+ moveTaskToStack(taskId, stackId, toTop);
+ reply.writeNoException();
+ return true;
+ }
+
+ case RESIZE_STACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int stackBoxId = data.readInt();
+ float weight = data.readFloat();
+ resizeStackBox(stackBoxId, weight);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_STACK_BOXES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ List<StackBoxInfo> list = getStackBoxes();
+ reply.writeNoException();
+ reply.writeTypedList(list);
+ return true;
+ }
+
+ case GET_STACK_BOX_INFO_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int stackBoxId = data.readInt();
+ StackBoxInfo info = getStackBoxInfo(stackBoxId);
+ reply.writeNoException();
+ if (info != null) {
+ reply.writeInt(1);
+ info.writeToParcel(reply, 0);
+ } else {
+ reply.writeInt(0);
+ }
+ return true;
+ }
+
+ case SET_FOCUSED_STACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int stackId = data.readInt();
+ setFocusedStack(stackId);
+ reply.writeNoException();
+ return true;
+ }
+
case GET_TASK_FOR_ACTIVITY_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -1033,9 +1094,9 @@
reply.writeInt(res);
return true;
}
-
+
case CLEAR_APP_DATA_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
+ data.enforceInterface(IActivityManager.descriptor);
String packageName = data.readString();
IPackageDataObserver observer = IPackageDataObserver.Stub.asInterface(
data.readStrongBinder());
@@ -1045,7 +1106,7 @@
reply.writeInt(res ? 1 : 0);
return true;
}
-
+
case GRANT_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -1057,7 +1118,7 @@
reply.writeNoException();
return true;
}
-
+
case REVOKE_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -1068,7 +1129,7 @@
reply.writeNoException();
return true;
}
-
+
case SHOW_WAITING_FOR_DEBUGGER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -1257,7 +1318,7 @@
reply.writeNoException();
return true;
}
-
+
case FORCE_STOP_PACKAGE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String packageName = data.readString();
@@ -1363,7 +1424,8 @@
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
int appid = data.readInt();
- killApplicationWithAppId(pkg, appid);
+ String reason = data.readString();
+ killApplicationWithAppId(pkg, appid, reason);
reply.writeNoException();
return true;
}
@@ -1437,6 +1499,22 @@
return true;
}
+ case CONVERT_FROM_TRANSLUCENT_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ convertFromTranslucent(token);
+ reply.writeNoException();
+ return true;
+ }
+
+ case CONVERT_TO_TRANSLUCENT_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ convertToTranslucent(token);
+ reply.writeNoException();
+ return true;
+ }
+
case SET_IMMERSIVE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -1879,6 +1957,21 @@
return true;
}
+ case REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ reportActivityFullyDrawn(token);
+ reply.writeNoException();
+ return true;
+ }
+
+ case NOTIFY_ACTIVITY_DRAWN_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ notifyActivityDrawn(token);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -2565,6 +2658,94 @@
data.recycle();
reply.recycle();
}
+ @Override
+ public int createStack(int taskId, int relativeStackBoxId, int position, float weight)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ data.writeInt(relativeStackBoxId);
+ data.writeInt(position);
+ data.writeFloat(weight);
+ mRemote.transact(CREATE_STACK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ @Override
+ public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ data.writeInt(stackId);
+ data.writeInt(toTop ? 1 : 0);
+ mRemote.transact(MOVE_TASK_TO_STACK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ @Override
+ public void resizeStackBox(int stackBoxId, float weight) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(stackBoxId);
+ data.writeFloat(weight);
+ mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ @Override
+ public List<StackBoxInfo> getStackBoxes() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_STACK_BOXES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ArrayList<StackBoxInfo> list = reply.createTypedArrayList(StackBoxInfo.CREATOR);
+ data.recycle();
+ reply.recycle();
+ return list;
+ }
+ @Override
+ public StackBoxInfo getStackBoxInfo(int stackBoxId) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(stackBoxId);
+ mRemote.transact(GET_STACK_BOX_INFO_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ StackBoxInfo info = null;
+ if (res != 0) {
+ info = StackBoxInfo.CREATOR.createFromParcel(reply);
+ }
+ data.recycle();
+ reply.recycle();
+ return info;
+ }
+ @Override
+ public void setFocusedStack(int stackId) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(stackId);
+ mRemote.transact(SET_FOCUSED_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -3575,12 +3756,14 @@
data.recycle();
}
- public void killApplicationWithAppId(String pkg, int appid) throws RemoteException {
+ public void killApplicationWithAppId(String pkg, int appid, String reason)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(pkg);
data.writeInt(appid);
+ data.writeString(reason);
mRemote.transact(KILL_APPLICATION_WITH_APPID_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -3671,7 +3854,31 @@
data.recycle();
reply.recycle();
}
-
+
+ public void convertFromTranslucent(IBinder token)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(CONVERT_FROM_TRANSLUCENT_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void convertToTranslucent(IBinder token)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
public void setImmersive(IBinder token, boolean immersive)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -4291,5 +4498,27 @@
reply.recycle();
}
+ public void reportActivityFullyDrawn(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void notifyActivityDrawn(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(NOTIFY_ACTIVITY_DRAWN_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4268fa6..b24aeb0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -75,7 +75,6 @@
import android.util.LogPrinter;
import android.util.PrintWriterPrinter;
import android.util.Slog;
-import android.view.CompatibilityInfoHolder;
import android.view.Display;
import android.view.HardwareRenderer;
import android.view.View;
@@ -91,6 +90,7 @@
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SamplingProfilerIntegration;
+import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.Objects;
import com.android.org.conscrypt.OpenSSLSocketImpl;
@@ -147,7 +147,7 @@
public static final boolean DEBUG_BROADCAST = false;
private static final boolean DEBUG_RESULTS = false;
private static final boolean DEBUG_BACKUP = false;
- private static final boolean DEBUG_CONFIGURATION = false;
+ public static final boolean DEBUG_CONFIGURATION = false;
private static final boolean DEBUG_SERVICE = false;
private static final boolean DEBUG_MEMORY_TRIM = false;
private static final boolean DEBUG_PROVIDER = false;
@@ -179,8 +179,6 @@
boolean mDensityCompatMode;
Configuration mConfiguration;
Configuration mCompatConfiguration;
- Configuration mResConfiguration;
- CompatibilityInfo mResCompatibilityInfo;
Application mInitialApplication;
final ArrayList<Application> mAllApplications
= new ArrayList<Application>();
@@ -209,14 +207,12 @@
= new HashMap<String, WeakReference<LoadedApk>>();
final HashMap<String, WeakReference<LoadedApk>> mResourcePackages
= new HashMap<String, WeakReference<LoadedApk>>();
- final HashMap<CompatibilityInfo, DisplayMetrics> mDefaultDisplayMetrics
- = new HashMap<CompatibilityInfo, DisplayMetrics>();
- final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources
- = new HashMap<ResourcesKey, WeakReference<Resources> >();
final ArrayList<ActivityClientRecord> mRelaunchingActivities
= new ArrayList<ActivityClientRecord>();
Configuration mPendingConfiguration = null;
+ private final ResourcesManager mResourcesManager;
+
private static final class ProviderKey {
final String authority;
final int userId;
@@ -539,20 +535,20 @@
IBinder requestToken;
int requestType;
}
-
+
private native void dumpGraphicsInfo(FileDescriptor fd);
private class ApplicationThread extends ApplicationThreadNative {
- private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s";
+ private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%21s %8d";
private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d";
private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s";
// Formatting for checkin service - update version if row format changes
- private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
+ private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3;
private void updatePendingConfiguration(Configuration config) {
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(config)) {
mPendingConfiguration = config;
@@ -895,17 +891,18 @@
@Override
public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin,
- boolean all, String[] args) {
+ boolean dumpInfo, boolean dumpDalvik, String[] args) {
FileOutputStream fout = new FileOutputStream(fd);
- PrintWriter pw = new PrintWriter(fout);
+ PrintWriter pw = new FastPrintWriter(fout);
try {
- return dumpMemInfo(pw, checkin, all);
+ return dumpMemInfo(pw, checkin, dumpInfo, dumpDalvik);
} finally {
pw.flush();
}
}
- private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean all) {
+ private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean dumpInfo,
+ boolean dumpDalvik) {
long nativeMax = Debug.getNativeHeapSize() / 1024;
long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
@@ -913,7 +910,7 @@
Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
- if (!all) {
+ if (!dumpInfo) {
return memInfo;
}
@@ -968,21 +965,48 @@
pw.print(memInfo.nativePss); pw.print(',');
pw.print(memInfo.dalvikPss); pw.print(',');
pw.print(memInfo.otherPss); pw.print(',');
- pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(',');
+ pw.print(memInfo.getTotalPss()); pw.print(',');
- // Heap info - shared
+ // Heap info - swappable set size
+ pw.print(memInfo.nativeSwappablePss); pw.print(',');
+ pw.print(memInfo.dalvikSwappablePss); pw.print(',');
+ pw.print(memInfo.otherSwappablePss); pw.print(',');
+ pw.print(memInfo.getTotalSwappablePss()); pw.print(',');
+
+ // Heap info - shared dirty
pw.print(memInfo.nativeSharedDirty); pw.print(',');
pw.print(memInfo.dalvikSharedDirty); pw.print(',');
pw.print(memInfo.otherSharedDirty); pw.print(',');
- pw.print(memInfo.nativeSharedDirty + memInfo.dalvikSharedDirty
- + memInfo.otherSharedDirty); pw.print(',');
+ pw.print(memInfo.getTotalSharedDirty()); pw.print(',');
- // Heap info - private
+ // Heap info - shared clean
+ pw.print(memInfo.nativeSharedClean); pw.print(',');
+ pw.print(memInfo.dalvikSharedClean); pw.print(',');
+ pw.print(memInfo.otherSharedClean); pw.print(',');
+ pw.print(memInfo.getTotalSharedClean()); pw.print(',');
+
+ // Heap info - private Dirty
pw.print(memInfo.nativePrivateDirty); pw.print(',');
pw.print(memInfo.dalvikPrivateDirty); pw.print(',');
pw.print(memInfo.otherPrivateDirty); pw.print(',');
- pw.print(memInfo.nativePrivateDirty + memInfo.dalvikPrivateDirty
- + memInfo.otherPrivateDirty); pw.print(',');
+ pw.print(memInfo.getTotalPrivateDirty()); pw.print(',');
+
+ // Heap info - private Clean
+ pw.print(memInfo.nativePrivateClean); pw.print(',');
+ pw.print(memInfo.dalvikPrivateClean); pw.print(',');
+ pw.print(memInfo.otherPrivateClean); pw.print(',');
+ pw.print(memInfo.getTotalPrivateClean()); pw.print(',');
+
+ // Heap info - other areas
+ for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
+ pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(',');
+ pw.print(memInfo.getOtherPss(i)); pw.print(',');
+ pw.print(memInfo.getOtherSwappablePss(i)); pw.print(',');
+ pw.print(memInfo.getOtherSharedDirty(i)); pw.print(',');
+ pw.print(memInfo.getOtherSharedClean(i)); pw.print(',');
+ pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(',');
+ pw.print(memInfo.getOtherPrivateClean(i)); pw.print(',');
+ }
// Object counts
pw.print(viewInstanceCount); pw.print(',');
@@ -1018,34 +1042,80 @@
}
// otherwise, show human-readable format
- printRow(pw, HEAP_COLUMN, "", "", "Shared", "Private", "Heap", "Heap", "Heap");
- printRow(pw, HEAP_COLUMN, "", "Pss", "Dirty", "Dirty", "Size", "Alloc", "Free");
+ printRow(pw, HEAP_COLUMN, "", "Pss", "Pss","Shared", "Private", "Shared", "Private",
+ "Heap", "Heap", "Heap");
+ printRow(pw, HEAP_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", "Clean", "Clean",
+ "Size", "Alloc", "Free");
printRow(pw, HEAP_COLUMN, "", "------", "------", "------", "------", "------",
- "------");
- printRow(pw, HEAP_COLUMN, "Native", memInfo.nativePss, memInfo.nativeSharedDirty,
- memInfo.nativePrivateDirty, nativeMax, nativeAllocated, nativeFree);
- printRow(pw, HEAP_COLUMN, "Dalvik", memInfo.dalvikPss, memInfo.dalvikSharedDirty,
- memInfo.dalvikPrivateDirty, dalvikMax, dalvikAllocated, dalvikFree);
+ "------", "------", "------", "------");
+ printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, memInfo.nativeSwappablePss,
+ memInfo.nativeSharedDirty,
+ memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
+ memInfo.nativePrivateClean, nativeMax, nativeAllocated, nativeFree);
+ printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, memInfo.dalvikSwappablePss,
+ memInfo.dalvikSharedDirty,
+ memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
+ memInfo.dalvikPrivateClean, dalvikMax, dalvikAllocated, dalvikFree);
int otherPss = memInfo.otherPss;
+ int otherSwappablePss = memInfo.otherSwappablePss;
int otherSharedDirty = memInfo.otherSharedDirty;
int otherPrivateDirty = memInfo.otherPrivateDirty;
+ int otherSharedClean = memInfo.otherSharedClean;
+ int otherPrivateClean = memInfo.otherPrivateClean;
for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
- printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
- memInfo.getOtherPss(i), memInfo.getOtherSharedDirty(i),
- memInfo.getOtherPrivateDirty(i), "", "", "");
- otherPss -= memInfo.getOtherPss(i);
- otherSharedDirty -= memInfo.getOtherSharedDirty(i);
- otherPrivateDirty -= memInfo.getOtherPrivateDirty(i);
+ final int myPss = memInfo.getOtherPss(i);
+ final int mySwappablePss = memInfo.getOtherSwappablePss(i);
+ final int mySharedDirty = memInfo.getOtherSharedDirty(i);
+ final int myPrivateDirty = memInfo.getOtherPrivateDirty(i);
+ final int mySharedClean = memInfo.getOtherSharedClean(i);
+ final int myPrivateClean = memInfo.getOtherPrivateClean(i);
+ if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
+ || mySharedClean != 0 || myPrivateClean != 0) {
+ printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
+ myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
+ mySharedClean, myPrivateClean, "", "", "");
+ otherPss -= myPss;
+ otherSwappablePss -= mySwappablePss;
+ otherSharedDirty -= mySharedDirty;
+ otherPrivateDirty -= myPrivateDirty;
+ otherSharedClean -= mySharedClean;
+ otherPrivateClean -= myPrivateClean;
+ }
}
- printRow(pw, HEAP_COLUMN, "Unknown", otherPss, otherSharedDirty,
- otherPrivateDirty, "", "", "");
+
+
+ printRow(pw, HEAP_COLUMN, "Unknown", otherPss, otherSwappablePss, otherSharedDirty,
+ otherPrivateDirty, otherSharedClean, otherPrivateClean,"", "", "");
printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(),
+ memInfo.getTotalSwappablePss(),
memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
- nativeMax+dalvikMax, nativeAllocated+dalvikAllocated,
- nativeFree+dalvikFree);
+ memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
+ nativeMax+dalvikMax,
+ nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
+
+ if (dumpDalvik) {
+ pw.println(" ");
+ pw.println(" Dalvik Details");
+
+ for (int i=Debug.MemoryInfo.NUM_OTHER_STATS;
+ i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) {
+ final int myPss = memInfo.getOtherPss(i);
+ final int mySwappablePss = memInfo.getOtherSwappablePss(i);
+ final int mySharedDirty = memInfo.getOtherSharedDirty(i);
+ final int myPrivateDirty = memInfo.getOtherPrivateDirty(i);
+ final int mySharedClean = memInfo.getOtherSharedClean(i);
+ final int myPrivateClean = memInfo.getOtherPrivateClean(i);
+ if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
+ || mySharedClean != 0 || myPrivateClean != 0) {
+ printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
+ myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
+ mySharedClean, myPrivateClean, "", "", "");
+ }
+ }
+ }
pw.println(" ");
pw.println(" Objects");
@@ -1105,7 +1175,7 @@
@Override
public void dumpDbInfo(FileDescriptor fd, String[] args) {
- PrintWriter pw = new PrintWriter(new FileOutputStream(fd));
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
PrintWriterPrinter printer = new PrintWriterPrinter(pw);
SQLiteDebug.dump(printer, args);
pw.flush();
@@ -1145,6 +1215,9 @@
queueOrSendMessage(H.TRIM_MEMORY, null, level);
}
+ public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) {
+ queueOrSendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
+ }
}
private class H extends Handler {
@@ -1192,6 +1265,7 @@
public static final int DUMP_PROVIDER = 141;
public static final int UNSTABLE_PROVIDER_DIED = 142;
public static final int REQUEST_ACTIVITY_EXTRAS = 143;
+ public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
@@ -1239,6 +1313,7 @@
case DUMP_PROVIDER: return "DUMP_PROVIDER";
case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
case REQUEST_ACTIVITY_EXTRAS: return "REQUEST_ACTIVITY_EXTRAS";
+ case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
}
}
return Integer.toString(code);
@@ -1453,6 +1528,9 @@
case REQUEST_ACTIVITY_EXTRAS:
handleRequestActivityExtras((RequestActivityExtras)msg.obj);
break;
+ case TRANSLUCENT_CONVERSION_COMPLETE:
+ handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1);
+ break;
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
@@ -1485,6 +1563,7 @@
}
private class Idler implements MessageQueue.IdleHandler {
+ @Override
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
boolean stopProfiling = false;
@@ -1523,70 +1602,13 @@
}
final class GcIdler implements MessageQueue.IdleHandler {
+ @Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}
- private static class ResourcesKey {
- final private String mResDir;
- final private int mDisplayId;
- final private Configuration mOverrideConfiguration;
- final private float mScale;
- final private int mHash;
-
- ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration, float scale) {
- mResDir = resDir;
- mDisplayId = displayId;
- if (overrideConfiguration != null) {
- if (Configuration.EMPTY.equals(overrideConfiguration)) {
- overrideConfiguration = null;
- }
- }
- mOverrideConfiguration = overrideConfiguration;
- mScale = scale;
- int hash = 17;
- hash = 31 * hash + mResDir.hashCode();
- hash = 31 * hash + mDisplayId;
- hash = 31 * hash + (mOverrideConfiguration != null
- ? mOverrideConfiguration.hashCode() : 0);
- hash = 31 * hash + Float.floatToIntBits(mScale);
- mHash = hash;
- }
-
- @Override
- public int hashCode() {
- return mHash;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof ResourcesKey)) {
- return false;
- }
- ResourcesKey peer = (ResourcesKey) obj;
- if (!mResDir.equals(peer.mResDir)) {
- return false;
- }
- if (mDisplayId != peer.mDisplayId) {
- return false;
- }
- if (mOverrideConfiguration != peer.mOverrideConfiguration) {
- if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
- return false;
- }
- if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) {
- return false;
- }
- }
- if (mScale != peer.mScale) {
- return false;
- }
- return true;
- }
- }
-
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}
@@ -1620,54 +1642,13 @@
return sPackageManager;
}
- private void flushDisplayMetricsLocked() {
- mDefaultDisplayMetrics.clear();
- }
-
- DisplayMetrics getDisplayMetricsLocked(int displayId, CompatibilityInfo ci) {
- boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(ci) : null;
- if (dm != null) {
- return dm;
- }
- dm = new DisplayMetrics();
-
- DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
- if (displayManager == null) {
- // may be null early in system startup
- dm.setToDefaults();
- return dm;
- }
-
- if (isDefaultDisplay) {
- mDefaultDisplayMetrics.put(ci, dm);
- }
-
- CompatibilityInfoHolder cih = new CompatibilityInfoHolder();
- cih.set(ci);
- Display d = displayManager.getCompatibleDisplay(displayId, cih);
- if (d != null) {
- d.getMetrics(dm);
- } else {
- // Display no longer exists
- // FIXME: This would not be a problem if we kept the Display object around
- // instead of using the raw display id everywhere. The Display object caches
- // its information even after the display has been removed.
- dm.setToDefaults();
- }
- //Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h="
- // + metrics.heightPixels + " den=" + metrics.density
- // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi);
- return dm;
- }
-
private Configuration mMainThreadConfig = new Configuration();
Configuration applyConfigCompatMainThread(int displayDensity, Configuration config,
CompatibilityInfo compat) {
if (config == null) {
return null;
}
- if (compat != null && !compat.supportsScreen()) {
+ if (!compat.supportsScreen()) {
mMainThreadConfig.setTo(config);
config = mMainThreadConfig;
compat.applyToConfiguration(displayDensity, config);
@@ -1676,93 +1657,13 @@
}
/**
- * Creates the top level Resources for applications with the given compatibility info.
- *
- * @param resDir the resource directory.
- * @param compInfo the compability info. It will use the default compatibility info when it's
- * null.
- */
- Resources getTopLevelResources(String resDir,
- int displayId, Configuration overrideConfiguration,
- CompatibilityInfo compInfo) {
- ResourcesKey key = new ResourcesKey(resDir,
- displayId, overrideConfiguration,
- compInfo.applicationScale);
- Resources r;
- synchronized (mPackages) {
- // Resources is app scale dependent.
- if (false) {
- Slog.w(TAG, "getTopLevelResources: " + resDir + " / "
- + compInfo.applicationScale);
- }
- WeakReference<Resources> wr = mActiveResources.get(key);
- r = wr != null ? wr.get() : null;
- //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
- if (r != null && r.getAssets().isUpToDate()) {
- if (false) {
- Slog.w(TAG, "Returning cached resources " + r + " " + resDir
- + ": appScale=" + r.getCompatibilityInfo().applicationScale);
- }
- return r;
- }
- }
-
- //if (r != null) {
- // Slog.w(TAG, "Throwing away out-of-date resources!!!! "
- // + r + " " + resDir);
- //}
-
- AssetManager assets = new AssetManager();
- if (assets.addAssetPath(resDir) == 0) {
- return null;
- }
-
- //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
- DisplayMetrics dm = getDisplayMetricsLocked(displayId, null);
- Configuration config;
- boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- if (!isDefaultDisplay || key.mOverrideConfiguration != null) {
- config = new Configuration(getConfiguration());
- if (!isDefaultDisplay) {
- applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
- }
- if (key.mOverrideConfiguration != null) {
- config.updateFrom(key.mOverrideConfiguration);
- }
- } else {
- config = getConfiguration();
- }
- r = new Resources(assets, dm, config, compInfo);
- if (false) {
- Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
- + r.getConfiguration() + " appScale="
- + r.getCompatibilityInfo().applicationScale);
- }
-
- synchronized (mPackages) {
- WeakReference<Resources> wr = mActiveResources.get(key);
- Resources existing = wr != null ? wr.get() : null;
- if (existing != null && existing.getAssets().isUpToDate()) {
- // Someone else already created the resources while we were
- // unlocked; go ahead and use theirs.
- r.getAssets().close();
- return existing;
- }
-
- // XXX need to remove entries when weak references go away
- mActiveResources.put(key, new WeakReference<Resources>(r));
- return r;
- }
- }
-
- /**
* Creates the top level resources for the given package.
*/
Resources getTopLevelResources(String resDir,
int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
- return getTopLevelResources(resDir, displayId, overrideConfiguration,
- pkgInfo.mCompatibilityInfo.get());
+ return mResourcesManager.getTopLevelResources(resDir, displayId, overrideConfiguration,
+ pkgInfo.getCompatibilityInfo(), null);
}
final Handler getHandler() {
@@ -1776,7 +1677,7 @@
public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
int flags, int userId) {
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) {
ref = mPackages.get(packageName);
@@ -1846,7 +1747,7 @@
}
public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (includeCode) {
ref = mPackages.get(packageName);
@@ -1859,7 +1760,7 @@
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (includeCode) {
ref = mPackages.get(aInfo.packageName);
@@ -1891,6 +1792,7 @@
}
ActivityThread() {
+ mResourcesManager = ResourcesManager.getInstance();
}
public ApplicationThread getApplicationThread()
@@ -1903,10 +1805,6 @@
return mInstrumentation;
}
- public Configuration getConfiguration() {
- return mResConfiguration;
- }
-
public boolean isProfiling() {
return mProfiler != null && mProfiler.profileFile != null
&& mProfiler.profileFd == null;
@@ -1936,10 +1834,8 @@
LoadedApk info = new LoadedApk(this, "android", context, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
context.init(info, null, this);
- context.getResources().updateConfiguration(
- getConfiguration(), getDisplayMetricsLocked(
- Display.DEFAULT_DISPLAY,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO));
+ context.getResources().updateConfiguration(mResourcesManager.getConfiguration(),
+ mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY));
mSystemContext = context;
//Slog.i(TAG, "Created system resources " + context.getResources()
// + ": " + context.getResources().getConfiguration());
@@ -1965,7 +1861,7 @@
dalvik.system.VMRuntime.getRuntime().startJitCompilation();
}
}
-
+
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
@@ -2232,7 +2128,7 @@
DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
for (int displayId : dm.getDisplayIds()) {
if (displayId != Display.DEFAULT_DISPLAY) {
- Display display = dm.getRealDisplay(displayId);
+ Display display = dm.getRealDisplay(displayId, r.token);
baseContext = appContext.createDisplayContext(display);
break;
}
@@ -2367,7 +2263,14 @@
} catch (RemoteException e) {
}
}
-
+
+ public void handleTranslucentConversionComplete(IBinder token, boolean drawComplete) {
+ ActivityClientRecord r = mActivities.get(token);
+ if (r != null) {
+ r.activity.onTranslucentConversionComplete(drawComplete);
+ }
+ }
+
private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
/**
@@ -2651,7 +2554,8 @@
try {
Service s = mServices.get(info.token);
if (s != null) {
- PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(
+ info.fd.getFileDescriptor()));
s.dump(info.fd.getFileDescriptor(), pw, info.args);
pw.flush();
}
@@ -2666,7 +2570,8 @@
try {
ActivityClientRecord r = mActivities.get(info.token);
if (r != null && r.activity != null) {
- PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(
+ info.fd.getFileDescriptor()));
r.activity.dump(info.prefix, info.fd.getFileDescriptor(), pw, info.args);
pw.flush();
}
@@ -2681,7 +2586,8 @@
try {
ProviderClientRecord r = mLocalProviders.get(info.token);
if (r != null && r.mLocalProvider != null) {
- PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(
+ info.fd.getFileDescriptor()));
r.mLocalProvider.dump(info.fd.getFileDescriptor(), pw, info.args);
pw.flush();
}
@@ -3331,7 +3237,7 @@
}
private void handleSetCoreSettings(Bundle coreSettings) {
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
mCoreSettings = coreSettings;
}
}
@@ -3339,11 +3245,11 @@
private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
LoadedApk apk = peekPackageInfo(data.pkg, false);
if (apk != null) {
- apk.mCompatibilityInfo.set(data.info);
+ apk.setCompatibilityInfo(data.info);
}
apk = peekPackageInfo(data.pkg, true);
if (apk != null) {
- apk.mCompatibilityInfo.set(data.info);
+ apk.setCompatibilityInfo(data.info);
}
handleConfigurationChanged(mConfiguration, data.info);
WindowManagerGlobal.getInstance().reportNewConfiguration(mConfiguration);
@@ -3421,7 +3327,7 @@
private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
ActivityClientRecord r = mActivities.get(token);
- Class activityClass = null;
+ Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null) {
activityClass = r.activity.getClass();
@@ -3576,7 +3482,7 @@
boolean fromServer) {
ActivityClientRecord target = null;
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
for (int i=0; i<mRelaunchingActivities.size(); i++) {
ActivityClientRecord r = mRelaunchingActivities.get(i);
if (r.token == token) {
@@ -3637,7 +3543,7 @@
// First: make sure we have the most recent configuration and most
// recent version of the activity, or skip it if some previous call
// had taken a more recent version.
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
int N = mRelaunchingActivities.size();
IBinder token = tmp.token;
tmp = null;
@@ -3766,7 +3672,7 @@
ArrayList<ComponentCallbacks2> callbacks
= new ArrayList<ComponentCallbacks2>();
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
final int N = mAllApplications.size();
for (int i=0; i<N; i++) {
callbacks.add(mAllApplications.get(i));
@@ -3775,8 +3681,9 @@
for (ActivityClientRecord ar : mActivities.values()) {
Activity a = ar.activity;
if (a != null) {
- Configuration thisConfig = applyConfigCompatMainThread(mCurDefaultDisplayDpi,
- newConfig, ar.packageInfo.mCompatibilityInfo.getIfNeeded());
+ Configuration thisConfig = applyConfigCompatMainThread(
+ mCurDefaultDisplayDpi, newConfig,
+ ar.packageInfo.getCompatibilityInfo());
if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
// If the activity is currently resumed, its configuration
// needs to change right now.
@@ -3859,115 +3766,18 @@
}
public final void applyConfigurationToResources(Configuration config) {
- synchronized (mPackages) {
- applyConfigurationToResourcesLocked(config, null);
+ synchronized (mResourcesManager) {
+ mResourcesManager.applyConfigurationToResourcesLocked(config, null);
}
}
- final boolean applyConfigurationToResourcesLocked(Configuration config,
- CompatibilityInfo compat) {
- if (mResConfiguration == null) {
- mResConfiguration = new Configuration();
- }
- if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
- + mResConfiguration.seq + ", newSeq=" + config.seq);
- return false;
- }
- int changes = mResConfiguration.updateFrom(config);
- flushDisplayMetricsLocked();
- DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(
- Display.DEFAULT_DISPLAY, null);
-
- if (compat != null && (mResCompatibilityInfo == null ||
- !mResCompatibilityInfo.equals(compat))) {
- mResCompatibilityInfo = compat;
- changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
- | ActivityInfo.CONFIG_SCREEN_SIZE
- | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
- }
-
- // set it for java, this also affects newly created Resources
- if (config.locale != null) {
- Locale.setDefault(config.locale);
- }
-
- Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
-
- ApplicationPackageManager.configurationChanged();
- //Slog.i(TAG, "Configuration changed in " + currentPackageName());
-
- Configuration tmpConfig = null;
-
- Iterator<Map.Entry<ResourcesKey, WeakReference<Resources>>> it =
- mActiveResources.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<ResourcesKey, WeakReference<Resources>> entry = it.next();
- Resources r = entry.getValue().get();
- if (r != null) {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
- + r + " config to: " + config);
- int displayId = entry.getKey().mDisplayId;
- boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- DisplayMetrics dm = defaultDisplayMetrics;
- Configuration overrideConfig = entry.getKey().mOverrideConfiguration;
- if (!isDefaultDisplay || overrideConfig != null) {
- if (tmpConfig == null) {
- tmpConfig = new Configuration();
- }
- tmpConfig.setTo(config);
- if (!isDefaultDisplay) {
- dm = getDisplayMetricsLocked(displayId, null);
- applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
- }
- if (overrideConfig != null) {
- tmpConfig.updateFrom(overrideConfig);
- }
- r.updateConfiguration(tmpConfig, dm, compat);
- } else {
- r.updateConfiguration(config, dm, compat);
- }
- //Slog.i(TAG, "Updated app resources " + v.getKey()
- // + " " + r + ": " + r.getConfiguration());
- } else {
- //Slog.i(TAG, "Removing old resources " + v.getKey());
- it.remove();
- }
- }
-
- return changes != 0;
- }
-
- final void applyNonDefaultDisplayMetricsToConfigurationLocked(
- DisplayMetrics dm, Configuration config) {
- config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
- config.densityDpi = dm.densityDpi;
- config.screenWidthDp = (int)(dm.widthPixels / dm.density);
- config.screenHeightDp = (int)(dm.heightPixels / dm.density);
- int sl = Configuration.resetScreenLayout(config.screenLayout);
- if (dm.widthPixels > dm.heightPixels) {
- config.orientation = Configuration.ORIENTATION_LANDSCAPE;
- config.screenLayout = Configuration.reduceScreenLayout(sl,
- config.screenWidthDp, config.screenHeightDp);
- } else {
- config.orientation = Configuration.ORIENTATION_PORTRAIT;
- config.screenLayout = Configuration.reduceScreenLayout(sl,
- config.screenHeightDp, config.screenWidthDp);
- }
- config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
- config.compatScreenWidthDp = config.screenWidthDp;
- config.compatScreenHeightDp = config.screenHeightDp;
- config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
- }
-
final Configuration applyCompatConfiguration(int displayDensity) {
Configuration config = mConfiguration;
if (mCompatConfiguration == null) {
mCompatConfiguration = new Configuration();
}
mCompatConfiguration.setTo(mConfiguration);
- if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
- mResCompatibilityInfo.applyToConfiguration(displayDensity, mCompatConfiguration);
+ if (mResourcesManager.applyCompatConfiguration(displayDensity, mCompatConfiguration)) {
config = mCompatConfiguration;
}
return config;
@@ -3977,7 +3787,7 @@
int configDiff = 0;
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
if (mPendingConfiguration != null) {
if (!mPendingConfiguration.isOtherSeqNewer(config)) {
config = mPendingConfiguration;
@@ -3993,9 +3803,9 @@
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
+ config);
-
- applyConfigurationToResourcesLocked(config, compat);
-
+
+ mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
+
if (mConfiguration == null) {
mConfiguration = new Configuration();
}
@@ -4245,7 +4055,7 @@
* reflect configuration changes. The configuration object passed
* in AppBindData can be safely assumed to be up to date
*/
- applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+ mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
mCurDefaultDisplayDpi = data.config.densityDpi;
applyCompatConfiguration(mCurDefaultDisplayDpi);
@@ -4957,6 +4767,7 @@
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
+ @Override
public void run() {
ensureJitEnabled();
}
@@ -4993,12 +4804,13 @@
DropBox.setReporter(new DropBoxReporter());
ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
+ @Override
public void onConfigurationChanged(Configuration newConfig) {
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
// We need to apply this change to the resources
// immediately, because upon returning the view
// hierarchy will be informed about it.
- if (applyConfigurationToResourcesLocked(newConfig, null)) {
+ if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
@@ -5010,8 +4822,10 @@
}
}
}
+ @Override
public void onLowMemory() {
}
+ @Override
public void onTrimMemory(int level) {
}
});
@@ -5031,12 +4845,11 @@
}
public int getIntCoreSetting(String key, int defaultValue) {
- synchronized (mPackages) {
+ synchronized (mResourcesManager) {
if (mCoreSettings != null) {
return mCoreSettings.getInt(key, defaultValue);
- } else {
- return defaultValue;
}
+ return defaultValue;
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c9776f1..de94bb7 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -33,21 +33,22 @@
/**
* API for interacting with "application operation" tracking. Allows you to:
*
- * - Note when operations are happening, and find out if they are allowed for the current caller.
- * - Disallow specific apps from doing specific operations.
- * - Collect all of the current information about operations that have been executed or are not
- * being allowed.
- * - Monitor for changes in whether an operation is allowed.
+ * <ul>
+ * <li> Note when operations are happening, and find out if they are allowed for the current
+ * caller.</li>
+ * <li> Disallow specific apps from doing specific operations.</li>
+ * <li> Collect all of the current information about operations that have been executed or are not
+ * being allowed.</li>
+ * <li> Monitor for changes in whether an operation is allowed.</li>
+ * </ul>
*
- * Each operation is identified by a single integer; these integers are a fixed set of
+ * <p>Each operation is identified by a single integer; these integers are a fixed set of
* operations, enumerated by the OP_* constants.
*
- * When checking operations, the result is a "mode" integer indicating the current setting
+ * <p></p>When checking operations, the result is a "mode" integer indicating the current setting
* for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute the operation but
* fake its behavior enough so that the caller doesn't crash), MODE_ERRORED (through a
* SecurityException back to the caller; the normal operation calls will do this for you).
- *
- * @hide
*/
public class AppOpsManager {
final Context mContext;
@@ -63,40 +64,95 @@
// - increment _NUM_OP
// - add rows to sOpToSwitch, sOpNames, sOpPerms
// - add descriptive strings to Settings/res/values/arrays.xml
+
+ /** No operation specified. */
public static final int OP_NONE = -1;
+ /** Access to coarse location information. */
public static final int OP_COARSE_LOCATION = 0;
+ /** Access to fine location information. */
public static final int OP_FINE_LOCATION = 1;
+ /** Causing GPS to run. */
public static final int OP_GPS = 2;
+ /** @hide */
public static final int OP_VIBRATE = 3;
+ /** @hide */
public static final int OP_READ_CONTACTS = 4;
+ /** @hide */
public static final int OP_WRITE_CONTACTS = 5;
+ /** @hide */
public static final int OP_READ_CALL_LOG = 6;
+ /** @hide */
public static final int OP_WRITE_CALL_LOG = 7;
+ /** @hide */
public static final int OP_READ_CALENDAR = 8;
+ /** @hide */
public static final int OP_WRITE_CALENDAR = 9;
+ /** @hide */
public static final int OP_WIFI_SCAN = 10;
+ /** @hide */
public static final int OP_POST_NOTIFICATION = 11;
+ /** @hide */
public static final int OP_NEIGHBORING_CELLS = 12;
+ /** @hide */
public static final int OP_CALL_PHONE = 13;
+ /** @hide */
public static final int OP_READ_SMS = 14;
+ /** @hide */
public static final int OP_WRITE_SMS = 15;
+ /** @hide */
public static final int OP_RECEIVE_SMS = 16;
+ /** @hide */
public static final int OP_RECEIVE_EMERGECY_SMS = 17;
+ /** @hide */
public static final int OP_RECEIVE_MMS = 18;
+ /** @hide */
public static final int OP_RECEIVE_WAP_PUSH = 19;
+ /** @hide */
public static final int OP_SEND_SMS = 20;
+ /** @hide */
public static final int OP_READ_ICC_SMS = 21;
+ /** @hide */
public static final int OP_WRITE_ICC_SMS = 22;
+ /** @hide */
public static final int OP_WRITE_SETTINGS = 23;
+ /** @hide */
public static final int OP_SYSTEM_ALERT_WINDOW = 24;
+ /** @hide */
public static final int OP_ACCESS_NOTIFICATIONS = 25;
+ /** @hide */
public static final int OP_CAMERA = 26;
+ /** @hide */
public static final int OP_RECORD_AUDIO = 27;
+ /** @hide */
public static final int OP_PLAY_AUDIO = 28;
+ /** @hide */
public static final int OP_READ_CLIPBOARD = 29;
+ /** @hide */
public static final int OP_WRITE_CLIPBOARD = 30;
/** @hide */
- public static final int _NUM_OP = 31;
+ public static final int OP_TAKE_MEDIA_BUTTONS = 31;
+ /** @hide */
+ public static final int OP_TAKE_AUDIO_FOCUS = 32;
+ /** @hide */
+ public static final int OP_AUDIO_MASTER_VOLUME = 33;
+ /** @hide */
+ public static final int OP_AUDIO_VOICE_VOLUME = 34;
+ /** @hide */
+ public static final int OP_AUDIO_RING_VOLUME = 35;
+ /** @hide */
+ public static final int OP_AUDIO_MEDIA_VOLUME = 36;
+ /** @hide */
+ public static final int OP_AUDIO_ALARM_VOLUME = 37;
+ /** @hide */
+ public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38;
+ /** @hide */
+ public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39;
+ /** @hide */
+ public static final int OP_WAKE_LOCK = 40;
+ /** Continually monitoring location data. */
+ public static final int OP_MONITOR_LOCATION = 41;
+ /** @hide */
+ public static final int _NUM_OP = 42;
/**
* This maps each operation to the operation that serves as the
@@ -138,6 +194,17 @@
OP_PLAY_AUDIO,
OP_READ_CLIPBOARD,
OP_WRITE_CLIPBOARD,
+ OP_TAKE_MEDIA_BUTTONS,
+ OP_TAKE_AUDIO_FOCUS,
+ OP_AUDIO_MASTER_VOLUME,
+ OP_AUDIO_VOICE_VOLUME,
+ OP_AUDIO_RING_VOLUME,
+ OP_AUDIO_MEDIA_VOLUME,
+ OP_AUDIO_ALARM_VOLUME,
+ OP_AUDIO_NOTIFICATION_VOLUME,
+ OP_AUDIO_BLUETOOTH_VOLUME,
+ OP_WAKE_LOCK,
+ OP_COARSE_LOCATION,
};
/**
@@ -176,6 +243,17 @@
"PLAY_AUDIO",
"READ_CLIPBOARD",
"WRITE_CLIPBOARD",
+ "TAKE_MEDIA_BUTTONS",
+ "TAKE_AUDIO_FOCUS",
+ "AUDIO_MASTER_VOLUME",
+ "AUDIO_VOICE_VOLUME",
+ "AUDIO_RING_VOLUME",
+ "AUDIO_MEDIA_VOLUME",
+ "AUDIO_ALARM_VOLUME",
+ "AUDIO_NOTIFICATION_VOLUME",
+ "AUDIO_BLUETOOTH_VOLUME",
+ "WAKE_LOCK",
+ "MONITOR_LOCATION",
};
/**
@@ -214,10 +292,22 @@
null, // no permission for playing audio
null, // no permission for reading clipboard
null, // no permission for writing clipboard
+ null, // no permission for taking media buttons
+ null, // no permission for taking audio focus
+ null, // no permission for changing master volume
+ null, // no permission for changing voice volume
+ null, // no permission for changing ring volume
+ null, // no permission for changing media volume
+ null, // no permission for changing alarm volume
+ null, // no permission for changing notification volume
+ null, // no permission for changing bluetooth volume
+ android.Manifest.permission.WAKE_LOCK,
+ null, // no permission for generic location monitoring
};
/**
* Retrieve the op switch that controls the given operation.
+ * @hide
*/
public static int opToSwitch(int op) {
return sOpToSwitch[op];
@@ -233,6 +323,7 @@
/**
* Retrieve the permission associated with an operation, or null if there is not one.
+ * @hide
*/
public static String opToPermission(int op) {
return sOpPerms[op];
@@ -240,6 +331,7 @@
/**
* Class holding all of the operation information associated with an app.
+ * @hide
*/
public static class PackageOps implements Parcelable {
private final String mPackageName;
@@ -302,6 +394,7 @@
/**
* Class holding the information about one unique operation of an application.
+ * @hide
*/
public static class OpEntry implements Parcelable {
private final int mOp;
@@ -382,7 +475,7 @@
public void opChanged(int op, String packageName);
}
- public AppOpsManager(Context context, IAppOpsService service) {
+ AppOpsManager(Context context, IAppOpsService service) {
mContext = context;
mService = service;
}
@@ -391,6 +484,7 @@
* Retrieve current operation state for all applications.
*
* @param ops The set of operations you are interested in, or null if you want all of them.
+ * @hide
*/
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
try {
@@ -406,6 +500,7 @@
* @param uid The uid of the application of interest.
* @param packageName The name of the application of interest.
* @param ops The set of operations you are interested in, or null if you want all of them.
+ * @hide
*/
public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) {
try {
@@ -415,6 +510,7 @@
return null;
}
+ /** @hide */
public void setMode(int code, int uid, String packageName, int mode) {
try {
mService.setMode(code, uid, packageName, mode);
@@ -422,6 +518,12 @@
}
}
+ /**
+ * Monitor for changes to the operating mode for the given op in the given app package.
+ * @param op The operation to monitor, one of OP_*.
+ * @param packageName The name of the application to monitor.
+ * @param callback Where to report changes.
+ */
public void startWatchingMode(int op, String packageName, final Callback callback) {
synchronized (mModeWatchers) {
IAppOpsCallback cb = mModeWatchers.get(callback);
@@ -440,6 +542,10 @@
}
}
+ /**
+ * Stop monitoring that was previously started with {@link #startWatchingMode}. All
+ * monitoring associated with this callback will be removed.
+ */
public void stopWatchingMode(Callback callback) {
synchronized (mModeWatchers) {
IAppOpsCallback cb = mModeWatchers.get(callback);
@@ -452,6 +558,22 @@
}
}
+ /**
+ * Do a quick check for whether an application might be able to perform an operation.
+ * This is <em>not</em> a security check; you must use {@link #noteOp(int, int, String)}
+ * or {@link #startOp(int, int, String)} for your actual security checks, which also
+ * ensure that the given uid and package name are consistent. This function can just be
+ * used for a quick check to see if an operation has been disabled for the application,
+ * as an early reject of some work. This does not modify the time stamp or other data
+ * about the operation.
+ * @param op The operation to check. One of the OP_* constants.
+ * @param uid The user id of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @throws SecurityException If the app has been configured to crash on this op.
+ */
public int checkOp(int op, int uid, String packageName) {
try {
int mode = mService.checkOperation(op, uid, packageName);
@@ -464,6 +586,10 @@
return MODE_IGNORED;
}
+ /**
+ * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
+ * returns {@link #MODE_ERRORED}.
+ */
public int checkOpNoThrow(int op, int uid, String packageName) {
try {
return mService.checkOperation(op, uid, packageName);
@@ -472,6 +598,20 @@
return MODE_IGNORED;
}
+ /**
+ * Make note of an application performing an operation. Note that you must pass
+ * in both the uid and name of the application to be checked; this function will verify
+ * that these two match, and if not, return {@link #MODE_IGNORED}. If this call
+ * succeeds, the last execution time of the operation for this app will be updated to
+ * the current time.
+ * @param op The operation to note. One of the OP_* constants.
+ * @param uid The user id of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @throws SecurityException If the app has been configured to crash on this op.
+ */
public int noteOp(int op, int uid, String packageName) {
try {
int mode = mService.noteOperation(op, uid, packageName);
@@ -484,6 +624,10 @@
return MODE_IGNORED;
}
+ /**
+ * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it
+ * returns {@link #MODE_ERRORED}.
+ */
public int noteOpNoThrow(int op, int uid, String packageName) {
try {
return mService.noteOperation(op, uid, packageName);
@@ -492,10 +636,27 @@
return MODE_IGNORED;
}
+ /** @hide */
public int noteOp(int op) {
return noteOp(op, Process.myUid(), mContext.getBasePackageName());
}
+ /**
+ * Report that an application has started executing a long-running operation. Note that you
+ * must pass in both the uid and name of the application to be checked; this function will
+ * verify that these two match, and if not, return {@link #MODE_IGNORED}. If this call
+ * succeeds, the last execution time of the operation for this app will be updated to
+ * the current time and the operation will be marked as "running". In this case you must
+ * later call {@link #finishOp(int, int, String)} to report when the application is no
+ * longer performing the operation.
+ * @param op The operation to start. One of the OP_* constants.
+ * @param uid The user id of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @throws SecurityException If the app has been configured to crash on this op.
+ */
public int startOp(int op, int uid, String packageName) {
try {
int mode = mService.startOperation(op, uid, packageName);
@@ -508,6 +669,10 @@
return MODE_IGNORED;
}
+ /**
+ * Like {@link #startOp} but instead of throwing a {@link SecurityException} it
+ * returns {@link #MODE_ERRORED}.
+ */
public int startOpNoThrow(int op, int uid, String packageName) {
try {
return mService.startOperation(op, uid, packageName);
@@ -516,10 +681,17 @@
return MODE_IGNORED;
}
+ /** @hide */
public int startOp(int op) {
return startOp(op, Process.myUid(), mContext.getBasePackageName());
}
+ /**
+ * Report that an application is no longer performing an operation that had previously
+ * been started with {@link #startOp(int, int, String)}. There is no validation of input
+ * or result; the parameters supplied here must be the exact same ones previously passed
+ * in when starting the operation.
+ */
public void finishOp(int op, int uid, String packageName) {
try {
mService.finishOperation(op, uid, packageName);
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 954476d..c117486 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -28,6 +28,7 @@
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Printer;
+import com.android.internal.util.FastPrintWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -327,7 +328,9 @@
*/
public CrashInfo(Throwable tr) {
StringWriter sw = new StringWriter();
- tr.printStackTrace(new PrintWriter(sw));
+ PrintWriter pw = new FastPrintWriter(sw, false, 256);
+ tr.printStackTrace(pw);
+ pw.flush();
stackTrace = sw.toString();
exceptionMessage = tr.getMessage();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 271494f..432e9b1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1296,6 +1296,28 @@
return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
+ @Override
+ public boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+ UserHandle user) {
+ try {
+ return mPM.setApplicationBlockedSettingAsUser(packageName, blocked,
+ user.getIdentifier());
+ } catch (RemoteException re) {
+ // Should never happen!
+ }
+ return false;
+ }
+
+ @Override
+ public boolean getApplicationBlockedSettingAsUser(String packageName, UserHandle user) {
+ try {
+ return mPM.getApplicationBlockedSettingAsUser(packageName, user.getIdentifier());
+ } catch (RemoteException re) {
+ // Should never happen!
+ }
+ return false;
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index b1c58f2..cc495aa 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -524,12 +524,13 @@
data.enforceInterface(IApplicationThread.descriptor);
ParcelFileDescriptor fd = data.readFileDescriptor();
boolean checkin = data.readInt() != 0;
- boolean all = data.readInt() != 0;
+ boolean dumpInfo = data.readInt() != 0;
+ boolean dumpDalvik = data.readInt() != 0;
String[] args = data.readStringArray();
Debug.MemoryInfo mi = null;
if (fd != null) {
try {
- mi = dumpMemInfo(fd.getFileDescriptor(), checkin, all, args);
+ mi = dumpMemInfo(fd.getFileDescriptor(), checkin, dumpInfo, dumpDalvik, args);
} finally {
try {
fd.close();
@@ -602,6 +603,16 @@
reply.writeNoException();
return true;
}
+
+ case SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder token = data.readStrongBinder();
+ boolean timeout = data.readInt() == 1;
+ scheduleTranslucentConversionComplete(token, timeout);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -1159,14 +1170,15 @@
IBinder.FLAG_ONEWAY);
}
- public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean all,
- String[] args) throws RemoteException {
+ public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean dumpInfo,
+ boolean dumpDalvik, String[] args) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeFileDescriptor(fd);
data.writeInt(checkin ? 1 : 0);
- data.writeInt(all ? 1 : 0);
+ data.writeInt(dumpInfo ? 1 : 0);
+ data.writeInt(dumpDalvik ? 1 : 0);
data.writeStringArray(args);
mRemote.transact(DUMP_MEM_INFO_TRANSACTION, data, reply, 0);
reply.readException();
@@ -1195,6 +1207,7 @@
data.recycle();
}
+ @Override
public void unstableProviderDied(IBinder provider) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -1203,6 +1216,7 @@
data.recycle();
}
+ @Override
public void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -1213,4 +1227,15 @@
mRemote.transact(REQUEST_ACTIVITY_EXTRAS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
+
+ @Override
+ public void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(timeout ? 1 : 0);
+ mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
}
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 1b1d341..89ee145 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -21,6 +21,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.LogWriter;
+import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -583,8 +584,9 @@
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
- PrintWriter pw = new PrintWriter(logw);
+ PrintWriter pw = new FastPrintWriter(logw, false, 1024);
dump(" ", null, pw, null);
+ pw.flush();
}
mCommitted = true;
if (mAddToBackStack) {
@@ -691,8 +693,9 @@
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "popFromBackStack: " + this);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
- PrintWriter pw = new PrintWriter(logw);
+ PrintWriter pw = new FastPrintWriter(logw, false, 1024);
dump(" ", null, pw, null);
+ pw.flush();
}
bumpBackStackNesting(-1);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 3fc82fa..6a0fbd5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -49,6 +49,7 @@
import android.hardware.ISerialManager;
import android.hardware.SerialManager;
import android.hardware.SystemSensorManager;
+import android.hardware.photography.CameraManager;
import android.hardware.display.DisplayManager;
import android.hardware.input.InputManager;
import android.hardware.usb.IUsbManager;
@@ -90,12 +91,15 @@
import android.os.SystemVibrator;
import android.os.UserManager;
import android.os.storage.StorageManager;
+import android.print.IPrintManager;
+import android.print.PrintManager;
import android.telephony.TelephonyManager;
import android.content.ClipboardManager;
import android.util.AndroidRuntimeException;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
-import android.view.CompatibilityInfoHolder;
+import android.view.DisplayAdjustments;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.WindowManagerImpl;
@@ -169,8 +173,10 @@
private final static String TAG = "ContextImpl";
private final static boolean DEBUG = false;
- private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
- new HashMap<String, SharedPreferencesImpl>();
+ /**
+ * Map from package name, to preference name, to cached preferences.
+ */
+ private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
/*package*/ LoadedApk mPackageInfo;
private String mBasePackageName;
@@ -186,6 +192,7 @@
private Context mReceiverRestrictedContext = null;
private boolean mRestricted;
private UserHandle mUser;
+ private ResourcesManager mResourcesManager;
private final Object mSync = new Object();
@@ -199,6 +206,8 @@
private static final String[] EMPTY_FILE_LIST = {};
+ final private DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
+
/**
* Override this class when the system service constructor needs a
* ContextImpl. Else, use StaticServiceFetcher below.
@@ -513,12 +522,16 @@
}});
registerService(WINDOW_SERVICE, new ServiceFetcher() {
+ Display mDefaultDisplay;
public Object getService(ContextImpl ctx) {
Display display = ctx.mDisplay;
if (display == null) {
- DisplayManager dm = (DisplayManager)ctx.getOuterContext().getSystemService(
- Context.DISPLAY_SERVICE);
- display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+ if (mDefaultDisplay == null) {
+ DisplayManager dm = (DisplayManager)ctx.getOuterContext().
+ getSystemService(Context.DISPLAY_SERVICE);
+ mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
+ }
+ display = mDefaultDisplay;
}
return new WindowManagerImpl(display);
}});
@@ -536,6 +549,20 @@
IAppOpsService service = IAppOpsService.Stub.asInterface(b);
return new AppOpsManager(ctx, service);
}});
+
+ registerService(CAMERA_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ return new CameraManager(ctx);
+ }
+ });
+
+ registerService(PRINT_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder iBinder = ServiceManager.getService(Context.PRINT_SERVICE);
+ IPrintManager service = IPrintManager.Stub.asInterface(iBinder);
+ return new PrintManager(ctx.getOuterContext(), service, UserHandle.myUserId(),
+ UserHandle.getAppId(Process.myUid()));
+ }});
}
static ContextImpl getImpl(Context context) {
@@ -667,12 +694,23 @@
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
- synchronized (sSharedPrefs) {
- sp = sSharedPrefs.get(name);
+ synchronized (mSync) {
+ if (sSharedPrefs == null) {
+ sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
+ }
+
+ final String packageName = getPackageName();
+ ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
+ if (packagePrefs == null) {
+ packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
+ sSharedPrefs.put(packageName, packagePrefs);
+ }
+
+ sp = packagePrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
- sSharedPrefs.put(name, sp);
+ packagePrefs.put(name, sp);
return sp;
}
}
@@ -1795,10 +1833,9 @@
ContextImpl c = new ContextImpl();
c.init(mPackageInfo, null, mMainThread);
- c.mResources = mMainThread.getTopLevelResources(
- mPackageInfo.getResDir(),
- getDisplayId(), overrideConfiguration,
- mResources.getCompatibilityInfo());
+ c.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
+ getDisplayId(), overrideConfiguration, mResources.getCompatibilityInfo(),
+ mActivityToken);
return c;
}
@@ -1809,17 +1846,13 @@
}
int displayId = display.getDisplayId();
- CompatibilityInfo ci = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
- CompatibilityInfoHolder cih = getCompatibilityInfo(displayId);
- if (cih != null) {
- ci = cih.get();
- }
ContextImpl context = new ContextImpl();
context.init(mPackageInfo, null, mMainThread);
context.mDisplay = display;
- context.mResources = mMainThread.getTopLevelResources(
- mPackageInfo.getResDir(), displayId, null, ci);
+ DisplayAdjustments daj = getDisplayAdjustments(displayId);
+ context.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
+ displayId, null, daj.getCompatibilityInfo(), null);
return context;
}
@@ -1833,8 +1866,8 @@
}
@Override
- public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
- return displayId == Display.DEFAULT_DISPLAY ? mPackageInfo.mCompatibilityInfo : null;
+ public DisplayAdjustments getDisplayAdjustments(int displayId) {
+ return mDisplayAdjustments;
}
private File getDataDirFile() {
@@ -1886,6 +1919,7 @@
mUser = context.mUser;
mDisplay = context.mDisplay;
mOuterContext = this;
+ mDisplayAdjustments.setCompatibilityInfo(mPackageInfo.getCompatibilityInfo());
}
final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) {
@@ -1897,17 +1931,28 @@
mPackageInfo = packageInfo;
mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;
mResources = mPackageInfo.getResources(mainThread);
+ mResourcesManager = ResourcesManager.getInstance();
- if (mResources != null && container != null
- && container.getCompatibilityInfo().applicationScale !=
- mResources.getCompatibilityInfo().applicationScale) {
+ CompatibilityInfo compatInfo =
+ container == null ? null : container.getCompatibilityInfo();
+ if (mResources != null &&
+ ((compatInfo != null && compatInfo.applicationScale !=
+ mResources.getCompatibilityInfo().applicationScale)
+ || activityToken != null)) {
if (DEBUG) {
Log.d(TAG, "loaded context has different scaling. Using container's" +
" compatiblity info:" + container.getDisplayMetrics());
}
- mResources = mainThread.getTopLevelResources(
- mPackageInfo.getResDir(), Display.DEFAULT_DISPLAY,
- null, container.getCompatibilityInfo());
+ if (compatInfo == null) {
+ compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ }
+ mDisplayAdjustments.setCompatibilityInfo(compatInfo);
+ mDisplayAdjustments.setActivityToken(activityToken);
+ mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
+ Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);
+ } else {
+ mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo());
+ mDisplayAdjustments.setActivityToken(activityToken);
}
mMainThread = mainThread;
mActivityToken = activityToken;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index b3d99c5..cda2c5f 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -16,6 +16,8 @@
package android.app;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import com.android.internal.app.ActionBarImpl;
import com.android.internal.policy.PolicyManager;
@@ -264,6 +266,9 @@
mDecor = mWindow.getDecorView();
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
+ final ApplicationInfo info = mContext.getApplicationInfo();
+ mWindow.setDefaultIcon(info.icon);
+ mWindow.setDefaultLogo(info.logo);
mActionBar = new ActionBarImpl(this);
}
@@ -301,6 +306,7 @@
* method to do cleanup when the dialog is dismissed, instead implement
* that in {@link #onStop}.
*/
+ @Override
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
@@ -320,7 +326,7 @@
}
try {
- mWindowManager.removeView(mDecor);
+ mWindowManager.removeViewImmediate(mDecor);
} finally {
if (mActionMode != null) {
mActionMode.finish();
@@ -329,7 +335,7 @@
mWindow.closeAllPanels();
onStop();
mShowing = false;
-
+
sendDismissMessage();
}
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 72c9156..7358f73 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -36,6 +36,7 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -445,12 +446,13 @@
private void throwException(RuntimeException ex) {
Log.e(TAG, ex.getMessage());
LogWriter logw = new LogWriter(Log.ERROR, TAG);
- PrintWriter pw = new PrintWriter(logw);
+ PrintWriter pw = new FastPrintWriter(logw, false, 1024);
if (mActivity != null) {
Log.e(TAG, "Activity state:");
try {
mActivity.dump(" ", null, pw, new String[] { });
} catch (Exception e) {
+ pw.flush();
Log.e(TAG, "Failed dumping state", e);
}
} else {
@@ -458,9 +460,11 @@
try {
dump(" ", null, pw, new String[] { });
} catch (Exception e) {
+ pw.flush();
Log.e(TAG, "Failed dumping state", e);
}
}
+ pw.flush();
throw ex;
}
@@ -1806,8 +1810,9 @@
Log.v(TAG, "restoreAllState: back stack #" + i
+ " (index " + bse.mIndex + "): " + bse);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
- PrintWriter pw = new PrintWriter(logw);
+ PrintWriter pw = new FastPrintWriter(logw, false, 1024);
bse.dump(" ", pw, false);
+ pw.flush();
}
mBackStack.add(bse);
if (bse.mIndex >= 0) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index a21caee..19858dc 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -16,6 +16,9 @@
package android.app;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.RunningServiceInfo;
+import android.app.ActivityManager.StackBoxInfo;
import android.content.ComponentName;
import android.content.ContentProviderNative;
import android.content.IContentProvider;
@@ -99,19 +102,26 @@
public void activityDestroyed(IBinder token) throws RemoteException;
public String getCallingPackage(IBinder token) throws RemoteException;
public ComponentName getCallingActivity(IBinder token) throws RemoteException;
- public List getTasks(int maxNum, int flags,
+ public List<RunningTaskInfo> getTasks(int maxNum, int flags,
IThumbnailReceiver receiver) throws RemoteException;
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags, int userId) throws RemoteException;
public ActivityManager.TaskThumbnails getTaskThumbnails(int taskId) throws RemoteException;
public Bitmap getTaskTopThumbnail(int taskId) throws RemoteException;
- public List getServices(int maxNum, int flags) throws RemoteException;
+ public List<RunningServiceInfo> getServices(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
throws RemoteException;
public void moveTaskToFront(int task, int flags, Bundle options) throws RemoteException;
public void moveTaskToBack(int task) throws RemoteException;
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
+ public int createStack(int taskId, int relativeStackBoxId, int position, float weight)
+ throws RemoteException;
+ public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
+ public void resizeStackBox(int stackBoxId, float weight) throws RemoteException;
+ public List<StackBoxInfo> getStackBoxes() throws RemoteException;
+ public StackBoxInfo getStackBoxInfo(int stackBoxId) throws RemoteException;
+ public void setFocusedStack(int stackId) throws RemoteException;
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
/* oneway */
public void reportThumbnail(IBinder token,
@@ -149,14 +159,14 @@
public void serviceDoneExecuting(IBinder token, int type, int startId,
int res) throws RemoteException;
public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
-
+
public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
throws RemoteException;
public void clearPendingBackup() throws RemoteException;
public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException;
public void killApplicationProcess(String processName, int uid) throws RemoteException;
-
+
public boolean startInstrumentation(ComponentName className, String profileFile,
int flags, Bundle arguments, IInstrumentationWatcher watcher,
IUiAutomationConnection connection, int userId) throws RemoteException;
@@ -168,7 +178,7 @@
public void setRequestedOrientation(IBinder token,
int requestedOrientation) throws RemoteException;
public int getRequestedOrientation(IBinder token) throws RemoteException;
-
+
public ComponentName getActivityClassForToken(IBinder token) throws RemoteException;
public String getPackageForToken(IBinder token) throws RemoteException;
@@ -181,16 +191,16 @@
final IPackageDataObserver observer, int userId) throws RemoteException;
public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;
public int getUidForIntentSender(IIntentSender sender) throws RemoteException;
-
+
public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
boolean requireFull, String name, String callerPackage) throws RemoteException;
public void setProcessLimit(int max) throws RemoteException;
public int getProcessLimit() throws RemoteException;
-
+
public void setProcessForeground(IBinder token, int pid,
boolean isForeground) throws RemoteException;
-
+
public int checkPermission(String permission, int pid, int uid)
throws RemoteException;
@@ -248,7 +258,7 @@
StrictMode.ViolationInfo crashInfo) throws RemoteException;
/*
- * This will deliver the specified signal to all the persistent processes. Currently only
+ * This will deliver the specified signal to all the persistent processes. Currently only
* SIGUSR1 is delivered. All others are ignored.
*/
public void signalPersistentProcesses(int signal) throws RemoteException;
@@ -274,7 +284,8 @@
public void stopAppSwitches() throws RemoteException;
public void resumeAppSwitches() throws RemoteException;
- public void killApplicationWithAppId(String pkg, int appid) throws RemoteException;
+ public void killApplicationWithAppId(String pkg, int appid, String reason)
+ throws RemoteException;
public void closeSystemDialogs(String reason) throws RemoteException;
@@ -290,6 +301,10 @@
public void finishHeavyWeightApp() throws RemoteException;
+ public void convertFromTranslucent(IBinder token) throws RemoteException;
+ public void convertToTranslucent(IBinder token) throws RemoteException;
+ public void notifyActivityDrawn(IBinder token) throws RemoteException;
+
public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
public boolean isImmersive(IBinder token) throws RemoteException;
public boolean isTopActivityImmersive() throws RemoteException;
@@ -379,6 +394,8 @@
public void hang(IBinder who, boolean allowRestart) throws RemoteException;
+ public void reportActivityFullyDrawn(IBinder token) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -395,10 +412,12 @@
info = _info;
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
info.writeToParcel(dest, 0);
if (provider != null) {
@@ -412,10 +431,12 @@
public static final Parcelable.Creator<ContentProviderHolder> CREATOR
= new Parcelable.Creator<ContentProviderHolder>() {
+ @Override
public ContentProviderHolder createFromParcel(Parcel source) {
return new ContentProviderHolder(source);
}
+ @Override
public ContentProviderHolder[] newArray(int size) {
return new ContentProviderHolder[size];
}
@@ -441,10 +462,12 @@
public WaitResult() {
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
dest.writeInt(timeout ? 1 : 0);
@@ -455,10 +478,12 @@
public static final Parcelable.Creator<WaitResult> CREATOR
= new Parcelable.Creator<WaitResult>() {
+ @Override
public WaitResult createFromParcel(Parcel source) {
return new WaitResult(source);
}
+ @Override
public WaitResult[] newArray(int size) {
return new WaitResult[size];
}
@@ -471,7 +496,7 @@
thisTime = source.readLong();
totalTime = source.readLong();
}
- };
+ }
String descriptor = "android.app.IActivityManager";
@@ -641,4 +666,14 @@
int KILL_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+164;
int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+165;
int HANG_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+166;
+ int CREATE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+167;
+ int MOVE_TASK_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+168;
+ int RESIZE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+169;
+ int GET_STACK_BOXES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170;
+ int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171;
+ int GET_STACK_BOX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+172;
+ int CONVERT_FROM_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173;
+ int CONVERT_TO_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174;
+ int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175;
+ int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 3189b31a..286566d 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -126,13 +126,15 @@
void setCoreSettings(Bundle coreSettings) throws RemoteException;
void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException;
void scheduleTrimMemory(int level) throws RemoteException;
- Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean all,
- String[] args) throws RemoteException;
+ Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean dumpInfo,
+ boolean dumpDalvik, String[] args) throws RemoteException;
void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException;
void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException;
void unstableProviderDied(IBinder provider) throws RemoteException;
void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType)
throws RemoteException;
+ void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
+ throws RemoteException;
String descriptor = "android.app.IApplicationThread";
@@ -183,4 +185,5 @@
int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
int REQUEST_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
+ int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 2224490..05d3a47 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -40,7 +40,7 @@
import android.os.UserHandle;
import android.util.AndroidRuntimeException;
import android.util.Slog;
-import android.view.CompatibilityInfoHolder;
+import android.view.DisplayAdjustments;
import android.view.Display;
import java.io.File;
@@ -84,7 +84,7 @@
private final ClassLoader mBaseClassLoader;
private final boolean mSecurityViolation;
private final boolean mIncludeCode;
- public final CompatibilityInfoHolder mCompatibilityInfo = new CompatibilityInfoHolder();
+ private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
Resources mResources;
private ClassLoader mClassLoader;
private Application mApplication;
@@ -132,17 +132,17 @@
mBaseClassLoader = baseLoader;
mSecurityViolation = securityViolation;
mIncludeCode = includeCode;
- mCompatibilityInfo.set(compatInfo);
+ mDisplayAdjustments.setCompatibilityInfo(compatInfo);
if (mAppDir == null) {
if (ActivityThread.mSystemContext == null) {
ActivityThread.mSystemContext =
ContextImpl.createSystemContext(mainThread);
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
ActivityThread.mSystemContext.getResources().updateConfiguration(
- mainThread.getConfiguration(),
- mainThread.getDisplayMetricsLocked(
- Display.DEFAULT_DISPLAY, compatInfo),
- compatInfo);
+ resourcesManager.getConfiguration(),
+ resourcesManager.getDisplayMetricsLocked(
+ Display.DEFAULT_DISPLAY, mDisplayAdjustments), compatInfo);
//Slog.i(TAG, "Created system resources "
// + mSystemContext.getResources() + ": "
// + mSystemContext.getResources().getConfiguration());
@@ -169,7 +169,7 @@
mIncludeCode = true;
mClassLoader = systemContext.getClassLoader();
mResources = systemContext.getResources();
- mCompatibilityInfo.set(compatInfo);
+ mDisplayAdjustments.setCompatibilityInfo(compatInfo);
}
public String getPackageName() {
@@ -184,6 +184,14 @@
return mSecurityViolation;
}
+ public CompatibilityInfo getCompatibilityInfo() {
+ return mDisplayAdjustments.getCompatibilityInfo();
+ }
+
+ public void setCompatibilityInfo(CompatibilityInfo compatInfo) {
+ mDisplayAdjustments.setCompatibilityInfo(compatInfo);
+ }
+
/**
* Gets the array of shared libraries that are listed as
* used by the given package.
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 267555a..b13b24a 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -204,13 +204,13 @@
// These are the currently active loaders. A loader is here
// from the time its load is started until it has been explicitly
// stopped or restarted by the application.
- final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>();
+ final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(0);
// These are previously run loaders. This list is maintained internally
// to avoid destroying a loader while an application is still using it.
// It allows an application to restart a loader, but continue using its
// previously run loader until the new loader's data is available.
- final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>();
+ final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(0);
final String mWho;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b66e95b..bffbb51 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -432,51 +432,32 @@
/**
* Additional semantic data to be carried around with this Notification.
- * @hide
*/
public Bundle extras = new Bundle();
// extras keys for Builder inputs
- /** @hide */
public static final String EXTRA_TITLE = "android.title";
- /** @hide */
public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
- /** @hide */
public static final String EXTRA_TEXT = "android.text";
- /** @hide */
public static final String EXTRA_SUB_TEXT = "android.subText";
- /** @hide */
public static final String EXTRA_INFO_TEXT = "android.infoText";
- /** @hide */
public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
- /** @hide */
public static final String EXTRA_SMALL_ICON = "android.icon";
- /** @hide */
public static final String EXTRA_LARGE_ICON = "android.largeIcon";
- /** @hide */
public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
- /** @hide */
public static final String EXTRA_PROGRESS = "android.progress";
- /** @hide */
public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
- /** @hide */
public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
- /** @hide */
public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
- /** @hide */
public static final String EXTRA_SHOW_WHEN = "android.showWhen";
- /** @hide from BigPictureStyle */
public static final String EXTRA_PICTURE = "android.picture";
- /** @hide from InboxStyle */
public static final String EXTRA_TEXT_LINES = "android.textLines";
// extras keys for other interesting pieces of information
- /** @hide */
public static final String EXTRA_PEOPLE = "android.people";
/**
* Structure to encapsulate an "action", including title and icon, that can be attached to a Notification.
- * @hide
*/
public static class Action implements Parcelable {
public int icon;
@@ -530,9 +511,6 @@
};
}
- /**
- * @hide
- */
public Action[] actions;
/**
@@ -1450,7 +1428,6 @@
* called.
*
* @see Notification#extras
- * @hide
*/
public Builder setExtras(Bundle bag) {
mExtras = bag;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
new file mode 100644
index 0000000..e9693dd
--- /dev/null
+++ b/core/java/android/app/ResourcesManager.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static android.app.ActivityThread.DEBUG_CONFIGURATION;
+
+import android.app.ApplicationPackageManager;
+import android.content.pm.ActivityInfo;
+import android.content.res.AssetManager;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.ResourcesKey;
+import android.hardware.display.DisplayManagerGlobal;
+import android.os.IBinder;
+import android.util.ArrayMap;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+/** @hide */
+public class ResourcesManager {
+ static final String TAG = "ResourcesManager";
+ static final boolean DEBUG_CACHE = false;
+ static final boolean DEBUG_STATS = true;
+
+ private static ResourcesManager sResourcesManager;
+ final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources
+ = new HashMap<ResourcesKey, WeakReference<Resources> >();
+
+ final ArrayMap<DisplayAdjustments, DisplayMetrics> mDefaultDisplayMetrics
+ = new ArrayMap<DisplayAdjustments, DisplayMetrics>();
+
+ CompatibilityInfo mResCompatibilityInfo;
+
+ Configuration mResConfiguration;
+ final Configuration mTmpConfig = new Configuration();
+
+ public static ResourcesManager getInstance() {
+ synchronized (ResourcesManager.class) {
+ if (sResourcesManager == null) {
+ sResourcesManager = new ResourcesManager();
+ }
+ return sResourcesManager;
+ }
+ }
+
+ public Configuration getConfiguration() {
+ return mResConfiguration;
+ }
+
+ public void flushDisplayMetricsLocked() {
+ mDefaultDisplayMetrics.clear();
+ }
+
+ public DisplayMetrics getDisplayMetricsLocked(int displayId) {
+ return getDisplayMetricsLocked(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ }
+
+ public DisplayMetrics getDisplayMetricsLocked(int displayId, DisplayAdjustments daj) {
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(daj) : null;
+ if (dm != null) {
+ return dm;
+ }
+ dm = new DisplayMetrics();
+
+ DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
+ if (displayManager == null) {
+ // may be null early in system startup
+ dm.setToDefaults();
+ return dm;
+ }
+
+ if (isDefaultDisplay) {
+ mDefaultDisplayMetrics.put(daj, dm);
+ }
+
+ Display d = displayManager.getCompatibleDisplay(displayId, daj);
+ if (d != null) {
+ d.getMetrics(dm);
+ } else {
+ // Display no longer exists
+ // FIXME: This would not be a problem if we kept the Display object around
+ // instead of using the raw display id everywhere. The Display object caches
+ // its information even after the display has been removed.
+ dm.setToDefaults();
+ }
+ //Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h="
+ // + metrics.heightPixels + " den=" + metrics.density
+ // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi);
+ return dm;
+ }
+
+ final void applyNonDefaultDisplayMetricsToConfigurationLocked(
+ DisplayMetrics dm, Configuration config) {
+ config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
+ config.densityDpi = dm.densityDpi;
+ config.screenWidthDp = (int)(dm.widthPixels / dm.density);
+ config.screenHeightDp = (int)(dm.heightPixels / dm.density);
+ int sl = Configuration.resetScreenLayout(config.screenLayout);
+ if (dm.widthPixels > dm.heightPixels) {
+ config.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ config.screenLayout = Configuration.reduceScreenLayout(sl,
+ config.screenWidthDp, config.screenHeightDp);
+ } else {
+ config.orientation = Configuration.ORIENTATION_PORTRAIT;
+ config.screenLayout = Configuration.reduceScreenLayout(sl,
+ config.screenHeightDp, config.screenWidthDp);
+ }
+ config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
+ config.compatScreenWidthDp = config.screenWidthDp;
+ config.compatScreenHeightDp = config.screenHeightDp;
+ config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
+ }
+
+ public boolean applyCompatConfiguration(int displayDensity,
+ Configuration compatConfiguration) {
+ if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
+ mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Creates the top level Resources for applications with the given compatibility info.
+ *
+ * @param resDir the resource directory.
+ * @param compatInfo the compability info. Must not be null.
+ * @param token the application token for determining stack bounds.
+ */
+ public Resources getTopLevelResources(String resDir, int displayId,
+ Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
+ final float scale = compatInfo.applicationScale;
+ ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,
+ token);
+ Resources r;
+ synchronized (this) {
+ // Resources is app scale dependent.
+ if (false) {
+ Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
+ }
+ WeakReference<Resources> wr = mActiveResources.get(key);
+ r = wr != null ? wr.get() : null;
+ //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
+ if (r != null && r.getAssets().isUpToDate()) {
+ if (false) {
+ Slog.w(TAG, "Returning cached resources " + r + " " + resDir
+ + ": appScale=" + r.getCompatibilityInfo().applicationScale);
+ }
+ return r;
+ }
+ }
+
+ //if (r != null) {
+ // Slog.w(TAG, "Throwing away out-of-date resources!!!! "
+ // + r + " " + resDir);
+ //}
+
+ AssetManager assets = new AssetManager();
+ if (assets.addAssetPath(resDir) == 0) {
+ return null;
+ }
+
+ //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
+ DisplayMetrics dm = getDisplayMetricsLocked(displayId);
+ Configuration config;
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ final boolean hasOverrideConfig = key.hasOverrideConfiguration();
+ if (!isDefaultDisplay || hasOverrideConfig) {
+ config = new Configuration(getConfiguration());
+ if (!isDefaultDisplay) {
+ applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
+ }
+ if (hasOverrideConfig) {
+ config.updateFrom(key.mOverrideConfiguration);
+ }
+ } else {
+ config = getConfiguration();
+ }
+ r = new Resources(assets, dm, config, compatInfo, token);
+ if (false) {
+ Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ + r.getConfiguration() + " appScale="
+ + r.getCompatibilityInfo().applicationScale);
+ }
+
+ synchronized (this) {
+ WeakReference<Resources> wr = mActiveResources.get(key);
+ Resources existing = wr != null ? wr.get() : null;
+ if (existing != null && existing.getAssets().isUpToDate()) {
+ // Someone else already created the resources while we were
+ // unlocked; go ahead and use theirs.
+ r.getAssets().close();
+ return existing;
+ }
+
+ // XXX need to remove entries when weak references go away
+ mActiveResources.put(key, new WeakReference<Resources>(r));
+ return r;
+ }
+ }
+
+ public final boolean applyConfigurationToResourcesLocked(Configuration config,
+ CompatibilityInfo compat) {
+ if (mResConfiguration == null) {
+ mResConfiguration = new Configuration();
+ }
+ if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+ if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+ + mResConfiguration.seq + ", newSeq=" + config.seq);
+ return false;
+ }
+ int changes = mResConfiguration.updateFrom(config);
+ flushDisplayMetricsLocked();
+ DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(Display.DEFAULT_DISPLAY);
+
+ if (compat != null && (mResCompatibilityInfo == null ||
+ !mResCompatibilityInfo.equals(compat))) {
+ mResCompatibilityInfo = compat;
+ changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+ | ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+ }
+
+ // set it for java, this also affects newly created Resources
+ if (config.locale != null) {
+ Locale.setDefault(config.locale);
+ }
+
+ Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
+
+ ApplicationPackageManager.configurationChanged();
+ //Slog.i(TAG, "Configuration changed in " + currentPackageName());
+
+ Configuration tmpConfig = null;
+
+ Iterator<Map.Entry<ResourcesKey, WeakReference<Resources>>> it =
+ mActiveResources.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<ResourcesKey, WeakReference<Resources>> entry = it.next();
+ Resources r = entry.getValue().get();
+ if (r != null) {
+ if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ + r + " config to: " + config);
+ int displayId = entry.getKey().mDisplayId;
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ DisplayMetrics dm = defaultDisplayMetrics;
+ ResourcesKey resourcesKey = entry.getKey();
+ final boolean hasOverrideConfiguration = resourcesKey.hasOverrideConfiguration();
+ if (!isDefaultDisplay || hasOverrideConfiguration) {
+ if (tmpConfig == null) {
+ tmpConfig = new Configuration();
+ }
+ tmpConfig.setTo(config);
+ if (!isDefaultDisplay) {
+ dm = getDisplayMetricsLocked(displayId);
+ applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
+ }
+ if (hasOverrideConfiguration) {
+ tmpConfig.updateFrom(resourcesKey.mOverrideConfiguration);
+ }
+ r.updateConfiguration(tmpConfig, dm, compat);
+ } else {
+ r.updateConfiguration(config, dm, compat);
+ }
+ //Slog.i(TAG, "Updated app resources " + v.getKey()
+ // + " " + r + ": " + r.getConfiguration());
+ } else {
+ //Slog.i(TAG, "Removing old resources " + v.getKey());
+ it.remove();
+ }
+ }
+
+ return changes != 0;
+ }
+
+}
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 8aae45a..f104d71 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -198,7 +198,6 @@
* @return a appWidgetId
*/
public int allocateAppWidgetId() {
-
try {
if (mPackageName == null) {
mPackageName = mContext.getPackageName();
@@ -211,20 +210,17 @@
}
/**
- * Get a appWidgetId for a host in the calling process.
+ * Get a appWidgetId for a host in the given package.
*
* @return a appWidgetId
* @hide
*/
- public static int allocateAppWidgetIdForSystem(int hostId, int userId) {
+ public static int allocateAppWidgetIdForPackage(int hostId, int userId, String packageName) {
checkCallerIsSystem();
try {
if (sService == null) {
bindService();
}
- Context systemContext =
- (Context) ActivityThread.currentActivityThread().getSystemContext();
- String packageName = systemContext.getPackageName();
return sService.allocateAppWidgetId(packageName, hostId, userId);
} catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 79bb476..72ecda1 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -36,6 +36,7 @@
import java.util.HashSet;
import java.util.HashMap;
import java.util.LinkedList;
+import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -433,7 +434,7 @@
if (address == null || address.length != 6) {
throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
}
- return new BluetoothDevice(String.format("%02X:%02X:%02X:%02X:%02X:%02X",
+ return new BluetoothDevice(String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
address[0], address[1], address[2], address[3], address[4], address[5]));
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 0752df8..df3ec1a 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -167,7 +167,7 @@
try {
mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
synchronized(mStateLock) {
@@ -294,7 +294,7 @@
try {
mCallback.onServicesDiscovered(BluetoothGatt.this, status);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -341,7 +341,7 @@
try {
mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -387,7 +387,7 @@
try {
mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -418,7 +418,7 @@
try {
mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -467,7 +467,7 @@
try {
mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -515,7 +515,7 @@
try {
mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -532,7 +532,7 @@
try {
mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -549,7 +549,7 @@
try {
mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
};
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 78d536b..58ee54f 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -108,7 +108,7 @@
connected ? BluetoothProfile.STATE_CONNECTED :
BluetoothProfile.STATE_DISCONNECTED);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -128,7 +128,7 @@
try {
mCallback.onServiceAdded((int)status, service);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -154,7 +154,7 @@
try {
mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -186,7 +186,7 @@
try {
mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -214,7 +214,7 @@
mCallback.onCharacteristicWriteRequest(device, transId, characteristic,
isPrep, needRsp, offset, value);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -250,7 +250,7 @@
mCallback.onDescriptorWriteRequest(device, transId, descriptor,
isPrep, needRsp, offset, value);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
@@ -270,7 +270,7 @@
try {
mCallback.onExecuteWrite(device, transId, execWrite);
} catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ Log.w(TAG, "Unhandled exception in callback", ex);
}
}
};
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 793d798..963e9fc 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -815,27 +815,6 @@
}
/**
- * Notify Headset of phone roam state change.
- * This is a backdoor for phone app to call BluetoothHeadset since
- * there is currently not a good way to get roaming state change outside
- * of phone app.
- *
- * @hide
- */
- public void roamChanged(boolean roaming) {
- if (mService != null && isEnabled()) {
- try {
- mService.roamChanged(roaming);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
-
- /**
* Send Headset of CLCC response
*
* @hide
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index a19341c..d10eaea 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -31,6 +31,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
+import java.util.Locale;
import java.util.UUID;
import android.net.LocalSocket;
import java.nio.ByteOrder;
@@ -473,7 +474,7 @@
return mPort;
}
private String convertAddr(final byte[] addr) {
- return String.format("%02X:%02X:%02X:%02X:%02X:%02X",
+ return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]);
}
private String waitSocketSignal(InputStream is) throws IOException {
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
old mode 100644
new mode 100755
index fc7627a..285eea7
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -50,7 +50,6 @@
boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device);
boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device);
void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type);
- void roamChanged(boolean roam);
void clccResponse(int index, int direction, int status, int mode, boolean mpty,
String number, int type);
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index fefd343..f090e07 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -72,7 +72,13 @@
*/
@Deprecated
public static final String SYNC_EXTRAS_ACCOUNT = "account";
+
+ /**
+ * If this extra is set to true, the sync request will be scheduled
+ * at the front of the sync request queue and without any delay
+ */
public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
+
/**
* @deprecated instead use
* {@link #SYNC_EXTRAS_MANUAL}
@@ -104,8 +110,25 @@
*/
public static final String SYNC_EXTRAS_MANUAL = "force";
+ /**
+ * Indicates that this sync is intended to only upload local changes to the server.
+ * For example, this will be set to true if the sync is initiated by a call to
+ * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
+ */
public static final String SYNC_EXTRAS_UPLOAD = "upload";
+
+ /**
+ * Indicates that the sync adapter should proceed with the delete operations,
+ * even if it determines that there are too many.
+ * See {@link SyncResult#tooManyDeletions}
+ */
public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
+
+ /**
+ * Indicates that the sync adapter should not proceed with the delete operations,
+ * if it determines that there are too many.
+ * See {@link SyncResult#tooManyDeletions}
+ */
public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
/**
@@ -220,6 +243,7 @@
// Always log queries which take 500ms+; shorter queries are
// sampled accordingly.
+ private static final boolean ENABLE_CONTENT_SAMPLE = false;
private static final int SLOW_THRESHOLD_MILLIS = 500;
private final Random mRandom = new Random(); // guarded by itself
@@ -1832,6 +1856,7 @@
private void maybeLogQueryToEventLog(long durationMillis,
Uri uri, String[] projection,
String selection, String sortOrder) {
+ if (!ENABLE_CONTENT_SAMPLE) return;
int samplePercent = samplePercentForDuration(durationMillis);
if (samplePercent < 100) {
synchronized (mRandom) {
@@ -1871,6 +1896,7 @@
private void maybeLogUpdateToEventLog(
long durationMillis, Uri uri, String operation, String selection) {
+ if (!ENABLE_CONTENT_SAMPLE) return;
int samplePercent = samplePercentForDuration(durationMillis);
if (samplePercent < 100) {
synchronized (mRandom) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5bd28b9..d8bf6ba 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -35,7 +35,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AttributeSet;
-import android.view.CompatibilityInfoHolder;
+import android.view.DisplayAdjustments;
import android.view.Display;
import android.view.WindowManager;
@@ -45,7 +45,6 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.List;
/**
* Interface to global information about an application environment. This is
@@ -2284,6 +2283,25 @@
public static final String APP_OPS_SERVICE = "appops";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.hardware.photography.CameraManager} for interacting with
+ * camera devices.
+ *
+ * @see #getSystemService
+ * @see android.hardware.camera.CameraManager
+ */
+ public static final String CAMERA_SERVICE = "camera";
+
+ /**
+ * {@link android.print.PrintManager} for printing and managing
+ * printers and print taks.
+ *
+ * @see #getSystemService
+ * @see android.print.PrintManager
+ */
+ public static final String PRINT_SERVICE = "print";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
@@ -2434,7 +2452,7 @@
* Remove all permissions to access a particular content provider Uri
* that were previously added with {@link #grantUriPermission}. The given
* Uri will match all previously granted Uris that are the same or a
- * sub-path of the given Uri. That is, revoking "content://foo/one" will
+ * sub-path of the given Uri. That is, revoking "content://foo/target" will
* revoke both "content://foo/target" and "content://foo/target/sub", but not
* "content://foo".
*
@@ -2742,15 +2760,15 @@
public abstract Context createDisplayContext(Display display);
/**
- * Gets the compatibility info holder for this context. This information
- * is provided on a per-application basis and is used to simulate lower density
- * display metrics for legacy applications.
+ * Gets the display adjustments holder for this context. This information
+ * is provided on a per-application or activity basis and is used to simulate lower density
+ * display metrics for legacy applications and restricted screen sizes.
*
* @param displayId The display id for which to get compatibility info.
* @return The compatibility info holder, or null if not required by the application.
* @hide
*/
- public abstract CompatibilityInfoHolder getCompatibilityInfo(int displayId);
+ public abstract DisplayAdjustments getDisplayAdjustments(int displayId);
/**
* Indicates whether this Context is restricted.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 2f1bf8c..606a1f4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -35,7 +35,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.view.CompatibilityInfoHolder;
+import android.view.DisplayAdjustments;
import android.view.Display;
import java.io.File;
@@ -646,7 +646,7 @@
/** @hide */
@Override
- public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
- return mBase.getCompatibilityInfo(displayId);
+ public DisplayAdjustments getDisplayAdjustments(int displayId) {
+ return mBase.getDisplayAdjustments(displayId);
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fc95728..bda7112 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1165,13 +1165,12 @@
* additional optional contextual information about where the user was when they requested
* the voice assist.
* Output: nothing.
- * @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
/**
- * An optional field on {@link #ACTION_ASSIST}
+ * An optional field on {@link #ACTION_ASSIST} and {@link #ACTION_VOICE_ASSIST}
* containing the name of the current foreground application package at the time
* the assist was invoked.
*/
@@ -1179,7 +1178,7 @@
= "android.intent.extra.ASSIST_PACKAGE";
/**
- * An optional field on {@link #ACTION_ASSIST}
+ * An optional field on {@link #ACTION_ASSIST} and {@link #ACTION_VOICE_ASSIST}
* containing additional contextual information supplied by the current
* foreground app at the time of the assist request. This is a {@link Bundle} of
* additional data.
@@ -2441,6 +2440,18 @@
"android.intent.action.GET_RESTRICTION_ENTRIES";
/**
+ * Activity to challenge the user for a PIN that was configured when setting up
+ * restrictions. Launch the activity using
+ * {@link android.app.Activity#startActivityForResult(Intent, int)} and check if the
+ * result is {@link android.app.Activity#RESULT_OK} for a successful response to the
+ * challenge.<p/>
+ * Before launching this activity, make sure that there is a PIN in effect, by calling
+ * {@link android.os.UserManager#hasRestrictionsPin()}.
+ */
+ public static final String ACTION_RESTRICTIONS_PIN_CHALLENGE =
+ "android.intent.action.RESTRICTIONS_PIN_CHALLENGE";
+
+ /**
* Sent the first time a user is starting, to allow system apps to
* perform one time initialization. (This will not be seen by third
* party applications because a newly initialized user does not have any
@@ -2599,6 +2610,46 @@
*/
public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
+ /**
+ * Activity Action: Allow the user to select and open one or more existing
+ * documents. Both read and write access to the documents will be granted
+ * until explicitly revoked by the user.
+ * <p>
+ * Callers can restrict selection to a specific kind of data, such as
+ * photos, by setting one or more MIME types in {@link #EXTRA_MIME_TYPES}.
+ * <p>
+ * If the caller can handle multiple returned items (the user performing
+ * multiple selection), then it can specify {@link #EXTRA_ALLOW_MULTIPLE} to
+ * indicate this.
+ * <p>
+ * All returned URIs can be opened as a stream with
+ * {@link ContentResolver#openInputStream(Uri)}.
+ * <p>
+ * Output: The URI of the item that was picked. This must be a content: URI
+ * so that any receiver can access it.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
+
+ /**
+ * Activity Action: Allow the user to create a new document. Both read and
+ * write access to the document will be granted until explicitly revoked by
+ * the user.
+ * <p>
+ * Callers can provide a hint document name by setting {@link #EXTRA_TITLE},
+ * but the user may change this value before creating the file. Callers can
+ * optionally hint at the MIME type being created by setting
+ * {@link #setType(String)}.
+ * <p>
+ * All returned URIs can be opened as a stream with
+ * {@link ContentResolver#openOutputStream(Uri)}.
+ * <p>
+ * Output: The URI of the item that was created. This must be a content: URI
+ * so that any receiver can access it.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
@@ -3202,6 +3253,13 @@
public static final String EXTRA_RESTRICTIONS_INTENT =
"android.intent.extra.restrictions_intent";
+ /**
+ * Extra used to communicate set of acceptable MIME types for
+ * {@link #ACTION_GET_CONTENT} or {@link #ACTION_OPEN_DOCUMENT}. The type of the
+ * extra is <code>ArrayList<String></code>.
+ */
+ public static final String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
@@ -3251,6 +3309,15 @@
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;
/**
+ * When combined with {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or
+ * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the grant will be remembered
+ * until explicitly revoked with
+ * {@link Context#revokeUriPermission(Uri, int)}. These grants persist
+ * across device reboots.
+ */
+ public static final int FLAG_PERSIST_GRANT_URI_PERMISSION = 0x00000040;
+
+ /**
* If set, the new activity is not kept in the history stack. As soon as
* the user navigates away from it, the activity is finished. This may also
* be set with the {@link android.R.styleable#AndroidManifestActivity_noHistory
@@ -7039,7 +7106,8 @@
// and flags to ourselves to grant.
setClipData(target.getClipData());
addFlags(target.getFlags()
- & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION));
+ & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION
+ | FLAG_PERSIST_GRANT_URI_PERMISSION));
return true;
} else {
return false;
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 5e65b59..36b1abc 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -50,8 +50,8 @@
* <em>action</em>, <em>data</em>, and <em>categories</em>. For each of these
* characteristics you can provide
* multiple possible matching values (via {@link #addAction},
- * {@link #addDataType}, {@link #addDataScheme} {@link #addDataAuthority},
- * {@link #addDataPath}, and {@link #addCategory}, respectively).
+ * {@link #addDataType}, {@link #addDataScheme}, {@link #addDataSchemeSpecificPart},
+ * {@link #addDataAuthority}, {@link #addDataPath}, and {@link #addCategory}, respectively).
* For actions, the field
* will not be tested if no values have been given (treating it as a wildcard);
* if no data characteristics are specified, however, then the filter will
@@ -106,8 +106,15 @@
* formal RFC schemes!</em> You should thus always use lower case letters
* for your schemes.
*
+ * <p><strong>Data Scheme Specific Part</strong> matches if any of the given values match
+ * the Intent's data scheme specific part <em>and</em> one of the data schemes in the filter
+ * has matched the Intent, <em>or</em> no scheme specific parts were supplied in the filter.
+ * The Intent scheme specific part is determined by calling
+ * {@link Intent#getData} and {@link android.net.Uri#getSchemeSpecificPart} on that URI.
+ * <em>Note that scheme specific part matching is <b>case sensitive</b>.</em>
+ *
* <p><strong>Data Authority</strong> matches if any of the given values match
- * the Intent's data authority <em>and</em> one of the data scheme's in the filter
+ * the Intent's data authority <em>and</em> one of the data schemes in the filter
* has matched the Intent, <em>or</em> no authories were supplied in the filter.
* The Intent authority is determined by calling
* {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
@@ -135,6 +142,7 @@
private static final String PORT_STR = "port";
private static final String HOST_STR = "host";
private static final String AUTH_STR = "auth";
+ private static final String SSP_STR = "ssp";
private static final String SCHEME_STR = "scheme";
private static final String TYPE_STR = "type";
private static final String CAT_STR = "cat";
@@ -164,8 +172,8 @@
/**
* The part of a match constant that describes the category of match
* that occurred. May be either {@link #MATCH_CATEGORY_EMPTY},
- * {@link #MATCH_CATEGORY_SCHEME}, {@link #MATCH_CATEGORY_HOST},
- * {@link #MATCH_CATEGORY_PORT},
+ * {@link #MATCH_CATEGORY_SCHEME}, {@link #MATCH_CATEGORY_SCHEME_SPECIFIC_PART},
+ * {@link #MATCH_CATEGORY_HOST}, {@link #MATCH_CATEGORY_PORT},
* {@link #MATCH_CATEGORY_PATH}, or {@link #MATCH_CATEGORY_TYPE}. Higher
* values indicate a better match.
*/
@@ -210,6 +218,11 @@
*/
public static final int MATCH_CATEGORY_PATH = 0x0500000;
/**
+ * The filter matched an intent with the same data URI scheme and
+ * scheme specific part.
+ */
+ public static final int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 0x0580000;
+ /**
* The filter matched an intent with the same data MIME type.
*/
public static final int MATCH_CATEGORY_TYPE = 0x0600000;
@@ -236,6 +249,7 @@
private final ArrayList<String> mActions;
private ArrayList<String> mCategories = null;
private ArrayList<String> mDataSchemes = null;
+ private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
private ArrayList<AuthorityEntry> mDataAuthorities = null;
private ArrayList<PatternMatcher> mDataPaths = null;
private ArrayList<String> mDataTypes = null;
@@ -395,6 +409,9 @@
if (o.mDataSchemes != null) {
mDataSchemes = new ArrayList<String>(o.mDataSchemes);
}
+ if (o.mDataSchemeSpecificParts != null) {
+ mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(o.mDataSchemeSpecificParts);
+ }
if (o.mDataAuthorities != null) {
mDataAuthorities = new ArrayList<AuthorityEntry>(o.mDataAuthorities);
}
@@ -699,6 +716,75 @@
};
/**
+ * Add a new Intent data "scheme specific part" to match against. The filter must
+ * include one or more schemes (via {@link #addDataScheme}) for the
+ * scheme specific part to be considered. If any scheme specific parts are
+ * included in the filter, then an Intent's data must match one of
+ * them. If no scheme specific parts are included, then only the scheme must match.
+ *
+ * @param ssp Either a raw string that must exactly match the scheme specific part
+ * path, or a simple pattern, depending on <var>type</var>.
+ * @param type Determines how <var>ssp</var> will be compared to
+ * determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
+ * {@link PatternMatcher#PATTERN_PREFIX}, or
+ * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
+ *
+ * @see #matchData
+ * @see #addDataScheme
+ */
+ public final void addDataSchemeSpecificPart(String ssp, int type) {
+ if (mDataSchemeSpecificParts == null) {
+ mDataSchemeSpecificParts = new ArrayList<PatternMatcher>();
+ }
+ mDataSchemeSpecificParts.add(new PatternMatcher(ssp, type));
+ }
+
+ /**
+ * Return the number of data scheme specific parts in the filter.
+ */
+ public final int countDataSchemeSpecificParts() {
+ return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.size() : 0;
+ }
+
+ /**
+ * Return a data scheme specific part in the filter.
+ */
+ public final PatternMatcher getDataSchemeSpecificPart(int index) {
+ return mDataSchemeSpecificParts.get(index);
+ }
+
+ /**
+ * Is the given data scheme specific part included in the filter? Note that if the
+ * filter does not include any scheme specific parts, false will <em>always</em> be
+ * returned.
+ *
+ * @param data The scheme specific part that is being looked for.
+ *
+ * @return Returns true if the data string matches a scheme specific part listed in the
+ * filter.
+ */
+ public final boolean hasDataSchemeSpecificPart(String data) {
+ if (mDataSchemeSpecificParts == null) {
+ return false;
+ }
+ final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
+ for (int i = 0; i < numDataSchemeSpecificParts; i++) {
+ final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
+ if (pe.match(data)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return an iterator over the filter's data scheme specific parts.
+ */
+ public final Iterator<PatternMatcher> schemeSpecificPartsIterator() {
+ return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.iterator() : null;
+ }
+
+ /**
* Add a new Intent data authority to match against. The filter must
* include one or more schemes (via {@link #addDataScheme}) for the
* authority to be considered. If any authorities are
@@ -900,8 +986,6 @@
public final int matchData(String type, String scheme, Uri data) {
final ArrayList<String> types = mDataTypes;
final ArrayList<String> schemes = mDataSchemes;
- final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
- final ArrayList<PatternMatcher> paths = mDataPaths;
int match = MATCH_CATEGORY_EMPTY;
@@ -917,20 +1001,34 @@
return NO_MATCH_DATA;
}
- if (authorities != null) {
- int authMatch = matchDataAuthority(data);
- if (authMatch >= 0) {
- if (paths == null) {
- match = authMatch;
- } else if (hasDataPath(data.getPath())) {
- match = MATCH_CATEGORY_PATH;
+ final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;
+ if (schemeSpecificParts != null) {
+ match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart())
+ ? MATCH_CATEGORY_SCHEME_SPECIFIC_PART : NO_MATCH_DATA;
+ }
+ if (match != MATCH_CATEGORY_SCHEME_SPECIFIC_PART) {
+ // If there isn't any matching ssp, we need to match an authority.
+ final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
+ if (authorities != null) {
+ int authMatch = matchDataAuthority(data);
+ if (authMatch >= 0) {
+ final ArrayList<PatternMatcher> paths = mDataPaths;
+ if (paths == null) {
+ match = authMatch;
+ } else if (hasDataPath(data.getPath())) {
+ match = MATCH_CATEGORY_PATH;
+ } else {
+ return NO_MATCH_DATA;
+ }
} else {
return NO_MATCH_DATA;
}
- } else {
- return NO_MATCH_DATA;
}
}
+ // If neither an ssp nor an authority matched, we're done.
+ if (match == NO_MATCH_DATA) {
+ return NO_MATCH_DATA;
+ }
} else {
// Special case: match either an Intent with no data URI,
// or with a scheme: URI. This is to give a convenience for
@@ -1173,6 +1271,23 @@
serializer.attribute(null, NAME_STR, mDataSchemes.get(i));
serializer.endTag(null, SCHEME_STR);
}
+ N = countDataSchemeSpecificParts();
+ for (int i=0; i<N; i++) {
+ serializer.startTag(null, SSP_STR);
+ PatternMatcher pe = mDataSchemeSpecificParts.get(i);
+ switch (pe.getType()) {
+ case PatternMatcher.PATTERN_LITERAL:
+ serializer.attribute(null, LITERAL_STR, pe.getPath());
+ break;
+ case PatternMatcher.PATTERN_PREFIX:
+ serializer.attribute(null, PREFIX_STR, pe.getPath());
+ break;
+ case PatternMatcher.PATTERN_SIMPLE_GLOB:
+ serializer.attribute(null, SGLOB_STR, pe.getPath());
+ break;
+ }
+ serializer.endTag(null, SSP_STR);
+ }
N = countDataAuthorities();
for (int i=0; i<N; i++) {
serializer.startTag(null, AUTH_STR);
@@ -1238,6 +1353,15 @@
if (name != null) {
addDataScheme(name);
}
+ } else if (tagName.equals(SSP_STR)) {
+ String ssp = parser.getAttributeValue(null, LITERAL_STR);
+ if (ssp != null) {
+ addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_LITERAL);
+ } else if ((ssp=parser.getAttributeValue(null, PREFIX_STR)) != null) {
+ addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_PREFIX);
+ } else if ((ssp=parser.getAttributeValue(null, SGLOB_STR)) != null) {
+ addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SIMPLE_GLOB);
+ }
} else if (tagName.equals(AUTH_STR)) {
String host = parser.getAttributeValue(null, HOST_STR);
String port = parser.getAttributeValue(null, PORT_STR);
@@ -1289,6 +1413,16 @@
du.println(sb.toString());
}
}
+ if (mDataSchemeSpecificParts != null) {
+ Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
+ while (it.hasNext()) {
+ PatternMatcher pe = it.next();
+ sb.setLength(0);
+ sb.append(prefix); sb.append("Ssp: \"");
+ sb.append(pe); sb.append("\"");
+ du.println(sb.toString());
+ }
+ }
if (mDataAuthorities != null) {
Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
while (it.hasNext()) {
@@ -1363,6 +1497,15 @@
} else {
dest.writeInt(0);
}
+ if (mDataSchemeSpecificParts != null) {
+ final int N = mDataSchemeSpecificParts.size();
+ dest.writeInt(N);
+ for (int i=0; i<N; i++) {
+ mDataSchemeSpecificParts.get(i).writeToParcel(dest, flags);
+ }
+ } else {
+ dest.writeInt(0);
+ }
if (mDataAuthorities != null) {
final int N = mDataAuthorities.size();
dest.writeInt(N);
@@ -1376,7 +1519,7 @@
final int N = mDataPaths.size();
dest.writeInt(N);
for (int i=0; i<N; i++) {
- mDataPaths.get(i).writeToParcel(dest, 0);
+ mDataPaths.get(i).writeToParcel(dest, flags);
}
} else {
dest.writeInt(0);
@@ -1428,14 +1571,21 @@
}
int N = source.readInt();
if (N > 0) {
- mDataAuthorities = new ArrayList<AuthorityEntry>();
+ mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(N);
+ for (int i=0; i<N; i++) {
+ mDataSchemeSpecificParts.add(new PatternMatcher(source));
+ }
+ }
+ N = source.readInt();
+ if (N > 0) {
+ mDataAuthorities = new ArrayList<AuthorityEntry>(N);
for (int i=0; i<N; i++) {
mDataAuthorities.add(new AuthorityEntry(source));
}
}
N = source.readInt();
if (N > 0) {
- mDataPaths = new ArrayList<PatternMatcher>();
+ mDataPaths = new ArrayList<PatternMatcher>(N);
for (int i=0; i<N; i++) {
mDataPaths.add(new PatternMatcher(source));
}
diff --git a/core/java/android/content/SyncResult.java b/core/java/android/content/SyncResult.java
index 8b0afbd..6cb0d02 100644
--- a/core/java/android/content/SyncResult.java
+++ b/core/java/android/content/SyncResult.java
@@ -56,7 +56,7 @@
/**
* Used to indicate that the SyncAdapter experienced a hard error due to an error it
- * received from interacting with the storage later. The SyncManager will record that
+ * received from interacting with the storage layer. The SyncManager will record that
* the sync request failed and it will not reschedule the request.
*/
public boolean databaseError;
diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java
index ff628d9..bb24ccd 100644
--- a/core/java/android/content/SyncStatusInfo.java
+++ b/core/java/android/content/SyncStatusInfo.java
@@ -42,7 +42,10 @@
public long initialFailureTime;
public boolean pending;
public boolean initialize;
- public ArrayList<Long> periodicSyncTimes;
+
+ // Warning: It is up to the external caller to ensure there are
+ // no race conditions when accessing this list
+ private ArrayList<Long> periodicSyncTimes;
private static final String TAG = "Sync";
@@ -126,11 +129,59 @@
}
}
+ public SyncStatusInfo(SyncStatusInfo other) {
+ authorityId = other.authorityId;
+ totalElapsedTime = other.totalElapsedTime;
+ numSyncs = other.numSyncs;
+ numSourcePoll = other.numSourcePoll;
+ numSourceServer = other.numSourceServer;
+ numSourceLocal = other.numSourceLocal;
+ numSourceUser = other.numSourceUser;
+ numSourcePeriodic = other.numSourcePeriodic;
+ lastSuccessTime = other.lastSuccessTime;
+ lastSuccessSource = other.lastSuccessSource;
+ lastFailureTime = other.lastFailureTime;
+ lastFailureSource = other.lastFailureSource;
+ lastFailureMesg = other.lastFailureMesg;
+ initialFailureTime = other.initialFailureTime;
+ pending = other.pending;
+ initialize = other.initialize;
+ if (other.periodicSyncTimes != null) {
+ periodicSyncTimes = new ArrayList<Long>(other.periodicSyncTimes);
+ }
+ }
+
public void setPeriodicSyncTime(int index, long when) {
+ // The list is initialized lazily when scheduling occurs so we need to make sure
+ // we initialize elements < index to zero (zero is ignore for scheduling purposes)
ensurePeriodicSyncTimeSize(index);
periodicSyncTimes.set(index, when);
}
+ public long getPeriodicSyncTime(int index) {
+ if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
+ return periodicSyncTimes.get(index);
+ } else {
+ return 0;
+ }
+ }
+
+ public void removePeriodicSyncTime(int index) {
+ if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
+ periodicSyncTimes.remove(index);
+ }
+ }
+
+ public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
+ public SyncStatusInfo createFromParcel(Parcel in) {
+ return new SyncStatusInfo(in);
+ }
+
+ public SyncStatusInfo[] newArray(int size) {
+ return new SyncStatusInfo[size];
+ }
+ };
+
private void ensurePeriodicSyncTimeSize(int index) {
if (periodicSyncTimes == null) {
periodicSyncTimes = new ArrayList<Long>(0);
@@ -143,26 +194,4 @@
}
}
}
-
- public long getPeriodicSyncTime(int index) {
- if (periodicSyncTimes == null || periodicSyncTimes.size() < (index + 1)) {
- return 0;
- }
- return periodicSyncTimes.get(index);
- }
-
- public void removePeriodicSyncTime(int index) {
- ensurePeriodicSyncTimeSize(index);
- periodicSyncTimes.remove(index);
- }
-
- public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
- public SyncStatusInfo createFromParcel(Parcel in) {
- return new SyncStatusInfo(in);
- }
-
- public SyncStatusInfo[] newArray(int size) {
- return new SyncStatusInfo[size];
- }
- };
}
\ No newline at end of file
diff --git a/core/java/android/content/UndoManager.java b/core/java/android/content/UndoManager.java
new file mode 100644
index 0000000..1c2db47
--- /dev/null
+++ b/core/java/android/content/UndoManager.java
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelableParcel;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Top-level class for managing and interacting with the global undo state for
+ * a document or application. This class supports both undo and redo and has
+ * helpers for merging undoable operations together as they are performed.
+ *
+ * <p>A single undoable operation is represented by {@link UndoOperation} which
+ * apps implement to define their undo/redo behavior. The UndoManager keeps
+ * a stack of undo states; each state can have one or more undo operations
+ * inside of it.</p>
+ *
+ * <p>Updates to the stack must be done inside of a {@link #beginUpdate}/{@link #endUpdate()}
+ * pair. During this time you can add new operations to the stack with
+ * {@link #addOperation}, retrieve and modify existing operations with
+ * {@link #getLastOperation}, control the label shown to the user for this operation
+ * with {@link #setUndoLabel} and {@link #suggestUndoLabel}, etc.</p>
+ *
+ * <p>Every {link UndoOperation} is associated with an {@link UndoOwner}, which identifies
+ * the data it belongs to. The owner is used to indicate how operations are dependent
+ * on each other -- operations with the same owner are dependent on others with the
+ * same owner. For example, you may have a document with multiple embedded objects. If the
+ * document itself and each embedded object use different owners, then you
+ * can provide undo semantics appropriate to the user's context: while within
+ * an embedded object, only edits to that object are seen and the user can
+ * undo/redo them without needing to impact edits in other objects; while
+ * within the larger document, all edits can be seen and the user must
+ * undo/redo them as a single stream.</p>
+ */
+public class UndoManager {
+ private final HashMap<String, UndoOwner> mOwners = new HashMap<String, UndoOwner>();
+ private final ArrayList<UndoState> mUndos = new ArrayList<UndoState>();
+ private final ArrayList<UndoState> mRedos = new ArrayList<UndoState>();
+ private int mUpdateCount;
+ private int mHistorySize = 20;
+ private UndoState mWorking;
+ private int mCommitId = 1;
+ private boolean mInUndo;
+ private boolean mMerged;
+
+ private int mStateSeq;
+ private int mNextSavedIdx;
+ private UndoOwner[] mStateOwners;
+
+ /**
+ * Never merge with the last undo state.
+ */
+ public static final int MERGE_MODE_NONE = 0;
+
+ /**
+ * Allow merge with the last undo state only if it contains
+ * operations with the caller's owner.
+ */
+ public static final int MERGE_MODE_UNIQUE = 1;
+
+ /**
+ * Always allow merge with the last undo state, if possible.
+ */
+ public static final int MERGE_MODE_ANY = 2;
+
+ public UndoOwner getOwner(String tag, Object data) {
+ if (tag == null) {
+ throw new NullPointerException("tag can't be null");
+ }
+ if (data == null) {
+ throw new NullPointerException("data can't be null");
+ }
+ UndoOwner owner = mOwners.get(tag);
+ if (owner != null) {
+ if (owner.mData != data) {
+ if (owner.mData != null) {
+ throw new IllegalStateException("Owner " + owner + " already exists with data "
+ + owner.mData + " but giving different data " + data);
+ }
+ owner.mData = data;
+ }
+ return owner;
+ }
+
+ owner = new UndoOwner(tag);
+ owner.mManager = this;
+ owner.mData = data;
+ mOwners.put(tag, owner);
+ return owner;
+ }
+
+ void removeOwner(UndoOwner owner) {
+ // XXX need to figure out how to prune.
+ if (false) {
+ mOwners.remove(owner.mTag);
+ owner.mManager = null;
+ }
+ }
+
+ /**
+ * Flatten the current undo state into a Parcelable object, which can later be restored
+ * with {@link #restoreInstanceState(android.os.Parcelable)}.
+ */
+ public Parcelable saveInstanceState() {
+ if (mUpdateCount > 0) {
+ throw new IllegalStateException("Can't save state while updating");
+ }
+ ParcelableParcel pp = new ParcelableParcel(getClass().getClassLoader());
+ Parcel p = pp.getParcel();
+ mStateSeq++;
+ if (mStateSeq <= 0) {
+ mStateSeq = 0;
+ }
+ mNextSavedIdx = 0;
+ p.writeInt(mHistorySize);
+ p.writeInt(mOwners.size());
+ // XXX eventually we need to be smart here about limiting the
+ // number of undo states we write to not exceed X bytes.
+ int i = mUndos.size();
+ while (i > 0) {
+ p.writeInt(1);
+ i--;
+ mUndos.get(i).writeToParcel(p);
+ }
+ i = mRedos.size();
+ p.writeInt(i);
+ while (i > 0) {
+ p.writeInt(2);
+ i--;
+ mRedos.get(i).writeToParcel(p);
+ }
+ p.writeInt(0);
+ return pp;
+ }
+
+ void saveOwner(UndoOwner owner, Parcel out) {
+ if (owner.mStateSeq == mStateSeq) {
+ out.writeInt(owner.mSavedIdx);
+ } else {
+ owner.mStateSeq = mStateSeq;
+ owner.mSavedIdx = mNextSavedIdx;
+ out.writeInt(owner.mSavedIdx);
+ out.writeString(owner.mTag);
+ mNextSavedIdx++;
+ }
+ }
+
+ /**
+ * Restore an undo state previously created with {@link #saveInstanceState()}. This will
+ * restore the UndoManager's state to almost exactly what it was at the point it had
+ * been previously saved; the only information not restored is the data object
+ * associated with each {@link UndoOwner}, which requires separate calls to
+ * {@link #getOwner(String, Object)} to re-associate the owner with its data.
+ */
+ public void restoreInstanceState(Parcelable state) {
+ if (mUpdateCount > 0) {
+ throw new IllegalStateException("Can't save state while updating");
+ }
+ forgetUndos(null, -1);
+ forgetRedos(null, -1);
+ ParcelableParcel pp = (ParcelableParcel)state;
+ Parcel p = pp.getParcel();
+ mHistorySize = p.readInt();
+ mStateOwners = new UndoOwner[p.readInt()];
+
+ int stype;
+ while ((stype=p.readInt()) != 0) {
+ UndoState ustate = new UndoState(this, p, pp.getClassLoader());
+ if (stype == 1) {
+ mUndos.add(0, ustate);
+ } else {
+ mRedos.add(0, ustate);
+ }
+ }
+ }
+
+ UndoOwner restoreOwner(Parcel in) {
+ int idx = in.readInt();
+ UndoOwner owner = mStateOwners[idx];
+ if (owner == null) {
+ String tag = in.readString();
+ owner = new UndoOwner(tag);
+ mStateOwners[idx] = owner;
+ mOwners.put(tag, owner);
+ }
+ return owner;
+ }
+
+ /**
+ * Set the maximum number of undo states that will be retained.
+ */
+ public void setHistorySize(int size) {
+ mHistorySize = size;
+ if (mHistorySize >= 0 && countUndos(null) > mHistorySize) {
+ forgetUndos(null, countUndos(null) - mHistorySize);
+ }
+ }
+
+ /**
+ * Return the current maximum number of undo states.
+ */
+ public int getHistorySize() {
+ return mHistorySize;
+ }
+
+ /**
+ * Perform undo of last/top <var>count</var> undo states. The states impacted
+ * by this can be limited through <var>owners</var>.
+ * @param owners Optional set of owners that should be impacted. If null, all
+ * undo states will be visible and available for undo. If non-null, only those
+ * states that contain one of the owners specified here will be visible.
+ * @param count Number of undo states to pop.
+ * @return Returns the number of undo states that were actually popped.
+ */
+ public int undo(UndoOwner[] owners, int count) {
+ if (mWorking != null) {
+ throw new IllegalStateException("Can't be called during an update");
+ }
+
+ int num = 0;
+ int i = -1;
+
+ mInUndo = true;
+
+ UndoState us = getTopUndo(null);
+ if (us != null) {
+ us.makeExecuted();
+ }
+
+ while (count > 0 && (i=findPrevState(mUndos, owners, i)) >= 0) {
+ UndoState state = mUndos.remove(i);
+ state.undo();
+ mRedos.add(state);
+ count--;
+ num++;
+ }
+
+ mInUndo = false;
+
+ return num;
+ }
+
+ /**
+ * Perform redo of last/top <var>count</var> undo states in the transient redo stack.
+ * The states impacted by this can be limited through <var>owners</var>.
+ * @param owners Optional set of owners that should be impacted. If null, all
+ * undo states will be visible and available for undo. If non-null, only those
+ * states that contain one of the owners specified here will be visible.
+ * @param count Number of undo states to pop.
+ * @return Returns the number of undo states that were actually redone.
+ */
+ public int redo(UndoOwner[] owners, int count) {
+ if (mWorking != null) {
+ throw new IllegalStateException("Can't be called during an update");
+ }
+
+ int num = 0;
+ int i = -1;
+
+ mInUndo = true;
+
+ while (count > 0 && (i=findPrevState(mRedos, owners, i)) >= 0) {
+ UndoState state = mRedos.remove(i);
+ state.redo();
+ mUndos.add(state);
+ count--;
+ num++;
+ }
+
+ mInUndo = false;
+
+ return num;
+ }
+
+ /**
+ * Returns true if we are currently inside of an undo/redo operation. This is
+ * useful for editors to know whether they should be generating new undo state
+ * when they see edit operations happening.
+ */
+ public boolean isInUndo() {
+ return mInUndo;
+ }
+
+ public int forgetUndos(UndoOwner[] owners, int count) {
+ if (count < 0) {
+ count = mUndos.size();
+ }
+
+ int removed = 0;
+ for (int i=0; i<mUndos.size() && removed < count; i++) {
+ UndoState state = mUndos.get(i);
+ if (count > 0 && matchOwners(state, owners)) {
+ state.destroy();
+ mUndos.remove(i);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ public int forgetRedos(UndoOwner[] owners, int count) {
+ if (count < 0) {
+ count = mRedos.size();
+ }
+
+ int removed = 0;
+ for (int i=0; i<mRedos.size() && removed < count; i++) {
+ UndoState state = mRedos.get(i);
+ if (count > 0 && matchOwners(state, owners)) {
+ state.destroy();
+ mRedos.remove(i);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /**
+ * Return the number of undo states on the undo stack.
+ * @param owners If non-null, only those states containing an operation with one of
+ * the owners supplied here will be counted.
+ */
+ public int countUndos(UndoOwner[] owners) {
+ if (owners == null) {
+ return mUndos.size();
+ }
+
+ int count=0;
+ int i=0;
+ while ((i=findNextState(mUndos, owners, i)) >= 0) {
+ count++;
+ i++;
+ }
+ return count;
+ }
+
+ /**
+ * Return the number of redo states on the undo stack.
+ * @param owners If non-null, only those states containing an operation with one of
+ * the owners supplied here will be counted.
+ */
+ public int countRedos(UndoOwner[] owners) {
+ if (owners == null) {
+ return mRedos.size();
+ }
+
+ int count=0;
+ int i=0;
+ while ((i=findNextState(mRedos, owners, i)) >= 0) {
+ count++;
+ i++;
+ }
+ return count;
+ }
+
+ /**
+ * Return the user-visible label for the top undo state on the stack.
+ * @param owners If non-null, will select the top-most undo state containing an
+ * operation with one of the owners supplied here.
+ */
+ public CharSequence getUndoLabel(UndoOwner[] owners) {
+ UndoState state = getTopUndo(owners);
+ return state != null ? state.getLabel() : null;
+ }
+
+ /**
+ * Return the user-visible label for the top redo state on the stack.
+ * @param owners If non-null, will select the top-most undo state containing an
+ * operation with one of the owners supplied here.
+ */
+ public CharSequence getRedoLabel(UndoOwner[] owners) {
+ UndoState state = getTopRedo(owners);
+ return state != null ? state.getLabel() : null;
+ }
+
+ /**
+ * Start creating a new undo state. Multiple calls to this function will nest until
+ * they are all matched by a later call to {@link #endUpdate}.
+ * @param label Optional user-visible label for this new undo state.
+ */
+ public void beginUpdate(CharSequence label) {
+ if (mInUndo) {
+ throw new IllegalStateException("Can't being update while performing undo/redo");
+ }
+ if (mUpdateCount <= 0) {
+ createWorkingState();
+ mMerged = false;
+ mUpdateCount = 0;
+ }
+
+ mWorking.updateLabel(label);
+ mUpdateCount++;
+ }
+
+ private void createWorkingState() {
+ mWorking = new UndoState(this, mCommitId++);
+ if (mCommitId < 0) {
+ mCommitId = 1;
+ }
+ }
+
+ /**
+ * Returns true if currently inside of a {@link #beginUpdate}.
+ */
+ public boolean isInUpdate() {
+ return mUpdateCount > 0;
+ }
+
+ /**
+ * Forcibly set a new for the new undo state being built within a {@link #beginUpdate}.
+ * Any existing label will be replaced with this one.
+ */
+ public void setUndoLabel(CharSequence label) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ mWorking.setLabel(label);
+ }
+
+ /**
+ * Set a new for the new undo state being built within a {@link #beginUpdate}, but
+ * only if there is not a label currently set for it.
+ */
+ public void suggestUndoLabel(CharSequence label) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ mWorking.updateLabel(label);
+ }
+
+ /**
+ * Return the number of times {@link #beginUpdate} has been called without a matching
+ * {@link #endUpdate} call.
+ */
+ public int getUpdateNestingLevel() {
+ return mUpdateCount;
+ }
+
+ /**
+ * Check whether there is an {@link UndoOperation} in the current {@link #beginUpdate}
+ * undo state.
+ * @param owner Optional owner of the operation to look for. If null, will succeed
+ * if there is any operation; if non-null, will only succeed if there is an operation
+ * with the given owner.
+ * @return Returns true if there is a matching operation in the current undo state.
+ */
+ public boolean hasOperation(UndoOwner owner) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ return mWorking.hasOperation(owner);
+ }
+
+ /**
+ * Return the most recent {@link UndoOperation} that was added to the update.
+ * @param mergeMode May be either {@link #MERGE_MODE_NONE} or {@link #MERGE_MODE_ANY}.
+ */
+ public UndoOperation<?> getLastOperation(int mergeMode) {
+ return getLastOperation(null, null, mergeMode);
+ }
+
+ /**
+ * Return the most recent {@link UndoOperation} that was added to the update and
+ * has the given owner.
+ * @param owner Optional owner of last operation to retrieve. If null, the last
+ * operation regardless of owner will be retrieved; if non-null, the last operation
+ * matching the given owner will be retrieved.
+ * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE},
+ * or {@link #MERGE_MODE_ANY}.
+ */
+ public UndoOperation<?> getLastOperation(UndoOwner owner, int mergeMode) {
+ return getLastOperation(null, owner, mergeMode);
+ }
+
+ /**
+ * Return the most recent {@link UndoOperation} that was added to the update and
+ * has the given owner.
+ * @param clazz Optional class of the last operation to retrieve. If null, the
+ * last operation regardless of class will be retrieved; if non-null, the last
+ * operation whose class is the same as the given class will be retrieved.
+ * @param owner Optional owner of last operation to retrieve. If null, the last
+ * operation regardless of owner will be retrieved; if non-null, the last operation
+ * matching the given owner will be retrieved.
+ * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE},
+ * or {@link #MERGE_MODE_ANY}.
+ */
+ public <T extends UndoOperation> T getLastOperation(Class<T> clazz, UndoOwner owner,
+ int mergeMode) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ if (mergeMode != MERGE_MODE_NONE && !mMerged && !mWorking.hasData()) {
+ UndoState state = getTopUndo(null);
+ UndoOperation<?> last;
+ if (state != null && (mergeMode == MERGE_MODE_ANY || !state.hasMultipleOwners())
+ && state.canMerge() && (last=state.getLastOperation(clazz, owner)) != null) {
+ if (last.allowMerge()) {
+ mWorking.destroy();
+ mWorking = state;
+ mUndos.remove(state);
+ mMerged = true;
+ return (T)last;
+ }
+ }
+ }
+
+ return mWorking.getLastOperation(clazz, owner);
+ }
+
+ /**
+ * Add a new UndoOperation to the current update.
+ * @param op The new operation to add.
+ * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE},
+ * or {@link #MERGE_MODE_ANY}.
+ */
+ public void addOperation(UndoOperation<?> op, int mergeMode) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ UndoOwner owner = op.getOwner();
+ if (owner.mManager != this) {
+ throw new IllegalArgumentException(
+ "Given operation's owner is not in this undo manager.");
+ }
+ if (mergeMode != MERGE_MODE_NONE && !mMerged && !mWorking.hasData()) {
+ UndoState state = getTopUndo(null);
+ if (state != null && (mergeMode == MERGE_MODE_ANY || !state.hasMultipleOwners())
+ && state.canMerge() && state.hasOperation(op.getOwner())) {
+ mWorking.destroy();
+ mWorking = state;
+ mUndos.remove(state);
+ mMerged = true;
+ }
+ }
+ mWorking.addOperation(op);
+ }
+
+ /**
+ * Finish the creation of an undo state, matching a previous call to
+ * {@link #beginUpdate}.
+ */
+ public void endUpdate() {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ mUpdateCount--;
+
+ if (mUpdateCount == 0) {
+ pushWorkingState();
+ }
+ }
+
+ private void pushWorkingState() {
+ int N = mUndos.size() + 1;
+
+ if (mWorking.hasData()) {
+ mUndos.add(mWorking);
+ forgetRedos(null, -1);
+ mWorking.commit();
+ if (N >= 2) {
+ // The state before this one can no longer be merged, ever.
+ // The only way to get back to it is for the user to perform
+ // an undo.
+ mUndos.get(N-2).makeExecuted();
+ }
+ } else {
+ mWorking.destroy();
+ }
+ mWorking = null;
+
+ if (mHistorySize >= 0 && N > mHistorySize) {
+ forgetUndos(null, N - mHistorySize);
+ }
+ }
+
+ /**
+ * Commit the last finished undo state. This undo state can no longer be
+ * modified with further {@link #MERGE_MODE_UNIQUE} or
+ * {@link #MERGE_MODE_ANY} merge modes. If called while inside of an update,
+ * this will push any changes in the current update on to the undo stack
+ * and result with a fresh undo state, behaving as if {@link #endUpdate()}
+ * had been called enough to unwind the current update, then the last state
+ * committed, and {@link #beginUpdate} called to restore the update nesting.
+ * @param owner The optional owner to determine whether to perform the commit.
+ * If this is non-null, the commit will only execute if the current top undo
+ * state contains an operation with the given owner.
+ * @return Returns an integer identifier for the committed undo state, which
+ * can later be used to try to uncommit the state to perform further edits on it.
+ */
+ public int commitState(UndoOwner owner) {
+ if (mWorking != null && mWorking.hasData()) {
+ if (owner == null || mWorking.hasOperation(owner)) {
+ mWorking.setCanMerge(false);
+ int commitId = mWorking.getCommitId();
+ pushWorkingState();
+ createWorkingState();
+ mMerged = true;
+ return commitId;
+ }
+ } else {
+ UndoState state = getTopUndo(null);
+ if (state != null && (owner == null || state.hasOperation(owner))) {
+ state.setCanMerge(false);
+ return state.getCommitId();
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Attempt to undo a previous call to {@link #commitState}. This will work
+ * if the undo state at the top of the stack has the given id, and has not been
+ * involved in an undo operation. Otherwise false is returned.
+ * @param commitId The identifier for the state to be uncommitted, as returned
+ * by {@link #commitState}.
+ * @param owner Optional owner that must appear in the committed state.
+ * @return Returns true if the uncommit is successful, else false.
+ */
+ public boolean uncommitState(int commitId, UndoOwner owner) {
+ if (mWorking != null && mWorking.getCommitId() == commitId) {
+ if (owner == null || mWorking.hasOperation(owner)) {
+ return mWorking.setCanMerge(true);
+ }
+ } else {
+ UndoState state = getTopUndo(null);
+ if (state != null && (owner == null || state.hasOperation(owner))) {
+ if (state.getCommitId() == commitId) {
+ return state.setCanMerge(true);
+ }
+ }
+ }
+ return false;
+ }
+
+ UndoState getTopUndo(UndoOwner[] owners) {
+ if (mUndos.size() <= 0) {
+ return null;
+ }
+ int i = findPrevState(mUndos, owners, -1);
+ return i >= 0 ? mUndos.get(i) : null;
+ }
+
+ UndoState getTopRedo(UndoOwner[] owners) {
+ if (mRedos.size() <= 0) {
+ return null;
+ }
+ int i = findPrevState(mRedos, owners, -1);
+ return i >= 0 ? mRedos.get(i) : null;
+ }
+
+ boolean matchOwners(UndoState state, UndoOwner[] owners) {
+ if (owners == null) {
+ return true;
+ }
+ for (int i=0; i<owners.length; i++) {
+ if (state.matchOwner(owners[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int findPrevState(ArrayList<UndoState> states, UndoOwner[] owners, int from) {
+ final int N = states.size();
+
+ if (from == -1) {
+ from = N-1;
+ }
+ if (from >= N) {
+ return -1;
+ }
+ if (owners == null) {
+ return from;
+ }
+
+ while (from >= 0) {
+ UndoState state = states.get(from);
+ if (matchOwners(state, owners)) {
+ return from;
+ }
+ from--;
+ }
+
+ return -1;
+ }
+
+ int findNextState(ArrayList<UndoState> states, UndoOwner[] owners, int from) {
+ final int N = states.size();
+
+ if (from < 0) {
+ from = 0;
+ }
+ if (from >= N) {
+ return -1;
+ }
+ if (owners == null) {
+ return from;
+ }
+
+ while (from < N) {
+ UndoState state = states.get(from);
+ if (matchOwners(state, owners)) {
+ return from;
+ }
+ from++;
+ }
+
+ return -1;
+ }
+
+ final static class UndoState {
+ private final UndoManager mManager;
+ private final int mCommitId;
+ private final ArrayList<UndoOperation<?>> mOperations = new ArrayList<UndoOperation<?>>();
+ private ArrayList<UndoOperation<?>> mRecent;
+ private CharSequence mLabel;
+ private boolean mCanMerge = true;
+ private boolean mExecuted;
+
+ UndoState(UndoManager manager, int commitId) {
+ mManager = manager;
+ mCommitId = commitId;
+ }
+
+ UndoState(UndoManager manager, Parcel p, ClassLoader loader) {
+ mManager = manager;
+ mCommitId = p.readInt();
+ mCanMerge = p.readInt() != 0;
+ mExecuted = p.readInt() != 0;
+ mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
+ final int N = p.readInt();
+ for (int i=0; i<N; i++) {
+ UndoOwner owner = mManager.restoreOwner(p);
+ UndoOperation op = (UndoOperation)p.readParcelable(loader);
+ op.mOwner = owner;
+ mOperations.add(op);
+ }
+ }
+
+ void writeToParcel(Parcel p) {
+ if (mRecent != null) {
+ throw new IllegalStateException("Can't save state before committing");
+ }
+ p.writeInt(mCommitId);
+ p.writeInt(mCanMerge ? 1 : 0);
+ p.writeInt(mExecuted ? 1 : 0);
+ TextUtils.writeToParcel(mLabel, p, 0);
+ final int N = mOperations.size();
+ p.writeInt(N);
+ for (int i=0; i<N; i++) {
+ UndoOperation op = mOperations.get(i);
+ mManager.saveOwner(op.mOwner, p);
+ p.writeParcelable(op, 0);
+ }
+ }
+
+ int getCommitId() {
+ return mCommitId;
+ }
+
+ void setLabel(CharSequence label) {
+ mLabel = label;
+ }
+
+ void updateLabel(CharSequence label) {
+ if (mLabel != null) {
+ mLabel = label;
+ }
+ }
+
+ CharSequence getLabel() {
+ return mLabel;
+ }
+
+ boolean setCanMerge(boolean state) {
+ // Don't allow re-enabling of merging if state has been executed.
+ if (state && mExecuted) {
+ return false;
+ }
+ mCanMerge = state;
+ return true;
+ }
+
+ void makeExecuted() {
+ mExecuted = true;
+ }
+
+ boolean canMerge() {
+ return mCanMerge && !mExecuted;
+ }
+
+ int countOperations() {
+ return mOperations.size();
+ }
+
+ boolean hasOperation(UndoOwner owner) {
+ final int N = mOperations.size();
+ if (owner == null) {
+ return N != 0;
+ }
+ for (int i=0; i<N; i++) {
+ if (mOperations.get(i).getOwner() == owner) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean hasMultipleOwners() {
+ final int N = mOperations.size();
+ if (N <= 1) {
+ return false;
+ }
+ UndoOwner owner = mOperations.get(0).getOwner();
+ for (int i=1; i<N; i++) {
+ if (mOperations.get(i).getOwner() != owner) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void addOperation(UndoOperation<?> op) {
+ if (mOperations.contains(op)) {
+ throw new IllegalStateException("Already holds " + op);
+ }
+ mOperations.add(op);
+ if (mRecent == null) {
+ mRecent = new ArrayList<UndoOperation<?>>();
+ mRecent.add(op);
+ }
+ op.mOwner.mOpCount++;
+ }
+
+ <T extends UndoOperation> T getLastOperation(Class<T> clazz, UndoOwner owner) {
+ final int N = mOperations.size();
+ if (clazz == null && owner == null) {
+ return N > 0 ? (T)mOperations.get(N-1) : null;
+ }
+ // First look for the top-most operation with the same owner.
+ for (int i=N-1; i>=0; i--) {
+ UndoOperation<?> op = mOperations.get(i);
+ if (owner != null && op.getOwner() != owner) {
+ continue;
+ }
+ // Return this operation if it has the same class that the caller wants.
+ // Note that we don't search deeper for the class, because we don't want
+ // to end up with a different order of operations for the same owner.
+ if (clazz != null && op.getClass() != clazz) {
+ return null;
+ }
+ return (T)op;
+ }
+
+ return null;
+ }
+
+ boolean matchOwner(UndoOwner owner) {
+ for (int i=mOperations.size()-1; i>=0; i--) {
+ if (mOperations.get(i).matchOwner(owner)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean hasData() {
+ for (int i=mOperations.size()-1; i>=0; i--) {
+ if (mOperations.get(i).hasData()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void commit() {
+ final int N = mRecent != null ? mRecent.size() : 0;
+ for (int i=0; i<N; i++) {
+ mRecent.get(i).commit();
+ }
+ mRecent = null;
+ }
+
+ void undo() {
+ for (int i=mOperations.size()-1; i>=0; i--) {
+ mOperations.get(i).undo();
+ }
+ }
+
+ void redo() {
+ final int N = mOperations.size();
+ for (int i=0; i<N; i++) {
+ mOperations.get(i).redo();
+ }
+ }
+
+ void destroy() {
+ for (int i=mOperations.size()-1; i>=0; i--) {
+ UndoOwner owner = mOperations.get(i).mOwner;
+ owner.mOpCount--;
+ if (owner.mOpCount <= 0) {
+ if (owner.mOpCount < 0) {
+ throw new IllegalStateException("Underflow of op count on owner " + owner
+ + " in op " + mOperations.get(i));
+ }
+ mManager.removeOwner(owner);
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/content/UndoOperation.java b/core/java/android/content/UndoOperation.java
new file mode 100644
index 0000000..8084b1f
--- /dev/null
+++ b/core/java/android/content/UndoOperation.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A single undoable operation. You must subclass this to implement the state
+ * and behavior for your operation. Instances of this class are placed and
+ * managed in an {@link UndoManager}.
+ */
+public abstract class UndoOperation<DATA> implements Parcelable {
+ UndoOwner mOwner;
+
+ /**
+ * Create a new instance of the operation.
+ * @param owner Who owns the data being modified by this undo state; must be
+ * returned by {@link UndoManager#getOwner(String, Object) UndoManager.getOwner}.
+ */
+ public UndoOperation(UndoOwner owner) {
+ mOwner = owner;
+ }
+
+ /**
+ * Construct from a Parcel.
+ */
+ protected UndoOperation(Parcel src, ClassLoader loader) {
+ }
+
+ /**
+ * Owning object as given to {@link #UndoOperation(UndoOwner)}.
+ */
+ public UndoOwner getOwner() {
+ return mOwner;
+ }
+
+ /**
+ * Synonym for {@link #getOwner()}.{@link android.content.UndoOwner#getData()}.
+ */
+ public DATA getOwnerData() {
+ return (DATA)mOwner.getData();
+ }
+
+ /**
+ * Return true if this undo operation is a member of the given owner.
+ * The default implementation is <code>owner == getOwner()</code>. You
+ * can override this to provide more sophisticated dependencies between
+ * owners.
+ */
+ public boolean matchOwner(UndoOwner owner) {
+ return owner == getOwner();
+ }
+
+ /**
+ * Return true if this operation actually contains modification data. The
+ * default implementation always returns true. If you return false, the
+ * operation will be dropped when the final undo state is being built.
+ */
+ public boolean hasData() {
+ return true;
+ }
+
+ /**
+ * Return true if this operation can be merged with a later operation.
+ * The default implementation always returns true.
+ */
+ public boolean allowMerge() {
+ return true;
+ }
+
+ /**
+ * Called when this undo state is being committed to the undo stack.
+ * The implementation should perform the initial edits and save any state that
+ * may be needed to undo them.
+ */
+ public abstract void commit();
+
+ /**
+ * Called when this undo state is being popped off the undo stack (in to
+ * the temporary redo stack). The implementation should remove the original
+ * edits and thus restore the target object to its prior value.
+ */
+ public abstract void undo();
+
+ /**
+ * Called when this undo state is being pushed back from the transient
+ * redo stack to the main undo stack. The implementation should re-apply
+ * the edits that were previously removed by {@link #undo}.
+ */
+ public abstract void redo();
+
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/content/UndoOwner.java b/core/java/android/content/UndoOwner.java
new file mode 100644
index 0000000..a279de6
--- /dev/null
+++ b/core/java/android/content/UndoOwner.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+/**
+ * Representation of an owner of {@link UndoOperation} objects in an {@link UndoManager}.
+ */
+public class UndoOwner {
+ final String mTag;
+
+ UndoManager mManager;
+ Object mData;
+ int mOpCount;
+
+ // For saving/restoring state.
+ int mStateSeq;
+ int mSavedIdx;
+
+ UndoOwner(String tag) {
+ mTag = tag;
+ }
+
+ /**
+ * Return the unique tag name identifying this owner. This is the tag
+ * supplied to {@link UndoManager#getOwner(String, Object) UndoManager.getOwner}
+ * and is immutable.
+ */
+ public String getTag() {
+ return mTag;
+ }
+
+ /**
+ * Return the actual data object of the owner. This is the data object
+ * supplied to {@link UndoManager#getOwner(String, Object) UndoManager.getOwner}. An
+ * owner may have a null data if it was restored from a previously saved state with
+ * no getOwner call to associate it with its data.
+ */
+ public Object getData() {
+ return mData;
+ }
+}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 02401dc..9c46d96 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -315,6 +315,14 @@
public static final int FLAG_IS_DATA_ONLY = 1<<24;
/**
+ * Value for {@link #flags}: set to {@code true} if the application
+ * is permitted to hold privileged permissions.
+ *
+ * {@hide}
+ */
+ public static final int FLAG_PRIVILEGED = 1<<30;
+
+ /**
* Value for {@link #flags}: Set to true if the application has been
* installed using the forward lock option.
*
@@ -338,6 +346,13 @@
public static final int FLAG_CANT_SAVE_STATE = 1<<28;
/**
+ * Value for {@link #flags}: true if the application is blocked via restrictions and for
+ * most purposes is considered as not installed.
+ * {@hide}
+ */
+ public static final int FLAG_BLOCKED = 1<<27;
+
+ /**
* Flags associated with the application. Any combination of
* {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
* {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
@@ -351,7 +366,7 @@
* {@link #FLAG_INSTALLED}.
*/
public int flags = 0;
-
+
/**
* The required smallest screen width the application can run on. If 0,
* nothing has been specified. Comes from
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index 2812477..4dbcf23 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -116,6 +116,17 @@
public final int getIconResource() {
return icon != 0 ? icon : applicationInfo.icon;
}
+
+ /**
+ * Return the logo resource identifier to use for this component. If
+ * the component defines a logo, that is used; else, the application
+ * logo is used.
+ *
+ * @return The logo associated with this component.
+ */
+ public final int getLogoResource() {
+ return logo != 0 ? logo : applicationInfo.logo;
+ }
protected void dumpFront(Printer pw, String prefix) {
super.dumpFront(pw, prefix);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a0e1555..eaff7b2 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -399,4 +399,7 @@
/** Reflects current DeviceStorageMonitorService state */
boolean isStorageLow();
+
+ boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked, int userId);
+ boolean getApplicationBlockedSettingAsUser(String packageName, int userId);
}
diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java
new file mode 100644
index 0000000..0ef09a4
--- /dev/null
+++ b/core/java/android/content/pm/KeySet.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.os.Binder;
+
+/** @hide */
+public class KeySet {
+
+ private Binder token;
+
+ /** @hide */
+ public KeySet(Binder token) {
+ this.token = token;
+ }
+
+ Binder getToken() {
+ return token;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4266d85..8a8751e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3083,6 +3083,23 @@
public abstract int getApplicationEnabledSetting(String packageName);
/**
+ * Puts the package in a blocked state, which is almost like an uninstalled state,
+ * making the package unavailable, but it doesn't remove the data or the actual
+ * package file.
+ * @hide
+ */
+ public abstract boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+ UserHandle userHandle);
+
+ /**
+ * Returns the blocked state of a package.
+ * @see #setApplicationBlockedSettingAsUser(String, boolean, UserHandle)
+ * @hide
+ */
+ public abstract boolean getApplicationBlockedSettingAsUser(String packageName,
+ UserHandle userHandle);
+
+ /**
* Return whether the device has been booted into safe mode.
*/
public abstract boolean isSafeMode();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 34e0c12..810a521 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -45,14 +45,20 @@
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertPath;
+import java.security.cert.X509Certificate;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
@@ -153,7 +159,8 @@
private static WeakReference<byte[]> mReadBuffer;
private static boolean sCompatibilityModeEnabled = true;
- private static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ private static final int PARSE_DEFAULT_INSTALL_LOCATION =
+ PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
static class ParsePackageItemArgs {
final Package owner;
@@ -268,15 +275,20 @@
grantedPermissions, state, UserHandle.getCallingUserId());
}
- private static boolean checkUseInstalled(int flags, PackageUserState state) {
- return state.installed || ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0);
+ /**
+ * Returns true if the package is installed and not blocked, or if the caller
+ * explicitly wanted all uninstalled and blocked packages as well.
+ */
+ private static boolean checkUseInstalledOrBlocked(int flags, PackageUserState state) {
+ return (state.installed && !state.blocked)
+ || (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
}
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
HashSet<String> grantedPermissions, PackageUserState state, int userId) {
- if (!checkUseInstalled(flags, state)) {
+ if (!checkUseInstalledOrBlocked(flags, state)) {
return null;
}
PackageInfo pi = new PackageInfo();
@@ -470,6 +482,7 @@
public final static int PARSE_FORWARD_LOCK = 1<<4;
public final static int PARSE_ON_SDCARD = 1<<5;
public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
+ public final static int PARSE_IS_PRIVILEGED = 1<<7;
public int getParseError() {
return mParseError;
@@ -714,6 +727,13 @@
mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
return false;
}
+
+ // Add the signing KeySet to the system
+ pkg.mSigningKeys = new HashSet<PublicKey>();
+ for (int i=0; i < certs.length; i++) {
+ pkg.mSigningKeys.add(certs[i].getPublicKey());
+ }
+
} catch (CertificateEncodingException e) {
Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
@@ -1027,6 +1047,10 @@
if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
return null;
}
+ } else if (tagName.equals("keys")) {
+ if (!parseKeys(pkg, res, parser, attrs, outError)) {
+ return null;
+ }
} else if (tagName.equals("permission-group")) {
if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
return null;
@@ -1519,7 +1543,71 @@
}
return buildCompoundName(pkg, procSeq, "taskAffinity", outError);
}
-
+
+ private boolean parseKeys(Package owner, Resources res,
+ XmlPullParser parser, AttributeSet attrs, String[] outError)
+ throws XmlPullParserException, IOException {
+ // we've encountered the 'keys' tag
+ // all the keys and keysets that we want must be defined here
+ // so we're going to iterate over the parser and pull out the things we want
+ int outerDepth = parser.getDepth();
+
+ int type;
+ PublicKey currentKey = null;
+ Map<PublicKey, Set<String>> definedKeySets = new HashMap<PublicKey, Set<String>>();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG) {
+ continue;
+ }
+ String tagname = parser.getName();
+ if (tagname.equals("publicKey")) {
+ final TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.PublicKey);
+ final String encodedKey = sa.getNonResourceString(
+ com.android.internal.R.styleable.PublicKey_value);
+ currentKey = parsePublicKey(encodedKey);
+ definedKeySets.put(currentKey, new HashSet<String>());
+ sa.recycle();
+ } else if (tagname.equals("keyset")) {
+ final TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.KeySet);
+ final String name = sa.getNonResourceString(
+ com.android.internal.R.styleable.KeySet_name);
+ definedKeySets.get(currentKey).add(name);
+ sa.recycle();
+ } else if (RIGID_PARSER) {
+ Slog.w(TAG, "Bad element under <keys>: " + parser.getName()
+ + " at " + mArchiveSourcePath + " "
+ + parser.getPositionDescription());
+ return false;
+ } else {
+ Slog.w(TAG, "Unknown element under <keys>: " + parser.getName()
+ + " at " + mArchiveSourcePath + " "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ }
+
+ owner.mKeySetMapping = new HashMap<String, Set<PublicKey>>();
+ for (Map.Entry<PublicKey, Set<String>> e : definedKeySets.entrySet()) {
+ PublicKey key = e.getKey();
+ Set<String> keySetNames = e.getValue();
+ for (String alias : keySetNames) {
+ if (owner.mKeySetMapping.containsKey(alias)) {
+ owner.mKeySetMapping.get(alias).add(key);
+ } else {
+ Set<PublicKey> keys = new HashSet<PublicKey>();
+ keys.add(key);
+ owner.mKeySetMapping.put(alias, keys);
+ }
+ }
+ }
+
+ return true;
+ }
+
private PermissionGroup parsePermissionGroup(Package owner, int flags, Resources res,
XmlPullParser parser, AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
@@ -3083,20 +3171,28 @@
Slog.i(TAG, "verifier " + packageName + " public key was null; skipping");
}
+ PublicKey publicKey = parsePublicKey(encodedPublicKey);
+ if (publicKey != null) {
+ return new VerifierInfo(packageName, publicKey);
+ }
+
+ return null;
+ }
+
+ public static final PublicKey parsePublicKey(String encodedPublicKey) {
EncodedKeySpec keySpec;
try {
final byte[] encoded = Base64.decode(encodedPublicKey, Base64.DEFAULT);
keySpec = new X509EncodedKeySpec(encoded);
} catch (IllegalArgumentException e) {
- Slog.i(TAG, "Could not parse verifier " + packageName + " public key; invalid Base64");
+ Slog.i(TAG, "Could not parse verifier public key; invalid Base64");
return null;
}
/* First try the key as an RSA key. */
try {
final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- final PublicKey publicKey = keyFactory.generatePublic(keySpec);
- return new VerifierInfo(packageName, publicKey);
+ return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "Could not parse public key because RSA isn't included in build");
return null;
@@ -3107,8 +3203,7 @@
/* Now try it as a DSA key. */
try {
final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
- final PublicKey publicKey = keyFactory.generatePublic(keySpec);
- return new VerifierInfo(packageName, publicKey);
+ return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "Could not parse public key because DSA isn't included in build");
return null;
@@ -3200,6 +3295,24 @@
outInfo.addDataScheme(str);
}
+ str = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifestData_ssp, 0);
+ if (str != null) {
+ outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_LITERAL);
+ }
+
+ str = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifestData_sspPrefix, 0);
+ if (str != null) {
+ outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_PREFIX);
+ }
+
+ str = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifestData_sspPattern, 0);
+ if (str != null) {
+ outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+ }
+
String host = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_host, 0);
String port = sa.getNonConfigurationString(
@@ -3360,6 +3473,12 @@
*/
public ManifestDigest manifestDigest;
+ /**
+ * Data used to feed the KeySetManager
+ */
+ public Set<PublicKey> mSigningKeys;
+ public Map<String, Set<PublicKey>> mKeySetMapping;
+
public Package(String _name) {
packageName = _name;
applicationInfo.packageName = _name;
@@ -3611,7 +3730,7 @@
return true;
}
}
- if (!state.installed) {
+ if (!state.installed || state.blocked) {
return true;
}
if (state.stopped) {
@@ -3644,6 +3763,11 @@
} else {
ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
}
+ if (state.blocked) {
+ ai.flags |= ApplicationInfo.FLAG_BLOCKED;
+ } else {
+ ai.flags &= ~ApplicationInfo.FLAG_BLOCKED;
+ }
if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
} else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
@@ -3658,7 +3782,7 @@
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
PackageUserState state, int userId) {
if (p == null) return null;
- if (!checkUseInstalled(flags, state)) {
+ if (!checkUseInstalledOrBlocked(flags, state)) {
return null;
}
if (!copyNeeded(flags, p, state, null, userId)
@@ -3742,7 +3866,7 @@
public static final ActivityInfo generateActivityInfo(Activity a, int flags,
PackageUserState state, int userId) {
if (a == null) return null;
- if (!checkUseInstalled(flags, state)) {
+ if (!checkUseInstalledOrBlocked(flags, state)) {
return null;
}
if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) {
@@ -3779,7 +3903,7 @@
public static final ServiceInfo generateServiceInfo(Service s, int flags,
PackageUserState state, int userId) {
if (s == null) return null;
- if (!checkUseInstalled(flags, state)) {
+ if (!checkUseInstalledOrBlocked(flags, state)) {
return null;
}
if (!copyNeeded(flags, s.owner, state, s.metaData, userId)) {
@@ -3824,7 +3948,7 @@
public static final ProviderInfo generateProviderInfo(Provider p, int flags,
PackageUserState state, int userId) {
if (p == null) return null;
- if (!checkUseInstalled(flags, state)) {
+ if (!checkUseInstalledOrBlocked(flags, state)) {
return null;
}
if (!copyNeeded(flags, p.owner, state, p.metaData, userId)
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index dcd54fc..94e3f79 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -28,6 +28,7 @@
public boolean stopped;
public boolean notLaunched;
public boolean installed;
+ public boolean blocked; // Is the app restricted by owner / admin
public int enabled;
public String lastDisableAppCaller;
@@ -37,6 +38,7 @@
public PackageUserState() {
installed = true;
+ blocked = false;
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
}
@@ -45,6 +47,7 @@
stopped = o.stopped;
notLaunched = o.notLaunched;
enabled = o.enabled;
+ blocked = o.blocked;
lastDisableAppCaller = o.lastDisableAppCaller;
disabledComponents = o.disabledComponents != null
? new HashSet<String>(o.disabledComponents) : null;
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 288d55f..875e8de 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -82,7 +82,7 @@
@GuardedBy("mServicesLock")
private boolean mPersistentServicesFileDidNotExist;
@GuardedBy("mServicesLock")
- private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>();
+ private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
private static class UserServices<V> {
@GuardedBy("mServicesLock")
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 07117fe..de8e256 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -328,6 +328,7 @@
implements Comparator<ResolveInfo> {
public DisplayNameComparator(PackageManager pm) {
mPM = pm;
+ mCollator.setStrength(Collator.PRIMARY);
}
public final int compare(ResolveInfo a, ResolveInfo b) {
@@ -336,10 +337,10 @@
CharSequence sb = b.loadLabel(mPM);
if (sb == null) sb = b.activityInfo.name;
- return sCollator.compare(sa.toString(), sb.toString());
+ return mCollator.compare(sa.toString(), sb.toString());
}
- private final Collator sCollator = Collator.getInstance();
+ private final Collator mCollator = Collator.getInstance();
private PackageManager mPM;
}
}
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index 7d46710..e4cc77f 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -20,6 +20,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -30,7 +31,7 @@
* opened FileDescriptor that can be used to read the data, as well as the
* offset and length of that entry's data in the file.
*/
-public class AssetFileDescriptor implements Parcelable {
+public class AssetFileDescriptor implements Parcelable, Closeable {
/**
* Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
* and {@link #getDeclaredLength} when a length has not been declared. This means
@@ -122,6 +123,7 @@
/**
* Convenience for calling <code>getParcelFileDescriptor().close()</code>.
*/
+ @Override
public void close() throws IOException {
mFd.close();
}
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 28c751c..da35ee9 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -471,8 +471,7 @@
* Compute the frame Rect for applications runs under compatibility mode.
*
* @param dm the display metrics used to compute the frame size.
- * @param orientation the orientation of the screen.
- * @param outRect the output parameter which will contain the result.
+ * @param outDm If non-null the width and height will be set to their scaled values.
* @return Returns the scaling factor for the window.
*/
public static float computeCompatibleScaling(DisplayMetrics dm, DisplayMetrics outDm) {
@@ -518,6 +517,9 @@
@Override
public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
try {
CompatibilityInfo oc = (CompatibilityInfo)o;
if (mCompatibilityFlags != oc.mCompatibilityFlags) return false;
@@ -579,10 +581,12 @@
public static final Parcelable.Creator<CompatibilityInfo> CREATOR
= new Parcelable.Creator<CompatibilityInfo>() {
+ @Override
public CompatibilityInfo createFromParcel(Parcel source) {
return new CompatibilityInfo(source);
}
+ @Override
public CompatibilityInfo[] newArray(int size) {
return new CompatibilityInfo[size];
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 42f4faf..6483cd9 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -16,8 +16,6 @@
package android.content.res;
-import android.os.Trace;
-import android.view.View;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -30,12 +28,15 @@
import android.graphics.drawable.Drawable.ConstantState;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
import android.util.LongSparseArray;
+import android.view.DisplayAdjustments;
import java.io.IOException;
import java.io.InputStream;
@@ -100,11 +101,11 @@
/*package*/ final Configuration mTmpConfig = new Configuration();
/*package*/ TypedValue mTmpValue = new TypedValue();
/*package*/ final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
- = new LongSparseArray<WeakReference<Drawable.ConstantState> >();
+ = new LongSparseArray<WeakReference<Drawable.ConstantState> >(0);
/*package*/ final LongSparseArray<WeakReference<ColorStateList> > mColorStateListCache
- = new LongSparseArray<WeakReference<ColorStateList> >();
+ = new LongSparseArray<WeakReference<ColorStateList> >(0);
/*package*/ final LongSparseArray<WeakReference<Drawable.ConstantState> > mColorDrawableCache
- = new LongSparseArray<WeakReference<Drawable.ConstantState> >();
+ = new LongSparseArray<WeakReference<Drawable.ConstantState> >(0);
/*package*/ boolean mPreloading;
/*package*/ TypedArray mCachedStyledAttributes = null;
@@ -118,8 +119,9 @@
private final Configuration mConfiguration = new Configuration();
/*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
private NativePluralRules mPluralRule;
-
- private CompatibilityInfo mCompatibilityInfo;
+
+ private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ private WeakReference<IBinder> mToken;
static {
sPreloadedDrawables = new LongSparseArray[2];
@@ -174,7 +176,7 @@
* selecting/computing resource values (optional).
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
- this(assets, metrics, config, null);
+ this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
/**
@@ -185,15 +187,16 @@
* selecting/computing resource values.
* @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
- * @param compInfo this resource's compatibility info. It will use the default compatibility
- * info when it's null.
+ * @param compatInfo this resource's compatibility info. Must not be null.
+ * @param token The Activity token for determining stack affiliation. Usually null.
* @hide
*/
- public Resources(AssetManager assets, DisplayMetrics metrics,
- Configuration config, CompatibilityInfo compInfo) {
+ public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
+ CompatibilityInfo compatInfo, IBinder token) {
mAssets = assets;
mMetrics.setToDefaults();
- mCompatibilityInfo = compInfo;
+ mCompatibilityInfo = compatInfo;
+ mToken = new WeakReference<IBinder>(token);
updateConfiguration(config, metrics);
assets.ensureStringBlocks();
}
@@ -1665,13 +1668,6 @@
}
/**
- * @hide
- */
- public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
- updateSystemConfiguration(config, metrics, null);
- }
-
- /**
* Return the current display metrics that are in effect for this resource
* object. The returned object should be treated as read-only.
*
@@ -1985,6 +1981,13 @@
}
}
+ /**
+ * @hide
+ */
+ public LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
+ return sPreloadedDrawables[0];
+ }
+
private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
int resourceId, String name) {
// We allow preloading of resources even if they vary by font scale (which
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
new file mode 100644
index 0000000..53e0f2c
--- /dev/null
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res;
+
+import android.os.IBinder;
+
+/** @hide */
+public final class ResourcesKey {
+ final String mResDir;
+ final float mScale;
+ private final int mHash;
+ private final IBinder mToken;
+
+ public final int mDisplayId;
+ public final Configuration mOverrideConfiguration = new Configuration();
+
+ public ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration,
+ float scale, IBinder token) {
+ mResDir = resDir;
+ mDisplayId = displayId;
+ if (overrideConfiguration != null) {
+ mOverrideConfiguration.setTo(overrideConfiguration);
+ }
+ mScale = scale;
+ mToken = token;
+
+ int hash = 17;
+ hash = 31 * hash + (mResDir == null ? 0 : mResDir.hashCode());
+ hash = 31 * hash + mDisplayId;
+ hash = 31 * hash + (mOverrideConfiguration != null
+ ? mOverrideConfiguration.hashCode() : 0);
+ hash = 31 * hash + Float.floatToIntBits(mScale);
+ mHash = hash;
+ }
+
+ public boolean hasOverrideConfiguration() {
+ return !Configuration.EMPTY.equals(mOverrideConfiguration);
+ }
+
+ @Override
+ public int hashCode() {
+ return mHash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ResourcesKey)) {
+ return false;
+ }
+ ResourcesKey peer = (ResourcesKey) obj;
+ if (!mResDir.equals(peer.mResDir)) {
+ return false;
+ }
+ if (mDisplayId != peer.mDisplayId) {
+ return false;
+ }
+ if (mOverrideConfiguration != peer.mOverrideConfiguration) {
+ if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
+ return false;
+ }
+ if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) {
+ return false;
+ }
+ }
+ if (mScale != peer.mScale) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toHexString(mHash);
+ }
+}
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index 300b4d1..b5b89dd 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -369,7 +369,9 @@
}
public Uri getNotificationUri() {
- return mNotifyUri;
+ synchronized (mSelfObserverLock) {
+ return mNotifyUri;
+ }
}
public boolean getWantsAllOnMoveCalls() {
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index 907833d..7381e2c 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -425,6 +425,16 @@
void setNotificationUri(ContentResolver cr, Uri uri);
/**
+ * Return the URI at which notifications of changes in this Cursor's data
+ * will be delivered, as previously set by {@link #setNotificationUri}.
+ * @return Returns a URI that can be used with
+ * {@link ContentResolver#registerContentObserver(android.net.Uri, boolean, ContentObserver)
+ * ContentResolver.registerContentObserver} to find out about changes to this Cursor's
+ * data. May be null if no notification URI has been set.
+ */
+ Uri getNotificationUri();
+
+ /**
* onMove() will only be called across processes if this method returns true.
* @return whether all cursor movement should result in a call to onMove().
*/
diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java
index 7baeb8c..d8fcb17 100644
--- a/core/java/android/database/CursorWrapper.java
+++ b/core/java/android/database/CursorWrapper.java
@@ -194,6 +194,10 @@
mCursor.setNotificationUri(cr, uri);
}
+ public Uri getNotificationUri() {
+ return mCursor.getNotificationUri();
+ }
+
public void unregisterContentObserver(ContentObserver observer) {
mCursor.unregisterContentObserver(observer);
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 4e51080..feb47aa 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -31,6 +31,11 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.RSIllegalArgumentException;
+import android.renderscript.Type;
import android.util.Log;
import android.text.TextUtils;
import android.view.Surface;
@@ -152,6 +157,7 @@
private PictureCallback mRawImageCallback;
private PictureCallback mJpegCallback;
private PreviewCallback mPreviewCallback;
+ private boolean mUsingPreviewAllocation;
private PictureCallback mPostviewCallback;
private AutoFocusCallback mAutoFocusCallback;
private AutoFocusMoveCallback mAutoFocusMoveCallback;
@@ -327,6 +333,7 @@
mJpegCallback = null;
mPreviewCallback = null;
mPostviewCallback = null;
+ mUsingPreviewAllocation = false;
mZoomListener = null;
Looper looper;
@@ -587,6 +594,9 @@
mPreviewCallback = cb;
mOneShot = false;
mWithBuffer = false;
+ if (cb != null) {
+ mUsingPreviewAllocation = false;
+ }
// Always use one-shot mode. We fake camera preview mode by
// doing one-shot preview continuously.
setHasPreviewCallback(cb != null, false);
@@ -610,6 +620,9 @@
mPreviewCallback = cb;
mOneShot = true;
mWithBuffer = false;
+ if (cb != null) {
+ mUsingPreviewAllocation = false;
+ }
setHasPreviewCallback(cb != null, false);
}
@@ -645,6 +658,9 @@
mPreviewCallback = cb;
mOneShot = false;
mWithBuffer = true;
+ if (cb != null) {
+ mUsingPreviewAllocation = false;
+ }
setHasPreviewCallback(cb != null, true);
}
@@ -744,6 +760,134 @@
private native final void _addCallbackBuffer(
byte[] callbackBuffer, int msgType);
+ /**
+ * <p>Create a {@link android.renderscript RenderScript}
+ * {@link android.renderscript.Allocation Allocation} to use as a
+ * destination of preview callback frames. Use
+ * {@link #setPreviewCallbackAllocation setPreviewCallbackAllocation} to use
+ * the created Allocation as a destination for camera preview frames.</p>
+ *
+ * <p>The Allocation will be created with a YUV type, and its contents must
+ * be accessed within Renderscript with the {@code rsGetElementAtYuv_*}
+ * accessor methods. Its size will be based on the current
+ * {@link Parameters#getPreviewSize preview size} configured for this
+ * camera.</p>
+ *
+ * @param rs the RenderScript context for this Allocation.
+ * @param usage additional usage flags to set for the Allocation. The usage
+ * flag {@link android.renderscript.Allocation#USAGE_IO_INPUT} will always
+ * be set on the created Allocation, but additional flags may be provided
+ * here.
+ * @return a new YUV-type Allocation with dimensions equal to the current
+ * preview size.
+ * @throws RSIllegalArgumentException if the usage flags are not compatible
+ * with an YUV Allocation.
+ * @see #setPreviewCallbackAllocation
+ * @hide
+ */
+ public final Allocation createPreviewAllocation(RenderScript rs, int usage)
+ throws RSIllegalArgumentException {
+ Parameters p = getParameters();
+ Size previewSize = p.getPreviewSize();
+ Type.Builder yuvBuilder = new Type.Builder(rs,
+ Element.createPixel(rs,
+ Element.DataType.UNSIGNED_8,
+ Element.DataKind.PIXEL_YUV));
+ // Use YV12 for wide compatibility. Changing this requires also
+ // adjusting camera service's format selection.
+ yuvBuilder.setYuvFormat(ImageFormat.YV12);
+ yuvBuilder.setX(previewSize.width);
+ yuvBuilder.setY(previewSize.height);
+
+ Allocation a = Allocation.createTyped(rs, yuvBuilder.create(),
+ usage | Allocation.USAGE_IO_INPUT);
+
+ return a;
+ }
+
+ /**
+ * <p>Set an {@link android.renderscript.Allocation Allocation} as the
+ * target of preview callback data. Use this method for efficient processing
+ * of camera preview data with RenderScript. The Allocation must be created
+ * with the {@link #createPreviewAllocation createPreviewAllocation }
+ * method.</p>
+ *
+ * <p>Setting a preview allocation will disable any active preview callbacks
+ * set by {@link #setPreviewCallback setPreviewCallback} or
+ * {@link #setPreviewCallbackWithBuffer setPreviewCallbackWithBuffer}, and
+ * vice versa. Using a preview allocation still requires an active standard
+ * preview target to be set, either with
+ * {@link #setPreviewTexture setPreviewTexture} or
+ * {@link #setPreviewDisplay setPreviewDisplay}.</p>
+ *
+ * <p>To be notified when new frames are available to the Allocation, use
+ * {@link android.renderscript.Allocation#setIoInputNotificationHandler Allocation.setIoInputNotificationHandler}. To
+ * update the frame currently accessible from the Allocation to the latest
+ * preview frame, call
+ * {@link android.renderscript.Allocation#ioReceive Allocation.ioReceive}.</p>
+ *
+ * <p>To disable preview into the Allocation, call this method with a
+ * {@code null} parameter.</p>
+ *
+ * <p>Once a preview allocation is set, the preview size set by
+ * {@link Parameters#setPreviewSize setPreviewSize} cannot be changed. If
+ * you wish to change the preview size, first remove the preview allocation
+ * by calling {@code setPreviewCallbackAllocation(null)}, then change the
+ * preview size, create a new preview Allocation with
+ * {@link #createPreviewAllocation createPreviewAllocation}, and set it as
+ * the new preview callback allocation target.</p>
+ *
+ * <p>If you are using the preview data to create video or still images,
+ * strongly consider using {@link android.media.MediaActionSound} to
+ * properly indicate image capture or recording start/stop to the user.</p>
+ *
+ * @param previewAllocation the allocation to use as destination for preview
+ * @throws IOException if configuring the camera to use the Allocation for
+ * preview fails.
+ * @throws IllegalArgumentException if the Allocation's dimensions or other
+ * parameters don't meet the requirements.
+ * @see #createPreviewAllocation
+ * @see #setPreviewCallback
+ * @see #setPreviewCallbackWithBuffer
+ * @hide
+ */
+ public final void setPreviewCallbackAllocation(Allocation previewAllocation)
+ throws IOException {
+ Surface previewSurface = null;
+ if (previewAllocation != null) {
+ Parameters p = getParameters();
+ Size previewSize = p.getPreviewSize();
+ if (previewSize.width != previewAllocation.getType().getX() ||
+ previewSize.height != previewAllocation.getType().getY()) {
+ throw new IllegalArgumentException(
+ "Allocation dimensions don't match preview dimensions: " +
+ "Allocation is " +
+ previewAllocation.getType().getX() +
+ ", " +
+ previewAllocation.getType().getY() +
+ ". Preview is " + previewSize.width + ", " +
+ previewSize.height);
+ }
+ if ((previewAllocation.getUsage() &
+ Allocation.USAGE_IO_INPUT) == 0) {
+ throw new IllegalArgumentException(
+ "Allocation usage does not include USAGE_IO_INPUT");
+ }
+ if (previewAllocation.getType().getElement().getDataKind() !=
+ Element.DataKind.PIXEL_YUV) {
+ throw new IllegalArgumentException(
+ "Allocation is not of a YUV type");
+ }
+ previewSurface = previewAllocation.getSurface();
+ mUsingPreviewAllocation = true;
+ } else {
+ mUsingPreviewAllocation = false;
+ }
+ setPreviewCallbackSurface(previewSurface);
+ }
+
+ private native final void setPreviewCallbackSurface(Surface s);
+
private class EventHandler extends Handler
{
private Camera mCamera;
@@ -1492,6 +1636,17 @@
* @see #getParameters()
*/
public void setParameters(Parameters params) {
+ // If using preview allocations, don't allow preview size changes
+ if (mUsingPreviewAllocation) {
+ Size newPreviewSize = params.getPreviewSize();
+ Size currentPreviewSize = getParameters().getPreviewSize();
+ if (newPreviewSize.width != currentPreviewSize.width ||
+ newPreviewSize.height != currentPreviewSize.height) {
+ throw new IllegalStateException("Cannot change preview size" +
+ " while a preview allocation is configured.");
+ }
+ }
+
native_setParameters(params.flatten());
}
@@ -2614,21 +2769,24 @@
* JPEG {@link PictureCallback}. The camera driver may set orientation
* in the EXIF header without rotating the picture. Or the driver may
* rotate the picture and the EXIF thumbnail. If the Jpeg picture is
- * rotated, the orientation in the EXIF header will be missing or 1
- * (row #0 is top and column #0 is left side).
+ * rotated, the orientation in the EXIF header will be missing or 1 (row
+ * #0 is top and column #0 is left side).
*
- * <p>If applications want to rotate the picture to match the orientation
- * of what users see, apps should use {@link
- * android.view.OrientationEventListener} and {@link CameraInfo}.
- * The value from OrientationEventListener is relative to the natural
- * orientation of the device. CameraInfo.orientation is the angle
- * between camera orientation and natural device orientation. The sum
- * of the two is the rotation angle for back-facing camera. The
- * difference of the two is the rotation angle for front-facing camera.
- * Note that the JPEG pictures of front-facing cameras are not mirrored
- * as in preview display.
+ * <p>
+ * If applications want to rotate the picture to match the orientation
+ * of what users see, apps should use
+ * {@link android.view.OrientationEventListener} and
+ * {@link android.hardware.Camera.CameraInfo}. The value from
+ * OrientationEventListener is relative to the natural orientation of
+ * the device. CameraInfo.orientation is the angle between camera
+ * orientation and natural device orientation. The sum of the two is the
+ * rotation angle for back-facing camera. The difference of the two is
+ * the rotation angle for front-facing camera. Note that the JPEG
+ * pictures of front-facing cameras are not mirrored as in preview
+ * display.
*
- * <p>For example, suppose the natural orientation of the device is
+ * <p>
+ * For example, suppose the natural orientation of the device is
* portrait. The device is rotated 270 degrees clockwise, so the device
* orientation is 270. Suppose a back-facing camera sensor is mounted in
* landscape and the top side of the camera sensor is aligned with the
diff --git a/core/java/android/hardware/CameraInfo.aidl b/core/java/android/hardware/CameraInfo.aidl
new file mode 100644
index 0000000..e21e694
--- /dev/null
+++ b/core/java/android/hardware/CameraInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/** @hide */
+parcelable CameraInfo;
diff --git a/core/java/android/hardware/CameraInfo.java b/core/java/android/hardware/CameraInfo.java
new file mode 100644
index 0000000..53da0ce
--- /dev/null
+++ b/core/java/android/hardware/CameraInfo.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information about a camera
+ *
+ * @hide
+ */
+public class CameraInfo implements Parcelable {
+ // Can't parcel nested classes, so make this a top level class that composes
+ // CameraInfo.
+ public Camera.CameraInfo info = new Camera.CameraInfo();
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(info.facing);
+ out.writeInt(info.orientation);
+ }
+
+ public void readFromParcel(Parcel in) {
+ info.facing = in.readInt();
+ info.orientation = in.readInt();
+ }
+
+ public static final Parcelable.Creator<CameraInfo> CREATOR =
+ new Parcelable.Creator<CameraInfo>() {
+ @Override
+ public CameraInfo createFromParcel(Parcel in) {
+ CameraInfo info = new CameraInfo();
+ info.readFromParcel(in);
+
+ return info;
+ }
+
+ @Override
+ public CameraInfo[] newArray(int size) {
+ return new CameraInfo[size];
+ }
+ };
+};
diff --git a/core/java/android/hardware/ICamera.aidl b/core/java/android/hardware/ICamera.aidl
new file mode 100644
index 0000000..d4f64f8
--- /dev/null
+++ b/core/java/android/hardware/ICamera.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/** @hide */
+interface ICamera
+{
+ /**
+ * Keep up-to-date with frameworks/av/include/camera/ICamera.h
+ */
+ void disconnect();
+}
diff --git a/core/java/android/hardware/ICameraClient.aidl b/core/java/android/hardware/ICameraClient.aidl
new file mode 100644
index 0000000..d7877b4
--- /dev/null
+++ b/core/java/android/hardware/ICameraClient.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/** @hide */
+interface ICameraClient
+{
+ /**
+ * Keep up-to-date with frameworks/av/include/camera/ICameraClient.h
+ */
+ // TODO: consider implementing this.
+}
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
new file mode 100644
index 0000000..2d0c099
--- /dev/null
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.hardware.ICamera;
+import android.hardware.ICameraClient;
+import android.hardware.IProCameraUser;
+import android.hardware.IProCameraCallbacks;
+import android.hardware.photography.ICameraDeviceUser;
+import android.hardware.photography.ICameraDeviceCallbacks;
+import android.hardware.ICameraServiceListener;
+import android.hardware.CameraInfo;
+
+/** @hide */
+interface ICameraService
+{
+ /**
+ * Keep up-to-date with frameworks/av/include/camera/ICameraService.h
+ */
+ int getNumberOfCameras();
+
+ // rest of 'int' return values in this file are actually status_t
+
+ int getCameraInfo(int cameraId, out CameraInfo info);
+
+ ICamera connect(ICameraClient client, int cameraId,
+ String clientPackageName,
+ int clientUid);
+
+ IProCameraUser connectPro(IProCameraCallbacks callbacks, int cameraId,
+ String clientPackageName,
+ int clientUid);
+
+ ICameraDeviceUser connectDevice(ICameraDeviceCallbacks callbacks, int cameraId,
+ String clientPackageName,
+ int clientUid);
+
+ int addListener(ICameraServiceListener listener);
+ int removeListener(ICameraServiceListener listener);
+}
diff --git a/core/java/android/hardware/ICameraServiceListener.aidl b/core/java/android/hardware/ICameraServiceListener.aidl
new file mode 100644
index 0000000..c5484965
--- /dev/null
+++ b/core/java/android/hardware/ICameraServiceListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/** @hide */
+interface ICameraServiceListener
+{
+ /**
+ * Keep up-to-date with frameworks/av/include/camera/ICameraServiceListener.h
+ */
+ void onStatusChanged(int status, int cameraId);
+}
diff --git a/core/java/android/hardware/IProCameraCallbacks.aidl b/core/java/android/hardware/IProCameraCallbacks.aidl
new file mode 100644
index 0000000..a09b452
--- /dev/null
+++ b/core/java/android/hardware/IProCameraCallbacks.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/** @hide */
+interface IProCameraCallbacks
+{
+ /**
+ * Keep up-to-date with frameworks/av/include/camera/IProCameraCallbacks.h
+ */
+ // TODO: consider implementing this.
+}
diff --git a/core/java/android/hardware/IProCameraUser.aidl b/core/java/android/hardware/IProCameraUser.aidl
new file mode 100644
index 0000000..eacb0f4
--- /dev/null
+++ b/core/java/android/hardware/IProCameraUser.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/** @hide */
+interface IProCameraUser
+{
+ /**
+ * Keep up-to-date with frameworks/av/include/camera/IProCameraUser.h
+ */
+ void disconnect();
+}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 5cc1150..c3e9cb7 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -204,37 +204,71 @@
// TODO(): The following arrays are fragile and error-prone. This needs to be refactored.
// Note: This needs to be updated, whenever a new sensor is added.
- private static int[] sSensorReportingModes = {
- REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS,
- REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS,
- REPORTING_MODE_ON_CHANGE, REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS,
- REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ON_CHANGE,
- REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS,
- REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ONE_SHOT };
-
- // Note: This needs to be updated, whenever a new sensor is added.
- // Holds the maximum length of the values array associated with {@link SensorEvent} or
- // {@link TriggerEvent} for the Sensor
- private static int[] sMaxLengthValuesArray = {
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 3,
- 6, 4, 6, 1 };
+ // Holds the reporting mode and maximum length of the values array
+ // associated with
+ // {@link SensorEvent} or {@link TriggerEvent} for the Sensor
+ private static final int[] sSensorReportingModes = {
+ 0, 0, // padding because sensor types start at 1
+ REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_ACCELEROMETER
+ REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_GEOMAGNETIC_FIELD
+ REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_ORIENTATION
+ REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_GYROSCOPE
+ REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_LIGHT
+ REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_PRESSURE
+ REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_TEMPERATURE
+ REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_PROXIMITY
+ REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_GRAVITY
+ REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_LINEAR_ACCELERATION
+ REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_ROTATION_VECTOR
+ REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_RELATIVE_HUMIDITY
+ REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_AMBIENT_TEMPERATURE
+ REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED
+ REPORTING_MODE_CONTINUOUS, 4, // SENSOR_TYPE_GAME_ROTATION_VECTOR
+ REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_GYROSCOPE_UNCALIBRATED
+ REPORTING_MODE_ONE_SHOT, 1, // SENSOR_TYPE_SIGNIFICANT_MOTION
+ // added post 4.3
+ REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_DETECTOR
+ REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_COUNTER
+ REPORTING_MODE_CONTINUOUS, 5 // SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR
+ };
static int getReportingMode(Sensor sensor) {
- // mType starts from offset 1.
- return sSensorReportingModes[sensor.mType - 1];
+ int offset = sensor.mType * 2;
+ if (offset >= sSensorReportingModes.length) {
+ // we don't know about this sensor, so this is probably a
+ // vendor-defined sensor, in that case, we figure out the reporting
+ // mode from the sensor meta-data.
+ int minDelay = sensor.mMinDelay;
+ if (minDelay == 0) {
+ return REPORTING_MODE_ON_CHANGE;
+ } else if (minDelay < 0) {
+ return REPORTING_MODE_ONE_SHOT;
+ } else {
+ return REPORTING_MODE_CONTINUOUS;
+ }
+ }
+ return sSensorReportingModes[offset];
}
static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) {
- // mType starts from offset 1.
- int len = sMaxLengthValuesArray[sensor.mType - 1];
-
+ int type = sensor.mType;
// RotationVector length has changed to 3 to 5 for API level 18
// Set it to 3 for backward compatibility.
- if (sensor.getType() == Sensor.TYPE_ROTATION_VECTOR &&
+ if (type == Sensor.TYPE_ROTATION_VECTOR &&
sdkLevel <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- len = 3;
+ return 3;
}
- return len;
+ int offset = type * 2 + 1;
+ if (offset >= sSensorReportingModes.length) {
+ // we don't know about this sensor, so this is probably a
+ // vendor-defined sensor, in that case, we don't know how many value
+ // it has
+ // so we return the maximum and assume the app will know.
+ // FIXME: sensor HAL should advertise how much data is returned per
+ // sensor
+ return 16;
+ }
+ return sSensorReportingModes[offset];
}
/* Some of these fields are set only by the native bindings in
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 0a7a2e7..9e2e4ba 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -20,6 +20,7 @@
import android.os.Handler;
import android.util.SparseArray;
import android.view.Display;
+import android.view.Surface;
import java.util.ArrayList;
@@ -134,6 +135,7 @@
addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);
addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
}
return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
} finally {
@@ -157,7 +159,7 @@
Display display = mDisplays.get(displayId);
if (display == null) {
display = mGlobal.getCompatibleDisplay(displayId,
- mContext.getCompatibilityInfo(displayId));
+ mContext.getDisplayAdjustments(displayId));
if (display != null) {
mDisplays.put(displayId, display);
}
@@ -275,6 +277,41 @@
}
/**
+ * Creates a private virtual display.
+ * <p>
+ * The content of a virtual display is rendered to a {@link Surface} provided
+ * by the application that created the virtual display.
+ * </p><p>
+ * Only the application that created a private virtual display is allowed to
+ * place windows upon it. The private virtual display also does not participate
+ * in display mirroring: it will neither receive mirrored content from another
+ * display nor allow its own content to be mirrored elsewhere. More precisely,
+ * the only processes that are allowed to enumerate or interact with a private
+ * display are those that have the same UID as the application that originally
+ * created the private virtual display.
+ * </p><p>
+ * The private virtual display should be {@link VirtualDisplay#release released}
+ * when no longer needed. Because a private virtual display renders to a surface
+ * provided by the application, it will be released automatically when the
+ * process terminates and all remaining windows on it will be forcibly removed.
+ * </p>
+ *
+ * @param name The name of the virtual display, must be non-empty.
+ * @param width The width of the virtual display in pixels, must be greater than 0.
+ * @param height The height of the virtual display in pixels, must be greater than 0.
+ * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
+ * @param surface The surface to which the content of the virtual display should
+ * be rendered, must be non-null.
+ * @return The newly created virtual display, or null if the application could
+ * not create the virtual display.
+ */
+ public VirtualDisplay createPrivateVirtualDisplay(String name,
+ int width, int height, int densityDpi, Surface surface) {
+ return mGlobal.createPrivateVirtualDisplay(mContext,
+ name, width, height, densityDpi, surface);
+ }
+
+ /**
* Listens for changes in available display devices.
*/
public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a858681..320185d 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -18,17 +18,20 @@
import android.content.Context;
import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
-import android.view.CompatibilityInfoHolder;
+import android.view.DisplayAdjustments;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
import java.util.ArrayList;
@@ -161,18 +164,18 @@
* Gets information about a logical display.
*
* The display metrics may be adjusted to provide compatibility
- * for legacy applications.
+ * for legacy applications or limited screen areas.
*
* @param displayId The logical display id.
- * @param cih The compatibility info, or null if none is required.
+ * @param daj The compatibility info and activityToken.
* @return The display object, or null if there is no display with the given id.
*/
- public Display getCompatibleDisplay(int displayId, CompatibilityInfoHolder cih) {
+ public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
DisplayInfo displayInfo = getDisplayInfo(displayId);
if (displayInfo == null) {
return null;
}
- return new Display(this, displayId, displayInfo, cih);
+ return new Display(this, displayId, displayInfo, daj);
}
/**
@@ -182,7 +185,18 @@
* @return The display object, or null if there is no display with the given id.
*/
public Display getRealDisplay(int displayId) {
- return getCompatibleDisplay(displayId, null);
+ return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ }
+
+ /**
+ * Gets information about a logical display without applying any compatibility metrics.
+ *
+ * @param displayId The logical display id.
+ * @param IBinder the activity token for this display.
+ * @return The display object, or null if there is no display with the given id.
+ */
+ public Display getRealDisplay(int displayId, IBinder token) {
+ return getCompatibleDisplay(displayId, new DisplayAdjustments(token));
}
public void registerDisplayListener(DisplayListener listener, Handler handler) {
@@ -315,6 +329,53 @@
}
}
+ public VirtualDisplay createPrivateVirtualDisplay(Context context, String name,
+ int width, int height, int densityDpi, Surface surface) {
+ if (TextUtils.isEmpty(name)) {
+ throw new IllegalArgumentException("name must be non-null and non-empty");
+ }
+ if (width <= 0 || height <= 0 || densityDpi <= 0) {
+ throw new IllegalArgumentException("width, height, and densityDpi must be "
+ + "greater than 0");
+ }
+ if (surface == null) {
+ throw new IllegalArgumentException("surface must not be null");
+ }
+
+ Binder token = new Binder();
+ int displayId;
+ try {
+ displayId = mDm.createPrivateVirtualDisplay(token, context.getPackageName(),
+ name, width, height, densityDpi, surface);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Could not create private virtual display: " + name, ex);
+ return null;
+ }
+ if (displayId < 0) {
+ Log.e(TAG, "Could not create private virtual display: " + name);
+ return null;
+ }
+ Display display = getRealDisplay(displayId);
+ if (display == null) {
+ Log.wtf(TAG, "Could not obtain display info for newly created "
+ + "private virtual display: " + name);
+ try {
+ mDm.releaseVirtualDisplay(token);
+ } catch (RemoteException ex) {
+ }
+ return null;
+ }
+ return new VirtualDisplay(this, display, token);
+ }
+
+ public void releaseVirtualDisplay(IBinder token) {
+ try {
+ mDm.releaseVirtualDisplay(token);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to release virtual display.", ex);
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 79aad78..cd4896a 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -20,6 +20,7 @@
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
import android.view.DisplayInfo;
+import android.view.Surface;
/** @hide */
interface IDisplayManager {
@@ -46,4 +47,11 @@
// No permissions required.
WifiDisplayStatus getWifiDisplayStatus();
+
+ // No permissions required.
+ int createPrivateVirtualDisplay(IBinder token, String packageName,
+ String name, int width, int height, int densityDpi, in Surface surface);
+
+ // No permissions required but must be same Uid as the creator.
+ void releaseVirtualDisplay(in IBinder token);
}
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
new file mode 100644
index 0000000..145a217
--- /dev/null
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.display;
+
+import android.os.IBinder;
+import android.view.Display;
+
+/**
+ * Represents a virtual display.
+ *
+ * @see DisplayManager#createPrivateVirtualDisplay
+ */
+public final class VirtualDisplay {
+ private final DisplayManagerGlobal mGlobal;
+ private final Display mDisplay;
+ private IBinder mToken;
+
+ VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token) {
+ mGlobal = global;
+ mDisplay = display;
+ mToken = token;
+ }
+
+ /**
+ * Gets the virtual display.
+ */
+ public Display getDisplay() {
+ return mDisplay;
+ }
+
+ /**
+ * Releases the virtual display and destroys its underlying surface.
+ * <p>
+ * All remaining windows on the virtual display will be forcibly removed
+ * as part of releasing the virtual display.
+ * </p>
+ */
+ public void release() {
+ if (mToken != null) {
+ mGlobal.releaseVirtualDisplay(mToken);
+ mToken = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken + "}";
+ }
+}
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
index 2fd52b8..af5a84e 100644
--- a/core/java/android/hardware/display/WifiDisplay.java
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -33,6 +33,9 @@
private final String mDeviceAddress;
private final String mDeviceName;
private final String mDeviceAlias;
+ private final boolean mIsAvailable;
+ private final boolean mCanConnect;
+ private final boolean mIsRemembered;
public static final WifiDisplay[] EMPTY_ARRAY = new WifiDisplay[0];
@@ -41,7 +44,11 @@
String deviceAddress = in.readString();
String deviceName = in.readString();
String deviceAlias = in.readString();
- return new WifiDisplay(deviceAddress, deviceName, deviceAlias);
+ boolean isAvailable = (in.readInt() != 0);
+ boolean canConnect = (in.readInt() != 0);
+ boolean isRemembered = (in.readInt() != 0);
+ return new WifiDisplay(deviceAddress, deviceName, deviceAlias,
+ isAvailable, canConnect, isRemembered);
}
public WifiDisplay[] newArray(int size) {
@@ -49,7 +56,8 @@
}
};
- public WifiDisplay(String deviceAddress, String deviceName, String deviceAlias) {
+ public WifiDisplay(String deviceAddress, String deviceName, String deviceAlias,
+ boolean available, boolean canConnect, boolean remembered) {
if (deviceAddress == null) {
throw new IllegalArgumentException("deviceAddress must not be null");
}
@@ -60,6 +68,9 @@
mDeviceAddress = deviceAddress;
mDeviceName = deviceName;
mDeviceAlias = deviceAlias;
+ mIsAvailable = available;
+ mCanConnect = canConnect;
+ mIsRemembered = remembered;
}
/**
@@ -88,6 +99,27 @@
}
/**
+ * Returns true if device is available, false otherwise.
+ */
+ public boolean isAvailable() {
+ return mIsAvailable;
+ }
+
+ /**
+ * Returns true if device can be connected to (not in use), false otherwise.
+ */
+ public boolean canConnect() {
+ return mCanConnect;
+ }
+
+ /**
+ * Returns true if device has been remembered, false otherwise.
+ */
+ public boolean isRemembered() {
+ return mIsRemembered;
+ }
+
+ /**
* Gets the name to show in the UI.
* Uses the device alias if available, otherwise uses the device name.
*/
@@ -100,6 +132,10 @@
return o instanceof WifiDisplay && equals((WifiDisplay)o);
}
+ /**
+ * Returns true if the two displays have the same identity (address, name and alias).
+ * This method does not compare the current status of the displays.
+ */
public boolean equals(WifiDisplay other) {
return other != null
&& mDeviceAddress.equals(other.mDeviceAddress)
@@ -127,6 +163,9 @@
dest.writeString(mDeviceAddress);
dest.writeString(mDeviceName);
dest.writeString(mDeviceAlias);
+ dest.writeInt(mIsAvailable ? 1 : 0);
+ dest.writeInt(mCanConnect ? 1 : 0);
+ dest.writeInt(mIsRemembered ? 1 : 0);
}
@Override
@@ -141,6 +180,8 @@
if (mDeviceAlias != null) {
result += ", alias " + mDeviceAlias;
}
+ result += ", isAvailable " + mIsAvailable + ", canConnect " + mCanConnect
+ + ", isRemembered " + mIsRemembered;
return result;
}
}
diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java
index f7e72c4..77acdc0 100644
--- a/core/java/android/hardware/display/WifiDisplayStatus.java
+++ b/core/java/android/hardware/display/WifiDisplayStatus.java
@@ -20,6 +20,8 @@
import android.os.Parcelable;
import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
/**
* Describes the current global state of Wifi display connectivity, including the
@@ -35,8 +37,7 @@
private final int mScanState;
private final int mActiveDisplayState;
private final WifiDisplay mActiveDisplay;
- private final WifiDisplay[] mAvailableDisplays;
- private final WifiDisplay[] mRememberedDisplays;
+ private final WifiDisplay[] mDisplays;
/** Feature state: Wifi display is not available on this device. */
public static final int FEATURE_STATE_UNAVAILABLE = 0;
@@ -70,18 +71,13 @@
activeDisplay = WifiDisplay.CREATOR.createFromParcel(in);
}
- WifiDisplay[] availableDisplays = WifiDisplay.CREATOR.newArray(in.readInt());
- for (int i = 0; i < availableDisplays.length; i++) {
- availableDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in);
- }
-
- WifiDisplay[] rememberedDisplays = WifiDisplay.CREATOR.newArray(in.readInt());
- for (int i = 0; i < rememberedDisplays.length; i++) {
- rememberedDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in);
+ WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < displays.length; i++) {
+ displays[i] = WifiDisplay.CREATOR.createFromParcel(in);
}
return new WifiDisplayStatus(featureState, scanState, activeDisplayState,
- activeDisplay, availableDisplays, rememberedDisplays);
+ activeDisplay, displays);
}
public WifiDisplayStatus[] newArray(int size) {
@@ -91,25 +87,20 @@
public WifiDisplayStatus() {
this(FEATURE_STATE_UNAVAILABLE, SCAN_STATE_NOT_SCANNING, DISPLAY_STATE_NOT_CONNECTED,
- null, WifiDisplay.EMPTY_ARRAY, WifiDisplay.EMPTY_ARRAY);
+ null, WifiDisplay.EMPTY_ARRAY);
}
public WifiDisplayStatus(int featureState, int scanState,
- int activeDisplayState, WifiDisplay activeDisplay,
- WifiDisplay[] availableDisplays, WifiDisplay[] rememberedDisplays) {
- if (availableDisplays == null) {
- throw new IllegalArgumentException("availableDisplays must not be null");
- }
- if (rememberedDisplays == null) {
- throw new IllegalArgumentException("rememberedDisplays must not be null");
+ int activeDisplayState, WifiDisplay activeDisplay, WifiDisplay[] displays) {
+ if (displays == null) {
+ throw new IllegalArgumentException("displays must not be null");
}
mFeatureState = featureState;
mScanState = scanState;
mActiveDisplayState = activeDisplayState;
mActiveDisplay = activeDisplay;
- mAvailableDisplays = availableDisplays;
- mRememberedDisplays = rememberedDisplays;
+ mDisplays = displays;
}
/**
@@ -152,24 +143,12 @@
}
/**
- * Gets the list of all available Wifi displays as reported by the most recent
- * scan, never null.
- * <p>
- * Some of these displays may already be remembered, others may be unknown.
- * </p>
+ * Gets the list of Wifi displays, returns a combined list of all available
+ * Wifi displays as reported by the most recent scan, and all remembered
+ * Wifi displays (not necessarily available at the time).
*/
- public WifiDisplay[] getAvailableDisplays() {
- return mAvailableDisplays;
- }
-
- /**
- * Gets the list of all remembered Wifi displays, never null.
- * <p>
- * Not all remembered displays will necessarily be available.
- * </p>
- */
- public WifiDisplay[] getRememberedDisplays() {
- return mRememberedDisplays;
+ public WifiDisplay[] getDisplays() {
+ return mDisplays;
}
@Override
@@ -185,13 +164,8 @@
dest.writeInt(0);
}
- dest.writeInt(mAvailableDisplays.length);
- for (WifiDisplay display : mAvailableDisplays) {
- display.writeToParcel(dest, flags);
- }
-
- dest.writeInt(mRememberedDisplays.length);
- for (WifiDisplay display : mRememberedDisplays) {
+ dest.writeInt(mDisplays.length);
+ for (WifiDisplay display : mDisplays) {
display.writeToParcel(dest, flags);
}
}
@@ -208,8 +182,7 @@
+ ", scanState=" + mScanState
+ ", activeDisplayState=" + mActiveDisplayState
+ ", activeDisplay=" + mActiveDisplay
- + ", availableDisplays=" + Arrays.toString(mAvailableDisplays)
- + ", rememberedDisplays=" + Arrays.toString(mRememberedDisplays)
+ + ", displays=" + Arrays.toString(mDisplays)
+ "}";
}
}
diff --git a/core/java/android/hardware/photography/CameraAccessException.java b/core/java/android/hardware/photography/CameraAccessException.java
new file mode 100644
index 0000000..fac5086
--- /dev/null
+++ b/core/java/android/hardware/photography/CameraAccessException.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+import android.util.AndroidException;
+
+/**
+ * <p><code>CameraAccessException</code> is thrown if a camera device could not
+ * be queried or opened by the {@link CameraManager}, or if the connection to an
+ * opened {@link CameraDevice} is no longer valid.</p>
+ *
+ * @see CameraManager
+ * @see CameraDevice
+ */
+public class CameraAccessException extends AndroidException {
+ /**
+ * The camera device is in use already
+ */
+ public static final int CAMERA_IN_USE = 1;
+
+ /**
+ * The system-wide limit for number of open cameras has been reached,
+ * and more camera devices cannot be opened until previous instances are
+ * closed.
+ */
+ public static final int MAX_CAMERAS_IN_USE = 2;
+
+ /**
+ * The camera is disabled due to a device policy, and cannot be opened.
+ *
+ * @see android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean)
+ */
+ public static final int CAMERA_DISABLED = 3;
+
+ /**
+ * The camera device is removable and has been disconnected from the Android
+ * device, or the camera service has shut down the connection due to a
+ * higher-priority access request for the camera device.
+ */
+ public static final int CAMERA_DISCONNECTED = 4;
+
+ // Make the eclipse warning about serializable exceptions go away
+ private static final long serialVersionUID = 5630338637471475675L; // randomly generated
+
+ private final int mReason;
+
+ /**
+ * The reason for the failure to access the camera.
+ *
+ * @see #CAMERA_IN_USE
+ * @see #MAX_CAMERAS_IN_USE
+ * @see #CAMERA_DISABLED
+ * @see #CAMERA_DISCONNECTED
+ */
+ public final int getReason() {
+ return mReason;
+ }
+
+ public CameraAccessException(int problem) {
+ super(getDefaultMessage(problem));
+ mReason = problem;
+ }
+
+ public CameraAccessException(int problem, String message) {
+ super(message);
+ mReason = problem;
+ }
+
+ public CameraAccessException(int problem, String message, Throwable cause) {
+ super(message, cause);
+ mReason = problem;
+ }
+
+ public CameraAccessException(int problem, Throwable cause) {
+ super(getDefaultMessage(problem), cause);
+ mReason = problem;
+ }
+
+ private static String getDefaultMessage(int problem) {
+ switch (problem) {
+ case CAMERA_IN_USE:
+ return "The camera device is in use already";
+ case MAX_CAMERAS_IN_USE:
+ return "The system-wide limit for number of open cameras has been reached, " +
+ "and more camera devices cannot be opened until previous instances " +
+ "are closed.";
+ case CAMERA_DISABLED:
+ return "The camera is disabled due to a device policy, and cannot be opened.";
+ case CAMERA_DISCONNECTED:
+ return "The camera device is removable and has been disconnected from the Android" +
+ " device, or the camera service has shut down the connection due to a " +
+ "higher-priority access request for the camera device.";
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/hardware/photography/CameraDevice.java b/core/java/android/hardware/photography/CameraDevice.java
new file mode 100644
index 0000000..0852025
--- /dev/null
+++ b/core/java/android/hardware/photography/CameraDevice.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+import android.view.Surface;
+
+import java.lang.AutoCloseable;
+import java.util.List;
+
+/**
+ * <p>The CameraDevice class is an interface to a single camera connected to an
+ * Android device, allowing for fine-grain control of image capture and
+ * post-processing at high frame rates.</p>
+ *
+ * <p>Your application must declare the
+ * {@link android.Manifest.permission#CAMERA Camera} permission in its manifest
+ * in order to access camera devices.</p>
+ *
+ * <p>A given camera device may provide support at one of two levels: limited or
+ * full. If a device only supports the limited level, then Camera2 exposes a
+ * feature set that is roughly equivalent to the older
+ * {@link android.hardware.Camera Camera} API, although with a cleaner and more
+ * efficient interface. Devices that implement the full level of support
+ * provide substantially improved capabilities over the older camera
+ * API. Applications that target the limited level devices will run unchanged on
+ * the full-level devices; if your application requires a full-level device for
+ * proper operation, declare the "android.hardware.camera2.full" feature in your
+ * manifest.</p>
+ *
+ * @see CameraManager#openCamera
+ * @see android.Manifest.permission#CAMERA
+ */
+public interface CameraDevice extends AutoCloseable {
+
+ /**
+ * Create a request suitable for a camera preview window. Specifically, this
+ * means that high frame rate is given priority over the highest-quality
+ * post-processing. These requests would normally be used with the
+ * {@link #setRepeatingRequest} method.
+ *
+ * @see #createCaptureRequest
+ */
+ public static final int TEMPLATE_PREVIEW = 1;
+
+ /**
+ * Create a request suitable for still image capture. Specifically, this
+ * means prioritizing image quality over frame rate. These requests would
+ * commonly be used with the {@link #capture} method.
+ *
+ * @see #createCaptureRequest
+ */
+ public static final int TEMPLATE_STILL_CAPTURE = 2;
+
+ /**
+ * Create a request suitable for video recording. Specifically, this means
+ * that a stable frame rate is used, and post-processing is set for
+ * recording quality. These requests would commonly be used with the
+ * {@link #setRepeatingRequest} method.
+ *
+ * @see #createCaptureRequest
+ */
+ public static final int TEMPLATE_RECORD = 3;
+
+ /**
+ * Create a request suitable for still image capture while recording
+ * video. Specifically, this means maximizing image quality without
+ * disrupting the ongoing recording. These requests would commonly be used
+ * with the {@link #capture} method while a request based on
+ * {@link #TEMPLATE_RECORD} is is in use with {@link #setRepeatingRequest}.
+ *
+ * @see #createCaptureRequest
+ */
+ public static final int TEMPLATE_VIDEO_SNAPSHOT = 4;
+
+ /**
+ * A basic template for direct application control of capture
+ * parameters. All automatic control is disabled (auto-exposure, auto-white
+ * balance, auto-focus), and post-processing parameters are set to preview
+ * quality. The manual capture parameters (exposure, sensitivity, and so on)
+ * are set to reasonable defaults, but should be overriden by the
+ * application depending on the intended use case.
+ *
+ * @see #createCaptureRequest
+ */
+ public static final int TEMPLATE_MANUAL = 5;
+
+ /**
+ * Get the static properties for this camera. These are identical to the
+ * properties returned by {@link CameraManager#getCameraProperties}.
+ *
+ * @return the static properties of the camera.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected
+ *
+ * @see CameraManager#getCameraProperties
+ */
+ public CameraProperties getProperties() throws CameraAccessException;
+ /**
+ * <p>Set up a new output set of Surfaces for the camera device.</p>
+ *
+ * <p>The configuration 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. This method must be called before requests
+ * can be submitted to the camera with {@link #capture capture},
+ * {@link #captureBurst captureBurst},
+ * {@link #setRepeatingRequest setRepeatingRequest}, or
+ * {@link #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
+ * supported
+ * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes}
+ * before 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 supported
+ * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes}
+ * 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
+ * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes}
+ * </li>
+ *
+ * <li>For recording with {@link android.media.MediaRecorder}: TODO</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 supported
+ * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes}. Then
+ * obtain the Surface with
+ * {@link android.renderscript.Allocation#getSurface}.</li>
+ *
+ * <li>For access to uncompressed, JPEG, or raw sensor data in the
+ * application: Create a {@link android.media.ImageReader} object with the
+ * desired {@link CameraProperties#SCALER_AVAILABLE_FORMATS image format},
+ * and a size from the matching
+ * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed},
+ * {@link CameraProperties#SCALER_AVAILABLE_JPEG_SIZES jpeg}, or
+ * {@link CameraProperties#SCALER_AVAILABLE_RAW_SIZES raw} sizes. Then
+ * obtain a Surface from it.</li>
+ *
+ * </ul>
+ *
+ * </p>
+ *
+ * <p>This function can take several hundred milliseconds to execute, since
+ * camera hardware may need to be powered on or reconfigured.</p>
+ *
+ * <p>To change the configuration after requests have been submitted to the
+ * device, the camera device must be idle. To idle the device, stop any
+ * repeating requests with {@link #stopRepeating stopRepeating}, and then
+ * call {@link #waitUntilIdle waitUntilIdle}.</p>
+ *
+ * <p>Using larger resolution outputs, or more outputs, can result in slower
+ * output rate from the device.</p>
+ *
+ * @param outputs the new set of Surfaces that should be made available as
+ * targets for captured image data.
+ *
+ * @throws IllegalArgumentException if the set of output Surfaces do not
+ * meet the requirements
+ * @throws CameraAccessException if the camera device is no longer connected
+ * @throws IllegalStateException if the camera device is not idle, or has
+ * encountered a fatal error
+ */
+ public void configureOutputs(List<Surface> outputs) throws CameraAccessException;
+
+ /**
+ * <p>Create a {@link CaptureRequest} 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 recommended to reuse the same request for a
+ * different camera device; create a request for that device and override
+ * the settings as desired, instead.</p>
+ *
+ * @param templateType An enumeration selecting the use case for this
+ * request; one of the CameraDevice.TEMPLATE_ values.
+ * @return a filled-in CaptureRequest, except for output streams.
+ *
+ * @throws IllegalArgumentException if the templateType is not in the list
+ * of supported templates.
+ * @throws CameraAccessException if the camera device is no longer connected
+ * @throws IllegalStateException if the camera device has been closed or the
+ * device has encountered a fatal error.
+ *
+ * @see #TEMPLATE_PREVIEW
+ * @see #TEMPLATE_RECORD
+ * @see #TEMPLATE_STILL_CAPTURE
+ * @see #TEMPLATE_VIDEO_SNAPSHOT
+ * @see #TEMPLATE_MANUAL
+ */
+ public CaptureRequest createCaptureRequest(int templateType)
+ throws CameraAccessException;
+
+ /**
+ * <p>Submit a request for an image to be captured by this CameraDevice.</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, as defined by the request's .</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.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected
+ * @throws IllegalStateException if the camera device has been closed or the
+ * device has encountered a fatal error.
+ *
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ */
+ public void capture(CaptureRequest request, CaptureListener listener)
+ throws CameraAccessException;
+
+ /**
+ * <p>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>
+ *
+ * <p>The requests will be captured in order, each capture producing one
+ * {@link CaptureResult} and frames for one or more
+ * target {@link android.view.Surface surfaces}.</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.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected
+ * @throws IllegalStateException if the camera device has been closed or the
+ * device has encountered a fatal error.
+ *
+ * @see #capture
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ */
+ public void captureBurst(List<CaptureRequest> requests,
+ CaptureListener listener) throws CameraAccessException;
+
+ /**
+ * <p>Request endlessly repeating capture of images by this
+ * CameraDevice.</p>
+ *
+ * <p>With this method, the CameraDevice will continually capture
+ * images using the settings in the provided {@link
+ * CaptureRequest}, at the maximum rate possible.</p>
+ *
+ * <p>Repeat requests have lower priority than those submitted
+ * through {@link #capture} or {@link #captureBurst}, so if
+ * 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}</p>
+ *
+ * <p>Calling repeat will replace a burst set up by {@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.
+ *
+ * @throws CameraAccessException if the camera device is no longer
+ * connected
+ * @throws IllegalStateException if the camera device has been closed or the
+ * device has encountered a fatal error.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingBurst
+ */
+ public void setRepeatingRequest(CaptureRequest request, CaptureListener listener)
+ throws CameraAccessException;
+
+ /**
+ * <p>Request endlessly repeating capture of a sequence of images by this
+ * CameraDevice.</p>
+ *
+ * <p>With this method, the CameraDevice 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 submit
+ * requests through {@link #capture} at video rates.</p>
+ *
+ * <p>To stop the repeating capture, call {@link #stopRepeating}. Any
+ * ongoing burst will still be completed, however.</p>
+ *
+ * <p>Calling repeatBurst will replace a repeating request set up by
+ * {@link #setRepeatingRequest}, although any in-progress capture 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.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected
+ * @throws IllegalStateException if the camera device has been closed or the
+ * device has encountered a fatal error.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ */
+ public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener)
+ 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 idle state, use the
+ * {@link #waitUntilIdle waitUntilIdle} method.</p>
+ *
+ * @throws CameraAccessException if the camera device is no longer connected
+ * @throws IllegalStateException if the camera device has been closed or the
+ * device has encountered a fatal error.
+ *
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ * @see #waitUntilIdle
+ *
+ * @throws CameraAccessException if the camera device is no longer connected
+ * @throws IllegalStateException if the camera device has been closed, the
+ * device has encountered a fatal error, or if there is an active repeating
+ * request or burst.
+ */
+ public void stopRepeating() throws CameraAccessException;
+
+ /**
+ * <p>Wait until all the submitted requests have finished processing</p>
+ *
+ * <p>This method blocks until all the requests that have been submitted to
+ * the camera device, either through {@link #capture capture},
+ * {@link #captureBurst captureBurst},
+ * {@link #setRepeatingRequest setRepeatingRequest}, or
+ * {@link #setRepeatingBurst setRepeatingBurst}, have completed their
+ * processing.</p>
+ *
+ * <p>Once this call returns successfully, the device is in an idle state,
+ * and can be reconfigured with {@link #configureOutputs configureOutputs}.</p>
+ *
+ * <p>This method cannot be used if there is an active repeating request or
+ * burst, set with {@link #setRepeatingRequest setRepeatingRequest} or
+ * {@link #setRepeatingBurst setRepeatingBurst}. Call
+ * {@link #stopRepeating stopRepeating} before calling this method.</p>
+ *
+ * @throws CameraAccessException if the camera device is no longer connected
+ * @throws IllegalStateException if the camera device has been closed, the
+ * device has encountered a fatal error, or if there is an active repeating
+ * request or burst.
+ */
+ public void waitUntilIdle() throws CameraAccessException;
+
+ /**
+ * Set the error listener object to call when an asynchronous error
+ * occurs. The errors reported here are only device-wide errors; errors
+ * about individual requests or frames are reported through
+ * {@link CaptureListener#onCaptureFailed}.
+ *
+ * @param listener the ErrorListener to send asynchronous error
+ * notifications to. Setting this to null will stop notifications about
+ * asynchronous errors.
+ */
+ public void setErrorListener(ErrorListener listener);
+
+ /**
+ * Close the connection to this camera device. After this call, all calls to
+ * the camera device interface will throw a {@link IllegalStateException},
+ * except for calls to close().
+ * @throws Exception
+ */
+ @Override
+ public void close() throws Exception;
+ // TODO: We should decide on the behavior of in-flight requests should be on close.
+
+ /**
+ * A listener for receiving metadata about completed image captures. The
+ * metadata includes, among other things, the final capture settings and the
+ * state of the control algorithms.
+ */
+ public interface CaptureListener {
+ /**
+ * <p>Called when a capture request has been processed by a
+ * {@link CameraDevice}.</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 onCaptureComplete(CameraDevice camera,
+ CaptureRequest request, CaptureResult result);
+
+ /**
+ * <p>Called instead of onCaptureComplete when the camera device failed
+ * to produce a CaptureResult for the request. Other requests are
+ * unaffected, and some or all image buffers from the capture may have
+ * been pushed to their respective output streams.</p>
+ *
+ * @param camera The CameraDevice sending the callback.
+ * @param request The request that was given to the CameraDevice
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ */
+ public void onCaptureFailed(CameraDevice camera,
+ CaptureRequest request);
+ }
+
+ /**
+ * <p>A listener for asynchronous errors from the camera device. Errors
+ * about specific {@link CaptureRequest CaptureRequests} are sent through
+ * the capture {@link CaptureListener#onCaptureFailed listener}
+ * interface. Errors reported through this listener affect the device as a
+ * whole.</p>
+ */
+ public interface ErrorListener {
+ /**
+ * <p>This camera device has been disconnected by the camera
+ * service. Any attempt to call methods on this CameraDevice will throw
+ * a {@link CameraAccessException}. The disconnection could be due to a
+ * change in security policy or permissions; the physical disconnection
+ * of a removable camera device; or the camera being needed for a
+ * higher-priority use case.</p>
+ *
+ * <p>There may still be capture completion or camera stream listeners
+ * that will be called after this error is received.</p>
+ */
+ public static final int DEVICE_DISCONNECTED = 1;
+
+ /**
+ * <p>The camera device has encountered a fatal error. Any attempt to
+ * call methods on this CameraDevice will throw a
+ * {@link java.lang.IllegalStateException}.</p>
+ *
+ * <p>There may still be capture completion or camera stream listeners
+ * that will be called after this error is received.</p>
+ *
+ * <p>The application needs to re-open the camera device to use it
+ * again.</p>
+ */
+ public static final int DEVICE_ERROR = 2;
+
+ /**
+ * <p>The camera service has encountered a fatal error. Any attempt to
+ * call methods on this CameraDevice in the future will throw a
+ * {@link java.lang.IllegalStateException}.</p>
+ *
+ * <p>There may still be capture completion or camera stream listeners
+ * that will be called after this error is received.</p>
+ *
+ * <p>The device may need to be shut down and restarted to restore
+ * camera function, or there may be a persistent hardware problem.</p>
+ */
+ public static final int SERVICE_ERROR = 3;
+
+ /**
+ * The method to call when a camera device has encountered an error.
+ *
+ * @param camera The device reporting the error
+ * @param error The error code, one of the ErrorListener.ERROR_ values.
+ */
+ public void onCameraDeviceError(CameraDevice camera, int error);
+ }
+}
diff --git a/core/java/android/hardware/photography/CameraManager.java b/core/java/android/hardware/photography/CameraManager.java
new file mode 100644
index 0000000..c1c9435
--- /dev/null
+++ b/core/java/android/hardware/photography/CameraManager.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+import android.content.Context;
+import android.hardware.ICameraService;
+import android.hardware.ICameraServiceListener;
+import android.hardware.IProCameraUser;
+import android.hardware.photography.utils.CameraBinderDecorator;
+import android.hardware.photography.utils.CameraRuntimeException;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * <p>An interface for iterating, listing, and connecting to
+ * {@link CameraDevice CameraDevices}.</p>
+ *
+ * <p>You can get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(String) Context.getSystemService()}.</p>
+ *
+ * <pre>CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);</pre>
+ *
+ * <p>For more details about communicating with camera devices, read the Camera
+ * developer guide or the {@link android.hardware.photography photography}
+ * package documentation.</p>
+ */
+public final class CameraManager {
+
+ /**
+ * This should match the ICameraService definition
+ */
+ private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
+ private static final int USE_CALLING_UID = -1;
+
+ private final ICameraService mCameraService;
+ private ArrayList<String> mDeviceIdList;
+ private HashSet<CameraListener> mListenerSet;
+ private final Context mContext;
+ private final Object mLock = new Object();
+
+ /**
+ * @hide
+ */
+ public CameraManager(Context context) {
+ mContext = context;
+
+ IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
+ ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+
+ /**
+ * Wrap the camera service in a decorator which automatically translates return codes
+ * into exceptions, and RemoteExceptions into other exceptions.
+ */
+ mCameraService = CameraBinderDecorator.newInstance(cameraServiceRaw);
+
+ try {
+ mCameraService.addListener(new CameraServiceListener());
+ } catch(CameraRuntimeException e) {
+ throw new IllegalStateException("Failed to register a camera service listener",
+ e.asChecked());
+ } catch (RemoteException e) {
+ // impossible
+ }
+ }
+
+ /**
+ * <p>Return the list of currently connected camera devices by
+ * identifier. Non-removable cameras use integers starting at 0 for their
+ * identifiers, while removable cameras have a unique identifier for each
+ * individual device, even if they are the same model.</p>
+ *
+ * @return The list of currently connected camera devices.
+ */
+ public String[] getDeviceIdList() throws CameraAccessException {
+ synchronized (mLock) {
+ try {
+ return getOrCreateDeviceIdListLocked().toArray(new String[0]);
+ } catch(CameraAccessException e) {
+ // this should almost never happen, except if mediaserver crashes
+ throw new IllegalStateException(
+ "Failed to query camera service for device ID list", e);
+ }
+ }
+ }
+
+ /**
+ * Register a listener to be notified about camera device availability.
+ *
+ * Registering a listener more than once has no effect.
+ *
+ * @param listener the new listener to send camera availability notices to.
+ */
+ public void registerCameraListener(CameraListener listener) {
+ synchronized (mLock) {
+ mListenerSet.add(listener);
+ }
+ }
+
+ /**
+ * Remove a previously-added listener; the listener will no longer receive
+ * connection and disconnection callbacks.
+ *
+ * Removing a listener that isn't registered has no effect.
+ *
+ * @param listener the listener to remove from the notification list
+ */
+ public void unregisterCameraListener(CameraListener listener) {
+ synchronized (mLock) {
+ mListenerSet.remove(listener);
+ }
+ }
+
+ /**
+ * <p>Query the capabilities of a camera device. These capabilities are
+ * immutable for a given camera.</p>
+ *
+ * @param cameraId The id of the camera device to query
+ * @return The properties of the given camera
+ *
+ * @throws IllegalArgumentException if the cameraId does not match any
+ * currently connected camera device.
+ * @throws CameraAccessException if the camera is disabled by device policy.
+ * @throws SecurityException if the application does not have permission to
+ * access the camera
+ *
+ * @see #getDeviceIdList
+ * @see android.app.admin.DevicePolicyManager#setCameraDisabled
+ */
+ public CameraProperties getCameraProperties(String cameraId)
+ throws CameraAccessException {
+
+ synchronized (mLock) {
+ if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
+ throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
+ " currently connected camera device", cameraId));
+ }
+ }
+
+ // TODO: implement and call a service function to get the capabilities on C++ side
+
+ // TODO: get properties from service
+ return new CameraProperties();
+ }
+
+ /**
+ * Open a connection to a camera with the given ID. Use
+ * {@link #getDeviceIdList} 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 #getDeviceIdList} and
+ * {@link #openCamera}.
+ *
+ * @param cameraId The unique identifier of the camera device to open
+ *
+ * @throws IllegalArgumentException if the cameraId does not match any
+ * currently connected camera device.
+ * @throws CameraAccessException if the camera is disabled by device policy,
+ * or too many camera devices are already open.
+ * @throws SecurityException if the application does not have permission to
+ * access the camera
+ *
+ * @see #getDeviceIdList
+ * @see android.app.admin.DevicePolicyManager#setCameraDisabled
+ */
+ public CameraDevice openCamera(String cameraId) throws CameraAccessException {
+
+ try {
+
+ synchronized (mLock) {
+
+ ICameraDeviceUser cameraUser;
+
+ android.hardware.photography.impl.CameraDevice device =
+ new android.hardware.photography.impl.CameraDevice(cameraId);
+
+ cameraUser = mCameraService.connectDevice(device.getCallbacks(),
+ Integer.parseInt(cameraId),
+ mContext.getPackageName(), USE_CALLING_UID);
+
+ // TODO: change ICameraService#connectDevice to return status_t
+ if (cameraUser == null) {
+ // TEMPORARY CODE.
+ // catch-all exception since we aren't yet getting the actual error code
+ throw new IllegalStateException("Failed to open camera device");
+ }
+
+ // TODO: factor out listener to be non-nested, then move setter to constructor
+ device.setRemoteDevice(cameraUser);
+
+ return device;
+
+ }
+
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
+ + cameraId);
+ } catch (CameraRuntimeException e) {
+ if (e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) {
+ throw new IllegalArgumentException("Invalid camera ID specified -- " +
+ "perhaps the camera was physically disconnected", e);
+ } else {
+ throw e.asChecked();
+ }
+ } catch (RemoteException e) {
+ // impossible
+ return null;
+ }
+ }
+
+ /**
+ * Interface for listening to cameras becoming available or unavailable.
+ * Cameras become available when they are no longer in use, or when a new
+ * removable camera is connected. They become unavailable when some
+ * application or service starts using a camera, or when a removable camera
+ * is disconnected.
+ */
+ public interface CameraListener {
+ /**
+ * A new camera has become available to use.
+ *
+ * @param cameraId The unique identifier of the new camera.
+ */
+ public void onCameraAvailable(String cameraId);
+
+ /**
+ * A previously-available camera has become unavailable for use. If an
+ * application had an active CameraDevice instance for the
+ * now-disconnected camera, that application will receive a {@link
+ * CameraDevice.ErrorListener#DEVICE_DISCONNECTED disconnection error}.
+ *
+ * @param cameraId The unique identifier of the disconnected camera.
+ */
+ public void onCameraUnavailable(String cameraId);
+ }
+
+ private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
+ if (mDeviceIdList == null) {
+ int numCameras = 0;
+
+ try {
+ numCameras = mCameraService.getNumberOfCameras();
+ } catch(CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return null;
+ }
+
+ mDeviceIdList = new ArrayList<String>();
+ for (int i = 0; i < numCameras; ++i) {
+ // Non-removable cameras use integers starting at 0 for their
+ // identifiers
+ mDeviceIdList.add(String.valueOf(i));
+ }
+
+ }
+ return mDeviceIdList;
+ }
+
+ // TODO: this class needs unit tests
+ // TODO: extract class into top level
+ private class CameraServiceListener extends Binder implements ICameraServiceListener {
+
+ // Keep up-to-date with ICameraServiceListener.h
+
+ // Device physically unplugged
+ public static final int STATUS_NOT_PRESENT = 0;
+ // Device physically has been plugged in
+ // and the camera can be used exclusively
+ public static final int STATUS_PRESENT = 1;
+ // Device physically has been plugged in
+ // but it will not be connect-able until enumeration is complete
+ public static final int STATUS_ENUMERATING = 2;
+ // Camera is in use by another app and cannot be used exclusively
+ public static final int STATUS_NOT_AVAILABLE = 0x80000000;
+
+ // Camera ID -> Status map
+ private final HashMap<String, Integer> mDeviceStatus = new HashMap<String, Integer>();
+
+ private static final String TAG = "CameraServiceListener";
+
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ private boolean isAvailable(int status) {
+ switch (status) {
+ case STATUS_PRESENT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private boolean validStatus(int status) {
+ switch (status) {
+ case STATUS_NOT_PRESENT:
+ case STATUS_PRESENT:
+ case STATUS_ENUMERATING:
+ case STATUS_NOT_AVAILABLE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void onStatusChanged(int status, int cameraId) throws RemoteException {
+ synchronized(CameraManager.this.mLock) {
+
+ Log.v(TAG,
+ String.format("Camera id %d has status changed to 0x%x", cameraId, status));
+
+ String id = String.valueOf(cameraId);
+
+ if (!validStatus(status)) {
+ Log.e(TAG, String.format("Ignoring invalid device %d status 0x%x", cameraId,
+ status));
+ return;
+ }
+
+ Integer oldStatus = mDeviceStatus.put(id, status);
+
+ if (oldStatus == status) {
+ Log.v(TAG, String.format(
+ "Device status changed to 0x%x, which is what it already was",
+ status));
+ return;
+ }
+
+ // TODO: consider abstracting out this state minimization + transition
+ // into a separate
+ // more easily testable class
+ // i.e. (new State()).addState(STATE_AVAILABLE)
+ // .addState(STATE_NOT_AVAILABLE)
+ // .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
+ // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
+ // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
+ // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
+
+ // Translate all the statuses to either 'available' or 'not available'
+ // available -> available => no new update
+ // not available -> not available => no new update
+ if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
+
+ Log.v(TAG,
+ String.format(
+ "Device status was previously available (%d), " +
+ " and is now again available (%d)" +
+ "so no new client visible update will be sent",
+ isAvailable(status), isAvailable(status)));
+ return;
+ }
+
+ for (CameraListener listener : mListenerSet) {
+ if (isAvailable(status)) {
+ listener.onCameraAvailable(id);
+ } else {
+ listener.onCameraUnavailable(id);
+ }
+ } // for
+ } // synchronized
+ } // onStatusChanged
+ } // CameraServiceListener
+} // CameraManager
diff --git a/core/java/android/hardware/photography/CameraMetadata.aidl b/core/java/android/hardware/photography/CameraMetadata.aidl
new file mode 100644
index 0000000..b4dc9ac
--- /dev/null
+++ b/core/java/android/hardware/photography/CameraMetadata.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+/** @hide */
+parcelable CameraMetadata;
diff --git a/core/java/android/hardware/photography/CameraMetadata.java b/core/java/android/hardware/photography/CameraMetadata.java
new file mode 100644
index 0000000..5488952
--- /dev/null
+++ b/core/java/android/hardware/photography/CameraMetadata.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The base class for camera controls and information.
+ *
+ * This class defines the basic key/value map used for querying for camera
+ * characteristics or capture results, and for setting camera request
+ * parameters.
+ *
+ * @see CameraDevice
+ * @see CameraManager
+ * @see CameraProperties
+ **/
+public class CameraMetadata implements Parcelable, AutoCloseable {
+
+ public CameraMetadata() {
+ mMetadataMap = new HashMap<Key<?>, Object>();
+
+ mMetadataPtr = nativeAllocate();
+ if (mMetadataPtr == 0) {
+ throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
+ }
+ }
+
+ public static final Parcelable.Creator<CameraMetadata> CREATOR =
+ new Parcelable.Creator<CameraMetadata>() {
+ @Override
+ public CameraMetadata createFromParcel(Parcel in) {
+ CameraMetadata metadata = new CameraMetadata();
+ metadata.readFromParcel(in);
+ return metadata;
+ }
+
+ @Override
+ public CameraMetadata[] newArray(int size) {
+ return new CameraMetadata[size];
+ }
+ };
+
+ private static final String TAG = "CameraMetadataJV";
+
+ /**
+ * Set a camera metadata field to a value. The field definitions can be
+ * found in {@link CameraProperties}, {@link CaptureResult}, and
+ * {@link CaptureRequest}.
+ *
+ * @param key the metadata field to write.
+ * @param value the value to set the field to, which must be of a matching
+ * type to the key.
+ */
+ public <T> void set(Key<T> key, T value) {
+ Log.e(TAG, "Not fully implemented yet");
+
+ mMetadataMap.put(key, value);
+ }
+
+ /**
+ * Get a camera metadata field value. The field definitions can be
+ * found in {@link CameraProperties}, {@link CaptureResult}, and
+ * {@link CaptureRequest}.
+ *
+ * @param key the metadata field to read.
+ * @return the value of that key, or {@code null} if the field is not set.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(Key<T> key) {
+ Log.e(TAG, "Not fully implemented yet");
+
+ return (T) mMetadataMap.get(key);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ nativeWriteToParcel(dest);
+ }
+
+ /**
+ * Expand this object from a Parcel.
+ * @param in The Parcel from which the object should be read
+ */
+ public void readFromParcel(Parcel in) {
+ nativeReadFromParcel(in);
+ }
+
+ public static class Key<T> {
+ public Key(String name) {
+ if (name == null) {
+ throw new NullPointerException("Key needs a valid name");
+ }
+ mName = name;
+ }
+
+ public final String getName() {
+ return mName;
+ }
+
+ @Override
+ public final int hashCode() {
+ return mName.hashCode();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ 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);
+ }
+
+ private final String mName;
+ }
+
+ private final Map<Key<?>, Object> mMetadataMap;
+
+ /**
+ * @hide
+ */
+ private long mMetadataPtr; // native CameraMetadata*
+
+ private native long nativeAllocate();
+ private native synchronized void nativeWriteToParcel(Parcel dest);
+ private native synchronized void nativeReadFromParcel(Parcel source);
+ private native synchronized void nativeSwap(CameraMetadata other) throws NullPointerException;
+ private native synchronized void nativeClose();
+ private native synchronized boolean nativeIsEmpty();
+ private native synchronized int nativeGetEntryCount();
+ private static native void nativeClassInit();
+
+ /**
+ * <p>Perform a 0-copy swap of the internal metadata with another object.</p>
+ *
+ * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p>
+ *
+ * @param other metadata to swap with
+ * @throws NullPointerException if other was null
+ * @hide
+ */
+ public void swap(CameraMetadata other) {
+ nativeSwap(other);
+ }
+
+ /**
+ * @hide
+ */
+ public int getEntryCount() {
+ return nativeGetEntryCount();
+ }
+
+ /**
+ * Does this metadata contain at least 1 entry?
+ *
+ * @hide
+ */
+ public boolean isEmpty() {
+ return nativeIsEmpty();
+ }
+
+ /**
+ * <p>Closes this object, and releases all native resources associated with it.</p>
+ *
+ * <p>Calling any other public method after this will result in an IllegalStateException
+ * being thrown.</p>
+ */
+ @Override
+ public void close() throws Exception {
+ // this sets mMetadataPtr to 0
+ nativeClose();
+ mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
+ }
+
+ /**
+ * Whether or not {@link #close} has already been called (at least once) on this object.
+ * @hide
+ */
+ public boolean isClosed() {
+ synchronized (this) {
+ return mMetadataPtr == 0;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * We use a class initializer to allow the native code to cache some field offsets
+ */
+ static {
+ System.loadLibrary("media_jni");
+ nativeClassInit();
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/photography/CameraProperties.java b/core/java/android/hardware/photography/CameraProperties.java
new file mode 100644
index 0000000..ad42285
--- /dev/null
+++ b/core/java/android/hardware/photography/CameraProperties.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+import android.graphics.Rect;
+
+import java.util.List;
+
+/**
+ * <p>The properties describing a
+ * {@link CameraDevice CameraDevice}.</p>
+ *
+ * <p>These properties are fixed for a given CameraDevice, and can be queried
+ * through the {@link CameraManager CameraManager}
+ * interface in addition to through the CameraDevice interface.</p>
+ *
+ * @see CameraDevice
+ * @see CameraManager
+ */
+public final class CameraProperties extends CameraMetadata {
+ /**
+ * The model name of the camera. For fixed (non-removable) cameras, this is
+ * the manufacturer's name. For removable cameras, this is a string that
+ * uniquely identifies the camera model and manufacturer. The
+ * {@link #INFO_IDENTIFIER} can be used to distinguish between multiple
+ * removable cameras of the same model.
+ */
+ public static final Key<String> INFO_MODEL =
+ new Key<String>("android.info.model");
+
+ /**
+ * A unique identifier for this camera. For removable cameras, every
+ * camera will have a unique identifier, including two cameras of the
+ * same model and manufacturer. For non-removable cameras, the
+ * identifier is equal to the the device's id.
+ */
+ public static final Key<String> INFO_IDENTIFIER =
+ new Key<String>("android.info.identifier");
+
+ /**
+ * <p>Whether this camera is removable or not.</p>
+ *
+ * <p>Applications using a removable camera must be prepared for the camera
+ * to be disconnected during use. Use the {@link #INFO_IDENTIFIER} field to
+ * determine if this camera is a match for a camera device seen earlier.</p>
+ */
+ public static final Key<Boolean> INFO_REMOVABLE =
+ new Key<Boolean>("android.info.isRemovable");
+
+ /**
+ * <p>The hardware operational model of this device. One of the
+ * INFO_SUPPORTED_HARDWARE_LEVEL_* constants.</p>
+ *
+ * <p>Limited-capability devices have a number of limitations relative to
+ * full-capability cameras. Roughly, they have capabilities comparable to
+ * those provided by the deprecated {@link android.hardware.Camera}
+ * class.</p>
+ *
+ * <p>Specifically, limited-mode devices:</p>
+ *
+ * <ol>
+ *
+ * <li>Do not provide per-frame result metadata for repeating
+ * captures. This means that a CaptureListener will not be called for
+ * captures done through {@link CameraDevice#setRepeatingRequest
+ * setRepeatingRequest} or {@link CameraDevice#setRepeatingBurst
+ * setRepeatingBurst}.</li>
+ *
+ * <li>Do not support complete result metadata. Only a few fields are
+ * provided, such as the timestamp (TODO).</li>
+ *
+ * <li>Do not support manual capture controls. Only the (TODO)
+ * ANDROID_CONTROL_* fields and TODO controls are used, and the various
+ * AE/AF/AWB_OFF control values cannot be used.</li>
+ *
+ * <li>Do not support high frame rate captures. To obtain high frame rate,
+ * the {@link CameraDevice#setRepeatingRequest setRepeatingRequest} method
+ * must be used. The {@link CameraDevice#capture capture},
+ * {@link CameraDevice#captureBurst captureBurst}, and
+ * {@link CameraDevice#setRepeatingBurst setRepeatingBurst} methods will
+ * result in slow frame captures.</li>
+ *
+ * </ol>
+ *
+ * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ */
+ public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
+ new Key<Integer>("android.info.supportedHardwareLevel");
+
+ /**
+ * <p>The type reported by limited-capability camera devices.</p>
+ *
+ * <p>Limited-capability devices have a number of limitations relative to
+ * full-capability cameras. Roughly, they have capabilities comparable to
+ * those provided by the deprecated {@link android.hardware.Camera}
+ * class.</p>
+ *
+ * <p>Specifically, limited-mode devices:</p>
+ *
+ * <ol>
+ *
+ * <li>Do not provide per-frame result metadata for repeating
+ * captures. This means that a CaptureListener will not be called for
+ * captures done through {@link CameraDevice#setRepeatingRequest
+ * setRepeatingRequest} or {@link CameraDevice#setRepeatingBurst
+ * setRepeatingBurst}.</li>
+ *
+ * <li>Do not support complete result metadata. Only a few fields are
+ * provided, such as the timestamp (TODO).</li>
+ *
+ * <li>Do not support manual capture controls. Only the (TODO)
+ * ANDROID_CONTROL_* fields and TODO controls are used, and the various
+ * AE/AF/AWB_OFF control values cannot be used.</li>
+ *
+ * <li>Do not support high frame rate captures. To obtain high frame rate,
+ * the {@link CameraDevice#setRepeatingRequest setRepeatingRequest} method
+ * must be used. The {@link CameraDevice#capture capture},
+ * {@link CameraDevice#captureBurst captureBurst}, and
+ * {@link CameraDevice#setRepeatingBurst setRepeatingBurst} methods will
+ * result in slow frame captures.</li>
+ *
+ * </ol>
+ *
+ * @see #INFO_SUPPORTED_HARDWARE_LEVEL
+ */
+ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0;
+
+ /**
+ * <p>The type reported by full-capability camera devices</p>
+ *
+ * <p>Full-capability devices allow for per-frame control of capture
+ * hardware and post-processing parameters at high frame rates. They also
+ * provide output data at high resolution in uncompressed formats, in
+ * addition to compressed JPEG output.</p>
+ *
+ * @see #INFO_SUPPORTED_HARDWARE_LEVEL
+ */
+ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1;
+
+ /**
+ * <p>The available output formats from this camera device. When using a
+ * {@link android.media.ImageReader} as an output target, the
+ * ImageReader must be configured to use one of these formats.</p>
+ *
+ * <p>The list is a subset of the formats defined in
+ * {@link android.graphics.ImageFormat}.</p>
+ *
+ * <p>The image formats {@link android.graphics.ImageFormat#JPEG},
+ * {@link android.graphics.ImageFormat#YUV_420_888} are guaranteed to be
+ * supported.</p>
+ */
+ public static final Key<Integer[]> SCALER_AVAILABLE_FORMATS =
+ new Key<Integer[]>("android.scaler.availableFormats");
+
+ /**
+ * <p>The available output sizes for JPEG buffers from this camera
+ * device. When using a {@link android.media.ImageReader} as an output
+ * target, the ImageReader must be configured to use one of these sizes
+ * when using format {@link android.graphics.ImageFormat#JPEG}.</p>
+ */
+ public static final Key<Size[]> SCALER_AVAILABLE_JPEG_SIZES =
+ new Key<Size[]>("android.scaler.availableJpegSizes");
+
+ /**
+ * <p>The available sizes for output buffers from this camera device, when
+ * the buffers are neither of the {@link android.graphics.ImageFormat#JPEG}
+ * or of the {@link android.graphics.ImageFormat#RAW_SENSOR} type.</p>
+ *
+ * <p>When using a {@link android.view.SurfaceView},
+ * {@link android.graphics.SurfaceTexture},
+ * {@link android.media.MediaRecorder}, {@link android.media.MediaCodec}, or
+ * {@link android.renderscript.Allocation} as an output target, that target
+ * must be configured to one of these sizes. See
+ * {@link CameraDevice#configureOutputs} for details.
+ *
+ * <p>When using a {@link android.media.ImageReader} as an output
+ * target, the ImageReader must be configured to use one of these sizes
+ * when using format {@link android.graphics.ImageFormat#YUV_420_888}.</p>
+ *
+ */
+ public static final Key<Size[]> SCALER_AVAILABLE_PROCESSED_SIZES =
+ new Key<Size[]>("android.scaler.availableProcessedSizes");
+
+ /**
+ * <p>The available sizes for output buffers from this camera device, when
+ * the buffers are of the {@link android.graphics.ImageFormat#RAW_SENSOR} type. This type of output may not be
+ * supported by the device; check {@link #SCALER_AVAILABLE_FORMATS} to
+ * check. In that case, this list will not exist.</p>
+ *
+ * <p>When using a {@link android.media.ImageReader} as an output
+ * target, the ImageReader must be configured to use one of these sizes
+ * when using image format {@link android.graphics.ImageFormat#RAW_SENSOR}.</p>
+ */
+ public static final Key<Size[]> SCALER_AVAILABLE_RAW_SIZES =
+ new Key<Size[]>("android.scaler.availableRawSizes");
+
+ /**
+ * <p>The coordinates of the sensor's active pixel array, relative to its
+ * total pixel array. These are the pixels that are actually used for image
+ * capture. The active pixel region may be smaller than the total number of
+ * pixels, if some pixels are used for other tasks such as calibrating the
+ * sensor's black level. If all pixels available for readout are used for
+ * imaging, then this rectangle will be
+ * {@code (0,0) - (SENSOR_PIXEL_ARRAY_SIZE.width,
+ * SENSOR_PIXEL_ARRAY_SIZE.height)}.</p>
+ *
+ * <p>If raw sensor capture is supported by this device, the width and
+ * height of the active pixel array match up to one of the supported raw
+ * capture sizes, and using that size will capture just the active pixel
+ * array region.</p>
+ *
+ * <p>Most other coordinates used by the camera device (for example, for
+ * metering and crop region selection, or for reporting detected faces) use
+ * a coordinate system based on the active array dimensions, with (0,0)
+ * being the top-left corner of the active array.</p>
+ */
+ public static final Key<Rect> SENSOR_ACTIVE_ARRAY_SIZE =
+ new Key<Rect>("android.sensor.activeArraySize");
+
+ /**
+ * <p>The size of the sensor's total pixel array available for readout. Some
+ * of these pixels may not be used for image capture, in which case
+ * {@link #SENSOR_ACTIVE_ARRAY_SIZE} will describe a rectangle smaller than
+ * this. If raw sensor capture is supported by this device, this is one of
+ * the supported capture sizes.</p>
+ */
+ public static final Key<Size> SENSOR_PIXEL_ARRAY_SIZE =
+ new Key<Size>("android.sensor.activeArraySize");
+
+ // TODO: Many more of these.
+
+}
diff --git a/core/java/android/hardware/photography/CaptureRequest.aidl b/core/java/android/hardware/photography/CaptureRequest.aidl
new file mode 100644
index 0000000..64fb6f2
--- /dev/null
+++ b/core/java/android/hardware/photography/CaptureRequest.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+/** @hide */
+parcelable CaptureRequest;
diff --git a/core/java/android/hardware/photography/CaptureRequest.java b/core/java/android/hardware/photography/CaptureRequest.java
new file mode 100644
index 0000000..ac2041b
--- /dev/null
+++ b/core/java/android/hardware/photography/CaptureRequest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.Surface;
+
+import java.util.HashSet;
+
+
+/**
+ * <p>All the settings required to capture a single image from the image sensor.</p>
+ *
+ * <p>Contains the configuration for the capture hardware (sensor, lens, flash),
+ * the processing pipeline, the control algorithms, and the output buffers.</p>
+ *
+ * <p>CaptureRequests can be created by calling
+ * {@link CameraDevice#createCaptureRequest}</p>
+ *
+ * <p>CaptureRequests are given to {@link CameraDevice#capture} or
+ * {@link CameraDevice#setRepeatingRequest} to capture images from a camera.</p>
+ *
+ * <p>Each request can specify a different subset of target Surfaces for the
+ * camera to send the captured data to. All the surfaces used in a request must
+ * be part of the surface list given to the last call to
+ * {@link CameraDevice#configureOutputs}.</p>
+ *
+ * <p>For example, a request meant for repeating preview might only include the
+ * Surface for the preview SurfaceView or SurfaceTexture, while a
+ * high-resolution still capture would also include a Surface from a ImageReader
+ * configured for high-resolution JPEG images.</p>
+ *
+ * @see CameraDevice#capture
+ * @see CameraDevice#setRepeatingRequest
+ * @see CameraDevice#createRequest
+ */
+public final class CaptureRequest extends CameraMetadata implements Parcelable {
+
+ private final Object mLock = new Object();
+ private final HashSet<Surface> mSurfaceSet = new HashSet<Surface>();
+
+ /**
+ * The exposure time for this capture, in nanoseconds.
+ */
+ public static final Key<Long> SENSOR_EXPOSURE_TIME =
+ new Key<Long>("android.sensor.exposureTime");
+
+ /**
+ * The sensor sensitivity (gain) setting for this camera.
+ * This is represented as an ISO sensitivity value
+ */
+ public static final Key<Integer> SENSOR_SENSITIVITY =
+ new Key<Integer>("android.sensor.sensitivity");
+
+ // Many more settings
+
+ /**
+ * @hide
+ */
+ public CaptureRequest() {
+ }
+
+ /**
+ * <p>Add a surface to the list of targets for this request</p>
+ *
+ * <p>The Surface added must be one of the surfaces included in the last
+ * call to {@link CameraDevice#configureOutputs}.</p>
+ *
+ * <p>Adding a target more than once has no effect.</p>
+ *
+ * @param outputTarget surface to use as an output target for this request
+ */
+ public void addTarget(Surface outputTarget) {
+ synchronized (mLock) {
+ mSurfaceSet.add(outputTarget);
+ }
+ }
+
+ /**
+ * <p>Remove a surface from the list of targets for this request.</p>
+ *
+ * <p>Removing a target that is not currently added has no effect.</p>
+ *
+ * @param outputTarget surface to use as an output target for this request
+ */
+ public void removeTarget(Surface outputTarget) {
+ synchronized (mLock) {
+ mSurfaceSet.remove(outputTarget);
+ }
+ }
+
+ public static final Parcelable.Creator<CaptureRequest> CREATOR =
+ new Parcelable.Creator<CaptureRequest>() {
+ @Override
+ public CaptureRequest createFromParcel(Parcel in) {
+ CaptureRequest request = new CaptureRequest();
+ request.readFromParcel(in);
+ return request;
+ }
+
+ @Override
+ public CaptureRequest[] newArray(int size) {
+ return new CaptureRequest[size];
+ }
+ };
+
+ /**
+ * Expand this object from a Parcel.
+ * @param in The parcel from which the object should be read
+ */
+ @Override
+ public void readFromParcel(Parcel in) {
+ synchronized (mLock) {
+ super.readFromParcel(in);
+
+ mSurfaceSet.clear();
+
+ Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader());
+
+ if (parcelableArray == null) {
+ return;
+ }
+
+ for (Parcelable p : parcelableArray) {
+ Surface s = (Surface) p;
+ mSurfaceSet.add(s);
+ }
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ synchronized (mLock) {
+ super.writeToParcel(dest, flags);
+
+ dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/photography/CaptureResult.java b/core/java/android/hardware/photography/CaptureResult.java
new file mode 100644
index 0000000..b502c4c
--- /dev/null
+++ b/core/java/android/hardware/photography/CaptureResult.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+
+/**
+ * <p>The results of a single image capture from the image sensor.</p>
+ *
+ * <p>Contains the final configuration for the capture hardware (sensor, lens,
+ * flash), the processing pipeline, the control algorithms, and the output
+ * buffers.</p>
+ *
+ * <p>CaptureResults are produced by a {@link CameraDevice} after processing a
+ * {@link CaptureRequest}. All properties listed for capture requests can also
+ * be queried on the capture result, to determine the final values used for
+ * capture. The result also includes additional metadata about the state of the
+ * camera device during the capture.</p>
+ *
+ */
+public final class CaptureResult extends CameraMetadata {
+
+ /**
+ * The timestamp representing the start of image capture, in nanoseconds.
+ * This corresponds to the timestamp available through
+ * {@link android.graphics.SurfaceTexture#getTimestamp SurfaceTexture.getTimestamp()}
+ * or {@link android.media.Image#getTimestamp Image.getTimestamp()} for this
+ * capture's image data.
+ */
+ public static final Key SENSOR_TIMESTAMP =
+ new Key<Long>("android.sensor.timestamp");
+
+ /**
+ * The state of the camera device's auto-exposure algorithm. One of the
+ * CONTROL_AE_STATE_* enumerations.
+ */
+ public static final Key CONTROL_AE_STATE =
+ new Key<Integer>("android.control.aeState");
+
+ /**
+ * The auto-exposure algorithm is inactive.
+ * @see CONTROL_AE_STATE
+ */
+ public static final int CONTROL_AE_STATE_INACTIVE = 0;
+
+ /**
+ * The auto-exposure algorithm is currently searching for proper exposure.
+ * @see CONTROL_AE_STATE
+ */
+ public static final int CONTROL_AE_STATE_SEARCHING = 1;
+
+ /**
+ * The auto-exposure algorithm has reached proper exposure values for the
+ * current scene.
+ * @see CONTROL_AE_STATE
+ */
+ public static final int CONTROL_AE_STATE_CONVERGED = 2;
+
+ /**
+ * The auto-exposure algorithm has been locked to its current values.
+ * @see CONTROL_AE_STATE
+ */
+ public static final int CONTROL_AE_STATE_LOCKED = 3;
+
+ /**
+ * The auto-exposure algorithm has reached proper exposure values as with
+ * CONTROL_AE_STATE_CONVERGED, but the scene is too dark to take a good
+ * quality image without firing the camera flash.
+ * @see CONTROL_AE_STATE
+ */
+ public static final int CONTROL_AE_STATE_FLASH_REQUIRED = 4;
+
+ /**
+ * The precapture sequence of the auto-exposure algorithm has been triggered,
+ * and is underway.
+ * @see CONTROL_AE_STATE
+ */
+ public static final int CONTROL_AE_STATE_PRECAPTURE =5;
+
+ /**
+ * The list of faces detected in this capture. Available if face detection
+ * was enabled for this capture
+ */
+ public static final Key STATISTICS_DETECTED_FACES =
+ new Key<Face[]>("android.statistics.faces");
+
+ // TODO: Many many more
+
+ /**
+ * @hide
+ */
+ public CaptureResult() {
+ }
+
+ /**
+ * Describes a face detected in an image.
+ */
+ public static class Face {
+
+ /**
+ * <p>Bounds of the face. A rectangle relative to the sensor's
+ * {@link CameraProperties#SENSOR_ACTIVE_ARRAY_SIZE}, with (0,0)
+ * representing the top-left corner of the active array rectangle.</p>
+ */
+ public Rect getBounds() {
+ return mBounds;
+ }
+
+ /* <p>The confidence level for the detection of the face. The range is 1 to
+ * 100. 100 is the highest confidence.</p>
+ *
+ * <p>Depending on the device, even very low-confidence faces may be
+ * listed, so applications should filter out faces with low confidence,
+ * depending on the use case. For a typical point-and-shoot camera
+ * application that wishes to display rectangles around detected faces,
+ * filtering out faces with confidence less than 50 is recommended.</p>
+ *
+ */
+ public int getScore() {
+ return mScore;
+ }
+
+ /**
+ * An unique id per face while the face is visible to the tracker. If
+ * the face leaves the field-of-view and comes back, it will get a new
+ * id. This is an optional field, may not be supported on all devices.
+ * If not supported, id will always be set to -1. The optional fields
+ * are supported as a set. Either they are all valid, or none of them
+ * are.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * The coordinates of the center of the left eye. The coordinates are in
+ * the same space as the ones for {@link #getBounds}. This is an
+ * optional field, may not be supported on all devices. If not
+ * supported, the value will always be set to null. The optional fields
+ * are supported as a set. Either they are all valid, or none of them
+ * are.
+ */
+ public Point getLeftEye() {
+ return mLeftEye;
+ }
+
+ /**
+ * The coordinates of the center of the right eye. The coordinates are
+ * in the same space as the ones for {@link #getBounds}.This is an
+ * optional field, may not be supported on all devices. If not
+ * supported, the value will always be set to null. The optional fields
+ * are supported as a set. Either they are all valid, or none of them
+ * are.
+ */
+ public Point getRightEye() {
+ return mRightEye;
+ }
+
+ /**
+ * The coordinates of the center of the mouth. The coordinates are in
+ * the same space as the ones for {@link #getBounds}. This is an optional
+ * field, may not be supported on all devices. If not supported, the
+ * value will always be set to null. The optional fields are supported
+ * as a set. Either they are all valid, or none of them are.
+ */
+ public Point getMouth() {
+ return mMouth;
+ }
+
+ private Rect mBounds;
+ private int mScore;
+ private int mId;
+ private Point mLeftEye;
+ private Point mRightEye;
+ private Point mMouth;
+ }
+}
diff --git a/core/java/android/hardware/photography/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/photography/ICameraDeviceCallbacks.aidl
new file mode 100644
index 0000000..c506800
--- /dev/null
+++ b/core/java/android/hardware/photography/ICameraDeviceCallbacks.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+import android.hardware.photography.CameraMetadata;
+
+/** @hide */
+interface ICameraDeviceCallbacks
+{
+ /**
+ * Keep up-to-date with frameworks/av/include/camera/photography/ICameraDeviceCallbacks.h
+ */
+
+ void notifyCallback(int msgType, int ext1, int ext2);
+ void onResultReceived(int frameId, in CameraMetadata result);
+}
diff --git a/core/java/android/hardware/photography/ICameraDeviceUser.aidl b/core/java/android/hardware/photography/ICameraDeviceUser.aidl
new file mode 100644
index 0000000..d1310fb
--- /dev/null
+++ b/core/java/android/hardware/photography/ICameraDeviceUser.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+import android.view.Surface;
+import android.hardware.photography.CameraMetadata;
+import android.hardware.photography.CaptureRequest;
+
+/** @hide */
+interface ICameraDeviceUser
+{
+ /**
+ * Keep up-to-date with frameworks/av/include/camera/photography/ICameraDeviceUser.h
+ */
+ void disconnect();
+
+ // ints here are status_t
+
+ // non-negative value is the requestId. negative value is status_t
+ int submitRequest(in CaptureRequest request, boolean streaming);
+
+ int cancelRequest(int requestId);
+
+ int deleteStream(int streamId);
+
+ // non-negative value is the stream ID. negative value is status_t
+ int createStream(int width, int height, int format, in Surface surface);
+
+ int createDefaultRequest(int templateId, out CameraMetadata request);
+}
diff --git a/core/java/android/hardware/photography/Size.java b/core/java/android/hardware/photography/Size.java
new file mode 100644
index 0000000..e1115d3
--- /dev/null
+++ b/core/java/android/hardware/photography/Size.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography;
+
+/**
+ * A simple immutable class for describing the dimensions of camera image
+ * buffers.
+ */
+public final class Size {
+ /**
+ * Create a new immutable Size instance
+ *
+ * @param w The width to store in the Size instance
+ * @param h The height to store in the Size instance
+ */
+ Size(int w, int h) {
+ mWidth = w;
+ mHeight = h;
+ }
+
+ public final int getWidth() {
+ return mWidth;
+ }
+
+ public final int getHeight() {
+ return mHeight;
+ }
+
+ private final int mWidth;
+ private final int mHeight;
+};
diff --git a/core/java/android/hardware/photography/impl/CameraDevice.java b/core/java/android/hardware/photography/impl/CameraDevice.java
new file mode 100644
index 0000000..b1e3f6a
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/CameraDevice.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography.impl;
+
+import android.hardware.photography.CameraMetadata;
+import android.hardware.photography.CaptureResult;
+import android.hardware.photography.ICameraDeviceUser;
+import android.hardware.photography.ICameraDeviceCallbacks;
+import android.hardware.photography.CameraAccessException;
+import android.hardware.photography.CameraProperties;
+import android.hardware.photography.CaptureRequest;
+import android.hardware.photography.utils.CameraRuntimeException;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * HAL2.1+ implementation of CameraDevice Use CameraManager#open to instantiate
+ */
+public class CameraDevice implements android.hardware.photography.CameraDevice {
+
+ private final String TAG;
+
+ // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
+ private ICameraDeviceUser mRemoteDevice;
+
+ private final Object mLock = new Object();
+ private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
+
+ // XX: Make this a WeakReference<CaptureListener> ?
+ private final HashMap<Integer, CaptureListenerHolder> mCaptureListenerMap =
+ new HashMap<Integer, CaptureListenerHolder>();
+
+ private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
+
+ private final String mCameraId;
+
+ public CameraDevice(String cameraId) {
+ mCameraId = cameraId;
+ TAG = String.format("CameraDevice-%s-JV", mCameraId);
+ }
+
+ public CameraDeviceCallbacks getCallbacks() {
+ return mCallbacks;
+ }
+
+ /**
+ * @hide
+ */
+ public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
+ mRemoteDevice = remoteDevice;
+ }
+
+ @Override
+ public CameraProperties getProperties() throws CameraAccessException {
+ // TODO
+ Log.v(TAG, "TODO: Implement getProperties");
+ return new CameraProperties();
+ }
+
+ @Override
+ public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
+ synchronized (mLock) {
+ // TODO: delete outputs that aren't in this list that were configured previously
+ for (Surface s : outputs) {
+ try {
+ // TODO: remove width,height,format since we are ignoring
+ // it.
+ mRemoteDevice.createStream(0, 0, 0, s);
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public CaptureRequest createCaptureRequest(int templateType) throws CameraAccessException {
+
+ synchronized (mLock) {
+
+ CameraMetadata templatedRequest = new CameraMetadata();
+
+ try {
+ mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return null;
+ }
+
+ CaptureRequest request = new CaptureRequest();
+
+ // XX: could also change binder signature but that's more work than
+ // just using swap.
+ request.swap(templatedRequest);
+
+ return request;
+
+ }
+ }
+
+ @Override
+ public void capture(CaptureRequest request, CaptureListener listener)
+ throws CameraAccessException {
+ submitCaptureRequest(request, listener, /*streaming*/false);
+ }
+
+ @Override
+ public void captureBurst(List<CaptureRequest> requests, CaptureListener listener)
+ throws CameraAccessException {
+ // TODO
+ throw new UnsupportedOperationException("Burst capture implemented yet");
+
+ }
+
+ private void submitCaptureRequest(CaptureRequest request, CaptureListener listener,
+ boolean repeating) throws CameraAccessException {
+
+ synchronized (mLock) {
+
+ int requestId;
+
+ try {
+ requestId = mRemoteDevice.submitRequest(request, repeating);
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return;
+ }
+
+ mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
+ repeating));
+
+ if (repeating) {
+ mRepeatingRequestIdStack.add(requestId);
+ }
+
+ }
+ }
+
+ @Override
+ public void setRepeatingRequest(CaptureRequest request, CaptureListener listener)
+ throws CameraAccessException {
+ submitCaptureRequest(request, listener, /*streaming*/true);
+ }
+
+ @Override
+ public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener)
+ throws CameraAccessException {
+ // TODO
+ throw new UnsupportedOperationException("Burst capture implemented yet");
+ }
+
+ @Override
+ public void stopRepeating() throws CameraAccessException {
+
+ synchronized (mLock) {
+
+ while (!mRepeatingRequestIdStack.isEmpty()) {
+ int requestId = mRepeatingRequestIdStack.pop();
+
+ try {
+ mRemoteDevice.cancelRequest(requestId);
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void waitUntilIdle() throws CameraAccessException {
+ // TODO: implement
+ }
+
+ @Override
+ public void setErrorListener(ErrorListener listener) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ // TODO: every method should throw IllegalStateException after close has been called
+
+ synchronized (mLock) {
+
+ try {
+ mRemoteDevice.disconnect();
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ }
+
+ mRemoteDevice = null;
+
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } catch (CameraRuntimeException e) {
+ Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
+ }
+ finally {
+ super.finalize();
+ }
+ }
+
+ static class CaptureListenerHolder {
+
+ private final boolean mRepeating;
+ private final CaptureListener mListener;
+ private final CaptureRequest mRequest;
+
+ CaptureListenerHolder(CaptureListener listener, CaptureRequest request, boolean repeating) {
+ mRepeating = repeating;
+ mRequest = request;
+ mListener = listener;
+ }
+
+ public boolean isRepeating() {
+ return mRepeating;
+ }
+
+ public CaptureListener getListener() {
+ return mListener;
+ }
+
+ public CaptureRequest getRequest() {
+ return mRequest;
+ }
+ }
+
+ // TODO: unit tests
+ public class CameraDeviceCallbacks extends Binder implements ICameraDeviceCallbacks {
+
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ // TODO: consider rename to onMessageReceived
+ @Override
+ public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
+ Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2);
+ // TODO implement rest
+ }
+
+ @Override
+ public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException {
+ Log.d(TAG, "Received result for frameId " + frameId);
+
+ CaptureListenerHolder holder;
+
+ synchronized (mLock) {
+ // TODO: move this whole map into this class to make it more testable,
+ // exposing the methods necessary like subscribeToRequest, unsubscribe..
+ // TODO: make class static class
+
+ holder = CameraDevice.this.mCaptureListenerMap.get(frameId);
+
+ // Clean up listener once we no longer expect to see it.
+
+ // TODO: how to handle repeating listeners?
+ // we probably want cancelRequest to return # of times it already enqueued and
+ // keep a counter.
+ if (holder != null && !holder.isRepeating()) {
+ CameraDevice.this.mCaptureListenerMap.remove(frameId);
+ }
+ }
+
+ if (holder == null) {
+ Log.e(TAG, "Result had no listener holder associated with it, dropping result");
+ return;
+ }
+
+ CaptureResult resultAsCapture = new CaptureResult();
+ resultAsCapture.swap(result);
+
+ if (holder.getListener() != null) {
+ holder.getListener().onCaptureComplete(CameraDevice.this, holder.getRequest(),
+ resultAsCapture);
+ }
+ }
+
+ }
+
+}
diff --git a/core/java/android/hardware/photography/impl/package.html b/core/java/android/hardware/photography/impl/package.html
new file mode 100644
index 0000000..783d0a1
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/package.html
@@ -0,0 +1,3 @@
+<body>
+{@hide}
+</body>
diff --git a/core/java/android/hardware/photography/package.html b/core/java/android/hardware/photography/package.html
new file mode 100644
index 0000000..578bdf5
--- /dev/null
+++ b/core/java/android/hardware/photography/package.html
@@ -0,0 +1,84 @@
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<HTML>
+<BODY>
+<p>The android.hardware.photography package provides an interface to
+individual camera devices connected to an Android device. It replaces
+the deprecated {@link android.hardware.Camera} class.</p>
+
+<p>This package models a camera device as a pipeline, which takes in
+input requests for capturing a single frame, captures the single image
+per the request, and then outputs one capture result metadata packet,
+plus a set of output image buffers for the request. The requests are
+processed in-order, and multiple requests can be in flight at
+once. Since the camera device is a pipeline with multiple stages,
+having multiple requests in flight is required to maintain full
+framerate on most Android devices.</p>
+
+<p>To enumerate, query, and open available camera devices, obtain a
+{@link android.hardware.photography.CameraManager} instance.</p>
+
+<p>Individual {@link android.hardware.photography.CameraDevice
+CameraDevices} provide a set of static property information that
+describes the hardware device and the available settings and output
+parameters for the device. This information is provided through the
+{@link android.hardware.photography.CameraProperties} object.</p>
+
+<p>To capture or stream images from a camera device, the application
+must first configure a set of output Surfaces for use with the camera
+device, with {@link
+android.hardware.photography.CameraDevice#configureOutputs}. Each
+Surface has to be pre-configured with an appropriate size and format
+(if applicable) to match the sizes and formats available from the
+camera device. A target Surface can be obtained from a variety of
+classes, including {@link android.view.SurfaceView}, {@link
+android.graphics.SurfaceTexture} via {@link
+android.view.Surface#Surface(SurfaceTexture), {@link
+android.media.MediaCodec}, and {@link android.media.ImageReader}.
+</p>
+
+<p>The application then needs to construct a {@link
+android.hardware.photography.CaptureRequest}, which defines all the
+capture parameters needed by a camera device to capture a single
+image. The request also lists which of the configured output Surfaces
+should be used as targets for this capture. The CameraDevice has a
+{@link android.hardware.photography.CameraDevice#createCaptureRequest
+convenience factory method} for creating a request for a given use
+case which is optimized for the Android device the application is
+running on.</p>
+
+<p>Once the request has been set up, it can be handed to the
+CameraDevice either for a one-shot {@link
+android.hardware.photography.CameraDevice#capture} or for an endlessly
+{@link android.hardware.photography.CameraDevice#setRepeatingRequest
+repeating} use. Both methods also accept a list of requests to use as
+a burst capture / repeating burst. Repeating requests have a lower
+priority than captures, so a request submitted
+through <code>capture()</code> while there's a repeating request
+configured will be captured as soon as the current repeat (burst)
+capture completes.</p>
+
+<p>After processing a request, the camera device will produce a {@link
+android.hardware.photography.CaptureResult} object, which contains
+information about the state of the camera device at time of capture,
+and the final settings used. These may vary somewhat from the request,
+if rounding or resolving contradictory parameters was necessary. The
+camera device will also send a frame of image data into each of the
+output streams included in the request. These are produced
+asynchronously relative to the output CaptureResult, sometimes
+substantially later.</p>
+
+</BODY>
+</HTML>
diff --git a/core/java/android/hardware/photography/utils/CameraBinderDecorator.java b/core/java/android/hardware/photography/utils/CameraBinderDecorator.java
new file mode 100644
index 0000000..1a44b97f
--- /dev/null
+++ b/core/java/android/hardware/photography/utils/CameraBinderDecorator.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography.utils;
+
+import static android.hardware.photography.CameraAccessException.CAMERA_DISABLED;
+import static android.hardware.photography.CameraAccessException.CAMERA_DISCONNECTED;
+import static android.hardware.photography.CameraAccessException.CAMERA_IN_USE;
+
+import android.os.DeadObjectException;
+import android.os.RemoteException;
+
+import java.lang.reflect.Method;
+
+/**
+ * Translate camera service status_t return values into exceptions.
+ *
+ * @see android.hardware.photography.utils.CameraBinderDecorator#newInstance
+ * @hide
+ */
+public class CameraBinderDecorator {
+
+ public static final int NO_ERROR = 0;
+ public static final int PERMISSION_DENIED = -1;
+ public static final int ALREADY_EXISTS = -17;
+ public static final int BAD_VALUE = -22;
+ public static final int DEAD_OBJECT = -32;
+
+ /**
+ * TODO: add as error codes in Errors.h
+ * - POLICY_PROHIBITS
+ * - RESOURCE_BUSY
+ * - NO_SUCH_DEVICE
+ */
+ public static final int EACCES = -13;
+ public static final int EBUSY = -16;
+ public static final int ENODEV = -19;
+
+ private static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
+
+ @Override
+ public void onBeforeInvocation(Method m, Object[] args) {
+ }
+
+ @Override
+ public void onAfterInvocation(Method m, Object[] args, Object result) {
+ // int return type => status_t => convert to exception
+ if (m.getReturnType() == Integer.TYPE) {
+ int returnValue = (Integer) result;
+
+ switch (returnValue) {
+ case NO_ERROR:
+ return;
+ case PERMISSION_DENIED:
+ throw new SecurityException("Lacking privileges to access camera service");
+ case ALREADY_EXISTS:
+ // This should be handled at the call site. Typically this isn't bad,
+ // just means we tried to do an operation that already completed.
+ return;
+ case BAD_VALUE:
+ throw new IllegalArgumentException("Bad argument passed to camera service");
+ case DEAD_OBJECT:
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_DISCONNECTED));
+ // TODO: Camera service (native side) should return
+ // EACCES error
+ // when there's a policy manager disabled causing this
+ case EACCES:
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_DISABLED));
+ case EBUSY:
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_IN_USE));
+ case ENODEV:
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_DISCONNECTED));
+ }
+
+ /**
+ * Trap the rest of the negative return values. If we have known
+ * error codes i.e. ALREADY_EXISTS that aren't really runtime
+ * errors, then add them to the top switch statement
+ */
+ if (returnValue < 0) {
+ throw new UnsupportedOperationException(String.format("Unknown error %d",
+ returnValue));
+ }
+ }
+ }
+
+ @Override
+ public boolean onCatchException(Method m, Object[] args, Throwable t) {
+
+ if (t instanceof DeadObjectException) {
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_DISCONNECTED,
+ "Process hosting the camera service has died unexpectedly",
+ t));
+ } else if (t instanceof RemoteException) {
+ throw new UnsupportedOperationException("An unknown RemoteException was thrown" +
+ " which should never happen.", t);
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onFinally(Method m, Object[] args) {
+ }
+
+ }
+
+ /**
+ * <p>
+ * Wraps the type T with a proxy that will check 'status_t' return codes
+ * from the native side of the camera service, and throw Java exceptions
+ * automatically based on the code.
+ * </p>
+ * <p>
+ * In addition it also rewrites binder's RemoteException into either a
+ * CameraAccessException or an UnsupportedOperationException.
+ * </p>
+ * <p>
+ * As a result of calling any method on the proxy, RemoteException is
+ * guaranteed never to be thrown.
+ * </p>
+ *
+ * @param obj object that will serve as the target for all method calls
+ * @param <T> the type of the element you want to wrap. This must be an interface.
+ * @return a proxy that will intercept all invocations to obj
+ */
+ public static <T> T newInstance(T obj) {
+ return Decorator.<T> newInstance(obj, new CameraBinderDecoratorListener());
+ }
+}
diff --git a/core/java/android/hardware/photography/utils/CameraRuntimeException.java b/core/java/android/hardware/photography/utils/CameraRuntimeException.java
new file mode 100644
index 0000000..25dfc62
--- /dev/null
+++ b/core/java/android/hardware/photography/utils/CameraRuntimeException.java
@@ -0,0 +1,63 @@
+package android.hardware.photography.utils;
+
+import android.hardware.photography.CameraAccessException;
+
+/**
+ * @hide
+ */
+public class CameraRuntimeException extends RuntimeException {
+
+ private final int mReason;
+ private String mMessage;
+ private Throwable mCause;
+
+ public final int getReason() {
+ return mReason;
+ }
+
+ public CameraRuntimeException(int problem) {
+ super();
+ mReason = problem;
+ }
+
+ public CameraRuntimeException(int problem, String message) {
+ super(message);
+ mReason = problem;
+ mMessage = message;
+ }
+
+ public CameraRuntimeException(int problem, String message, Throwable cause) {
+ super(message, cause);
+ mReason = problem;
+ mMessage = message;
+ mCause = cause;
+ }
+
+ public CameraRuntimeException(int problem, Throwable cause) {
+ super(cause);
+ mReason = problem;
+ mCause = cause;
+ }
+
+ /**
+ * Recreate this exception as the CameraAccessException equivalent.
+ * @return CameraAccessException
+ */
+ public CameraAccessException asChecked() {
+ CameraAccessException e;
+
+ if (mMessage != null && mCause != null) {
+ e = new CameraAccessException(mReason, mMessage, mCause);
+ } else if (mMessage != null) {
+ e = new CameraAccessException(mReason, mMessage);
+ } else if (mCause != null) {
+ e = new CameraAccessException(mReason, mCause);
+ } else {
+ e = new CameraAccessException(mReason);
+ }
+ // throw and catch, so java has a chance to fill out the stack trace
+ e.setStackTrace(this.getStackTrace());
+
+ return e;
+ }
+}
diff --git a/core/java/android/hardware/photography/utils/Decorator.java b/core/java/android/hardware/photography/utils/Decorator.java
new file mode 100644
index 0000000..ed05dd2
--- /dev/null
+++ b/core/java/android/hardware/photography/utils/Decorator.java
@@ -0,0 +1,92 @@
+
+package android.hardware.photography.utils;
+
+import java.lang.reflect.*;
+
+/**
+ * This is an implementation of the 'decorator' design pattern using Java's proxy mechanism.
+ *
+ * @see android.hardware.photography.utils.Decorator#newInstance
+ *
+ * @hide
+ */
+public class Decorator<T> implements InvocationHandler {
+
+ public interface DecoratorListener {
+ /**
+ * This method is called before the target method is invoked
+ * @param args arguments to target method
+ * @param m Method being called
+ */
+ void onBeforeInvocation(Method m, Object[] args);
+ /**
+ * This function is called after the target method is invoked
+ * if there were no uncaught exceptions
+ * @param args arguments to target method
+ * @param m Method being called
+ * @param result return value of target method
+ */
+ void onAfterInvocation(Method m, Object[] args, Object result);
+ /**
+ * This method is called only if there was an exception thrown by the target method
+ * during its invocation.
+ *
+ * @param args arguments to target method
+ * @param m Method being called
+ * @param t Throwable that was thrown
+ * @return false to rethrow exception, true if the exception was handled
+ */
+ boolean onCatchException(Method m, Object[] args, Throwable t);
+ /**
+ * This is called after the target method is invoked, regardless of whether or not
+ * there were any exceptions.
+ * @param args arguments to target method
+ * @param m Method being called
+ */
+ void onFinally(Method m, Object[] args);
+ }
+
+ private final T mObject;
+ private final DecoratorListener mListener;
+
+ /**
+ * Create a decorator wrapping the specified object's method calls.
+ *
+ * @param obj the object whose method calls you want to intercept
+ * @param listener the decorator handler for intercepted method calls
+ * @param <T> the type of the element you want to wrap. This must be an interface.
+ * @return a wrapped interface-compatible T
+ */
+ @SuppressWarnings("unchecked")
+ public static<T> T newInstance(T obj, DecoratorListener listener) {
+ return (T)java.lang.reflect.Proxy.newProxyInstance(
+ obj.getClass().getClassLoader(),
+ obj.getClass().getInterfaces(),
+ new Decorator<T>(obj, listener));
+ }
+
+ private Decorator(T obj, DecoratorListener listener) {
+ this.mObject = obj;
+ this.mListener = listener;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method m, Object[] args)
+ throws Throwable
+ {
+ Object result = null;
+ try {
+ mListener.onBeforeInvocation(m, args);
+ result = m.invoke(mObject, args);
+ mListener.onAfterInvocation(m, args, result);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ if (!mListener.onCatchException(m, args, t)) {
+ throw t;
+ }
+ } finally {
+ mListener.onFinally(m, args);
+ }
+ return result;
+ }
+}
diff --git a/core/java/android/hardware/photography/utils/UncheckedThrow.java b/core/java/android/hardware/photography/utils/UncheckedThrow.java
new file mode 100644
index 0000000..8eed6b1
--- /dev/null
+++ b/core/java/android/hardware/photography/utils/UncheckedThrow.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.photography.utils;
+
+/**
+ * @hide
+ */
+public class UncheckedThrow {
+
+ /**
+ * Throw any kind of exception without needing it to be checked
+ * @param e any instance of a Exception
+ */
+ public static void throwAnyException(Exception e) {
+ /**
+ * Abuse type erasure by making the compiler think we are throwing RuntimeException,
+ * which is unchecked, but then inserting any exception in there.
+ */
+ UncheckedThrow.<RuntimeException>throwAnyImpl(e);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static<T extends Exception> void throwAnyImpl(Exception e) throws T {
+ throw (T) e;
+ }
+}
diff --git a/core/java/android/hardware/photography/utils/package.html b/core/java/android/hardware/photography/utils/package.html
new file mode 100644
index 0000000..783d0a1
--- /dev/null
+++ b/core/java/android/hardware/photography/utils/package.html
@@ -0,0 +1,3 @@
+<body>
+{@hide}
+</body>
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 9306373..06d8e4a 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -279,6 +279,10 @@
try {
InputMethodSession ls = ((IInputMethodSessionWrapper)
session).getInternalInputMethodSession();
+ if (ls == null) {
+ Log.w(TAG, "Session is already finished: " + session);
+ return;
+ }
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
DO_SET_SESSION_ENABLED, enabled ? 1 : 0, ls));
} catch (ClassCastException e) {
@@ -291,6 +295,10 @@
try {
InputMethodSession ls = ((IInputMethodSessionWrapper)
session).getInternalInputMethodSession();
+ if (ls == null) {
+ Log.w(TAG, "Session is already finished: " + session);
+ return;
+ }
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REVOKE_SESSION, ls));
} catch (ClassCastException e) {
Log.w(TAG, "Incoming session not of correct type: " + session, e);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4881d14..7f82ce3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -381,7 +381,6 @@
if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
+ " ic=" + mInputConnection);
onUnbindInput();
- mInputStarted = false;
mInputBinding = null;
mInputConnection = null;
}
@@ -719,7 +718,7 @@
super.onDestroy();
mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
mInsetsComputer);
- finishViews();
+ doFinishInput();
if (mWindowAdded) {
// Disable exit animation for the current IME window
// to avoid the race condition between the exit and enter animations
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6487c92..04ab0a5 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -465,6 +465,21 @@
}
/**
+ * Checks if the given network type is backed by a Wi-Fi radio.
+ *
+ * @hide
+ */
+ public static boolean isNetworkTypeWifi(int networkType) {
+ switch (networkType) {
+ case TYPE_WIFI:
+ case TYPE_WIFI_P2P:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
* Specifies the preferred network type. When the device has more
* than one type available the preferred network type will be used.
* Note that this made sense when we only had 2 network types,
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 4ab479e..333fcc6 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -21,6 +21,7 @@
import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.Collection;
+import java.util.Locale;
import android.util.Log;
@@ -223,7 +224,7 @@
public static InetAddress hexToInet6Address(String addrHexString)
throws IllegalArgumentException {
try {
- return numericToInetAddress(String.format("%s:%s:%s:%s:%s:%s:%s:%s",
+ return numericToInetAddress(String.format(Locale.US, "%s:%s:%s:%s:%s:%s:%s:%s",
addrHexString.substring(0,4), addrHexString.substring(4,8),
addrHexString.substring(8,12), addrHexString.substring(12,16),
addrHexString.substring(16,20), addrHexString.substring(20,24),
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 1d1e149..a9289ba 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -19,7 +19,6 @@
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.Environment.UserEnvironment;
import android.os.StrictMode;
import android.util.Log;
import java.io.File;
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 65d3f2b..733de94 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -17,8 +17,8 @@
package android.net;
import android.app.Activity;
-import android.app.Service;
import android.app.PendingIntent;
+import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -30,10 +30,10 @@
import com.android.internal.net.VpnConfig;
-import java.net.InetAddress;
+import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.Inet6Address;
-import java.net.DatagramSocket;
+import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
@@ -329,7 +329,8 @@
throw new IllegalArgumentException("Bad address");
}
- mAddresses.append(' ' + address.getHostAddress() + '/' + prefixLength);
+ mAddresses.append(' ')
+ .append(address.getHostAddress()).append('/').append(prefixLength);
return this;
}
@@ -364,7 +365,7 @@
}
}
- mRoutes.append(String.format(" %s/%d", address.getHostAddress(), prefixLength));
+ mRoutes.append(' ').append(address.getHostAddress()).append('/').append(prefixLength);
return this;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 499ec77..12646bd 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -41,7 +41,10 @@
public abstract class BatteryStats implements Parcelable {
private static final boolean LOCAL_LOGV = false;
-
+
+ /** @hide */
+ public static final String SERVICE_NAME = "batterystats";
+
/**
* A constant indicating a partial wake lock timer.
*/
@@ -98,6 +101,11 @@
public static final int VIBRATOR_ON = 9;
/**
+ * A constant indicating a foreground activity timer
+ */
+ public static final int FOREGROUND_ACTIVITY = 10;
+
+ /**
* Include all of the data in the stats, including previously saved data.
*/
public static final int STATS_SINCE_CHARGED = 0;
@@ -125,7 +133,7 @@
/**
* Bump the version on this if the checkin format changes.
*/
- private static final int BATTERY_STATS_CHECKIN_VERSION = 5;
+ private static final int BATTERY_STATS_CHECKIN_VERSION = 7;
private static final long BYTES_PER_KB = 1024;
private static final long BYTES_PER_MB = 1048576; // 1024^2
@@ -137,6 +145,7 @@
private static final String PROCESS_DATA = "pr";
private static final String SENSOR_DATA = "sr";
private static final String VIBRATOR_DATA = "vib";
+ private static final String FOREGROUND_DATA = "fg";
private static final String WAKELOCK_DATA = "wl";
private static final String KERNEL_WAKELOCK_DATA = "kwl";
private static final String NETWORK_DATA = "nt";
@@ -253,17 +262,7 @@
* {@hide}
*/
public abstract int getUid();
-
- /**
- * {@hide}
- */
- public abstract long getTcpBytesReceived(int which);
-
- /**
- * {@hide}
- */
- public abstract long getTcpBytesSent(int which);
-
+
public abstract void noteWifiRunningLocked();
public abstract void noteWifiStoppedLocked();
public abstract void noteFullWifiLockAcquiredLocked();
@@ -276,6 +275,8 @@
public abstract void noteAudioTurnedOffLocked();
public abstract void noteVideoTurnedOnLocked();
public abstract void noteVideoTurnedOffLocked();
+ public abstract void noteActivityResumedLocked();
+ public abstract void noteActivityPausedLocked();
public abstract long getWifiRunningTime(long batteryRealtime, int which);
public abstract long getFullWifiLockTime(long batteryRealtime, int which);
public abstract long getWifiScanTime(long batteryRealtime, int which);
@@ -283,6 +284,7 @@
int which);
public abstract long getAudioTurnedOnTime(long batteryRealtime, int which);
public abstract long getVideoTurnedOnTime(long batteryRealtime, int which);
+ public abstract Timer getForegroundActivityTimer();
public abstract Timer getVibratorOnTimer();
/**
@@ -295,11 +297,14 @@
};
public static final int NUM_USER_ACTIVITY_TYPES = 3;
-
+
public abstract void noteUserActivityLocked(int type);
public abstract boolean hasUserActivity();
public abstract int getUserActivityCount(int type, int which);
-
+
+ public abstract boolean hasNetworkActivity();
+ public abstract long getNetworkActivityCount(int type, int which);
+
public static abstract class Sensor {
/*
* FIXME: it's not correct to use this magic value because it
@@ -920,6 +925,15 @@
*/
public abstract long getBluetoothOnTime(long batteryRealtime, int which);
+ public static final int NETWORK_MOBILE_RX_BYTES = 0;
+ public static final int NETWORK_MOBILE_TX_BYTES = 1;
+ public static final int NETWORK_WIFI_RX_BYTES = 2;
+ public static final int NETWORK_WIFI_TX_BYTES = 3;
+
+ public static final int NUM_NETWORK_ACTIVITY_TYPES = NETWORK_WIFI_TX_BYTES + 1;
+
+ public abstract long getNetworkActivityCount(int type, int which);
+
/**
* Return whether we are currently running on battery.
*/
@@ -1194,13 +1208,13 @@
pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
pw.print(uid); pw.print(',');
pw.print(category); pw.print(',');
- pw.print(type);
+ pw.print(type);
for (Object arg : args) {
- pw.print(',');
- pw.print(arg);
+ pw.print(',');
+ pw.print(arg);
}
- pw.print('\n');
+ pw.println();
}
/**
@@ -1229,7 +1243,7 @@
final int NU = uidStats.size();
String category = STAT_NAMES[which];
-
+
// Dump "battery" stat
dumpLine(pw, 0 /* uid */, category, BATTERY_DATA,
which == STATS_SINCE_CHARGED ? getStartCount() : "N/A",
@@ -1237,16 +1251,20 @@
totalRealtime / 1000, totalUptime / 1000);
// Calculate total network and wakelock times across all uids.
- long rxTotal = 0;
- long txTotal = 0;
+ long mobileRxTotal = 0;
+ long mobileTxTotal = 0;
+ long wifiRxTotal = 0;
+ long wifiTxTotal = 0;
long fullWakeLockTimeTotal = 0;
long partialWakeLockTimeTotal = 0;
for (int iu = 0; iu < NU; iu++) {
Uid u = uidStats.valueAt(iu);
- rxTotal += u.getTcpBytesReceived(which);
- txTotal += u.getTcpBytesSent(which);
-
+ mobileRxTotal += u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, which);
+ mobileTxTotal += u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, which);
+ wifiRxTotal += u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, which);
+ wifiTxTotal += u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, which);
+
Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
if (wakelocks.size() > 0) {
for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
@@ -1270,7 +1288,8 @@
// Dump misc stats
dumpLine(pw, 0 /* uid */, category, MISC_DATA,
screenOnTime / 1000, phoneOnTime / 1000, wifiOnTime / 1000,
- wifiRunningTime / 1000, bluetoothOnTime / 1000, rxTotal, txTotal,
+ wifiRunningTime / 1000, bluetoothOnTime / 1000,
+ mobileRxTotal, mobileTxTotal, wifiRxTotal, wifiTxTotal,
fullWakeLockTimeTotal, partialWakeLockTimeTotal,
getInputEventCount(which));
@@ -1341,14 +1360,18 @@
}
Uid u = uidStats.valueAt(iu);
// Dump Network stats per uid, if any
- long rx = u.getTcpBytesReceived(which);
- long tx = u.getTcpBytesSent(which);
+ long mobileRx = u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, which);
+ long mobileTx = u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, which);
+ long wifiRx = u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, which);
+ long wifiTx = u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, which);
long fullWifiLockOnTime = u.getFullWifiLockTime(batteryRealtime, which);
long wifiScanTime = u.getWifiScanTime(batteryRealtime, which);
long uidWifiRunningTime = u.getWifiRunningTime(batteryRealtime, which);
-
- if (rx > 0 || tx > 0) dumpLine(pw, uid, category, NETWORK_DATA, rx, tx);
-
+
+ if (mobileRx > 0 || mobileTx > 0 || wifiRx > 0 || wifiTx > 0) {
+ dumpLine(pw, uid, category, NETWORK_DATA, mobileRx, mobileTx, wifiRx, wifiTx);
+ }
+
if (fullWifiLockOnTime != 0 || wifiScanTime != 0
|| uidWifiRunningTime != 0) {
dumpLine(pw, uid, category, WIFI_DATA,
@@ -1384,7 +1407,11 @@
// Only log if we had at lease one wakelock...
if (sb.length() > 0) {
- dumpLine(pw, uid, category, WAKELOCK_DATA, ent.getKey(), sb.toString());
+ String name = ent.getKey();
+ if (name.indexOf(',') >= 0) {
+ name = name.replace(',', '_');
+ }
+ dumpLine(pw, uid, category, WAKELOCK_DATA, name, sb.toString());
}
}
}
@@ -1417,22 +1444,31 @@
}
}
+ Timer fgTimer = u.getForegroundActivityTimer();
+ if (fgTimer != null) {
+ // Convert from microseconds to milliseconds with rounding
+ long totalTime = (fgTimer.getTotalTimeLocked(batteryRealtime, which) + 500) / 1000;
+ int count = fgTimer.getCountLocked(which);
+ if (totalTime != 0) {
+ dumpLine(pw, uid, category, FOREGROUND_DATA, totalTime, count);
+ }
+ }
+
Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
if (processStats.size() > 0) {
for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
: processStats.entrySet()) {
Uid.Proc ps = ent.getValue();
-
- long userTime = ps.getUserTime(which);
- long systemTime = ps.getSystemTime(which);
- int starts = ps.getStarts(which);
-
- if (userTime != 0 || systemTime != 0 || starts != 0) {
- dumpLine(pw, uid, category, PROCESS_DATA,
- ent.getKey(), // proc
- userTime * 10, // cpu time in ms
- systemTime * 10, // user time in ms
- starts); // process starts
+
+ final long userMillis = ps.getUserTime(which) * 10;
+ final long systemMillis = ps.getSystemTime(which) * 10;
+ final long foregroundMillis = ps.getForegroundTime(which) * 10;
+ final long starts = ps.getStarts(which);
+
+ if (userMillis != 0 || systemMillis != 0 || foregroundMillis != 0
+ || starts != 0) {
+ dumpLine(pw, uid, category, PROCESS_DATA, ent.getKey(), userMillis,
+ systemMillis, foregroundMillis, starts);
}
}
}
@@ -1551,8 +1587,10 @@
pw.println(sb.toString());
// Calculate total network and wakelock times across all uids.
- long rxTotal = 0;
- long txTotal = 0;
+ long mobileRxTotal = 0;
+ long mobileTxTotal = 0;
+ long wifiRxTotal = 0;
+ long wifiTxTotal = 0;
long fullWakeLockTimeTotalMicros = 0;
long partialWakeLockTimeTotalMicros = 0;
@@ -1605,9 +1643,11 @@
for (int iu = 0; iu < NU; iu++) {
Uid u = uidStats.valueAt(iu);
- rxTotal += u.getTcpBytesReceived(which);
- txTotal += u.getTcpBytesSent(which);
-
+ mobileRxTotal += u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, which);
+ mobileTxTotal += u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, which);
+ wifiRxTotal += u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, which);
+ wifiTxTotal += u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, which);
+
Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
if (wakelocks.size() > 0) {
for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
@@ -1640,8 +1680,11 @@
}
pw.print(prefix);
- pw.print(" Total received: "); pw.print(formatBytesLocked(rxTotal));
- pw.print(", Total sent: "); pw.println(formatBytesLocked(txTotal));
+ pw.print(" Mobile total received: "); pw.print(formatBytesLocked(mobileRxTotal));
+ pw.print(", Total sent: "); pw.println(formatBytesLocked(mobileTxTotal));
+ pw.print(prefix);
+ pw.print(" Wi-Fi total received: "); pw.print(formatBytesLocked(wifiRxTotal));
+ pw.print(", Total sent: "); pw.println(formatBytesLocked(wifiTxTotal));
sb.setLength(0);
sb.append(prefix);
sb.append(" Total full wakelock time: "); formatTimeMs(sb,
@@ -1760,8 +1803,8 @@
for (int i=0; i<timers.size(); i++) {
TimerEntry timer = timers.get(i);
sb.setLength(0);
- sb.append(" Wake lock #");
- sb.append(timer.mId);
+ sb.append(" Wake lock ");
+ UserHandle.formatUid(sb, timer.mId);
sb.append(" ");
sb.append(timer.mName);
printWakeLock(sb, timer.mTimer, batteryRealtime, null, which, ": ");
@@ -1779,22 +1822,32 @@
}
Uid u = uidStats.valueAt(iu);
-
- pw.println(prefix + " #" + uid + ":");
+
+ pw.print(prefix);
+ pw.print(" ");
+ UserHandle.formatUid(pw, uid);
+ pw.println(":");
boolean uidActivity = false;
- long tcpReceived = u.getTcpBytesReceived(which);
- long tcpSent = u.getTcpBytesSent(which);
+ long mobileRxBytes = u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, which);
+ long mobileTxBytes = u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, which);
+ long wifiRxBytes = u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, which);
+ long wifiTxBytes = u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, which);
long fullWifiLockOnTime = u.getFullWifiLockTime(batteryRealtime, which);
long wifiScanTime = u.getWifiScanTime(batteryRealtime, which);
long uidWifiRunningTime = u.getWifiRunningTime(batteryRealtime, which);
-
- if (tcpReceived != 0 || tcpSent != 0) {
- pw.print(prefix); pw.print(" Network: ");
- pw.print(formatBytesLocked(tcpReceived)); pw.print(" received, ");
- pw.print(formatBytesLocked(tcpSent)); pw.println(" sent");
+
+ if (mobileRxBytes > 0 || mobileTxBytes > 0) {
+ pw.print(prefix); pw.print(" Mobile network: ");
+ pw.print(formatBytesLocked(mobileRxBytes)); pw.print(" received, ");
+ pw.print(formatBytesLocked(mobileTxBytes)); pw.println(" sent");
}
-
+ if (wifiRxBytes > 0 || wifiTxBytes > 0) {
+ pw.print(prefix); pw.print(" Wi-Fi network: ");
+ pw.print(formatBytesLocked(wifiRxBytes)); pw.print(" received, ");
+ pw.print(formatBytesLocked(wifiTxBytes)); pw.println(" sent");
+ }
+
if (u.hasUserActivity()) {
boolean hasData = false;
for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) {
@@ -1961,6 +2014,24 @@
}
}
+ Timer fgTimer = u.getForegroundActivityTimer();
+ if (fgTimer != null) {
+ // Convert from microseconds to milliseconds with rounding
+ long totalTime = (fgTimer.getTotalTimeLocked(batteryRealtime, which) + 500) / 1000;
+ int count = fgTimer.getCountLocked(which);
+ if (totalTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Foreground activities: ");
+ formatTimeMs(sb, totalTime);
+ sb.append("realtime (");
+ sb.append(count);
+ sb.append(" times)");
+ pw.println(sb.toString());
+ uidActivity = true;
+ }
+ }
+
Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
if (processStats.size() > 0) {
for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
@@ -1968,23 +2039,26 @@
Uid.Proc ps = ent.getValue();
long userTime;
long systemTime;
+ long foregroundTime;
int starts;
int numExcessive;
userTime = ps.getUserTime(which);
systemTime = ps.getSystemTime(which);
+ foregroundTime = ps.getForegroundTime(which);
starts = ps.getStarts(which);
numExcessive = which == STATS_SINCE_CHARGED
? ps.countExcessivePowers() : 0;
- if (userTime != 0 || systemTime != 0 || starts != 0
+ if (userTime != 0 || systemTime != 0 || foregroundTime != 0 || starts != 0
|| numExcessive != 0) {
sb.setLength(0);
sb.append(prefix); sb.append(" Proc ");
sb.append(ent.getKey()); sb.append(":\n");
sb.append(prefix); sb.append(" CPU: ");
formatTime(sb, userTime); sb.append("usr + ");
- formatTime(sb, systemTime); sb.append("krn");
+ formatTime(sb, systemTime); sb.append("krn ; ");
+ formatTime(sb, foregroundTime); sb.append("fg");
if (starts != 0) {
sb.append("\n"); sb.append(prefix); sb.append(" ");
sb.append(starts); sb.append(" proc starts");
@@ -2042,7 +2116,7 @@
sb.append(sent.getKey()); sb.append(":\n");
sb.append(prefix); sb.append(" Created for: ");
formatTimeMs(sb, startTime / 1000);
- sb.append(" uptime\n");
+ sb.append("uptime\n");
sb.append(prefix); sb.append(" Starts: ");
sb.append(starts);
sb.append(", launches: "); sb.append(launches);
@@ -2207,6 +2281,30 @@
}
oldState = rec.states;
}
+
+ public void printNextItemCheckin(PrintWriter pw, HistoryItem rec, long now) {
+ pw.print(rec.time-now);
+ pw.print(",");
+ if (rec.cmd == HistoryItem.CMD_START) {
+ pw.print("start");
+ } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
+ pw.print("overflow");
+ } else {
+ pw.print(rec.batteryLevel);
+ pw.print(",");
+ pw.print(rec.states);
+ pw.print(",");
+ pw.print(rec.batteryStatus);
+ pw.print(",");
+ pw.print(rec.batteryHealth);
+ pw.print(",");
+ pw.print(rec.batteryPlugType);
+ pw.print(",");
+ pw.print((int)rec.batteryTemperature);
+ pw.print(",");
+ pw.print((int)rec.batteryVoltage);
+ }
+ }
}
/**
@@ -2215,7 +2313,7 @@
* @param pw a Printer to receive the dump output.
*/
@SuppressWarnings("unused")
- public void dumpLocked(PrintWriter pw) {
+ public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly, int reqUid) {
prepareForDumpLocked();
long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
@@ -2267,29 +2365,38 @@
if (didPid) {
pw.println("");
}
-
- pw.println("Statistics since last charge:");
- pw.println(" System starts: " + getStartCount()
- + ", currently on battery: " + getIsOnBattery());
- dumpLocked(pw, "", STATS_SINCE_CHARGED, -1);
- pw.println("");
+
+ if (!isUnpluggedOnly) {
+ pw.println("Statistics since last charge:");
+ pw.println(" System starts: " + getStartCount()
+ + ", currently on battery: " + getIsOnBattery());
+ dumpLocked(pw, "", STATS_SINCE_CHARGED, reqUid);
+ pw.println("");
+ }
pw.println("Statistics since last unplugged:");
- dumpLocked(pw, "", STATS_SINCE_UNPLUGGED, -1);
+ dumpLocked(pw, "", STATS_SINCE_UNPLUGGED, reqUid);
}
@SuppressWarnings("unused")
- public void dumpCheckinLocked(PrintWriter pw, String[] args, List<ApplicationInfo> apps) {
+ public void dumpCheckinLocked(
+ PrintWriter pw, List<ApplicationInfo> apps, boolean isUnpluggedOnly) {
prepareForDumpLocked();
+
+ long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
- boolean isUnpluggedOnly = false;
-
- for (String arg : args) {
- if ("-u".equals(arg)) {
- if (LOCAL_LOGV) Log.v("BatteryStats", "Dumping unplugged data");
- isUnpluggedOnly = true;
+ final HistoryItem rec = new HistoryItem();
+ if (startIteratingHistoryLocked()) {
+ HistoryPrinter hprinter = new HistoryPrinter();
+ while (getNextHistoryLocked(rec)) {
+ pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+ pw.print(0); pw.print(',');
+ pw.print("h"); pw.print(',');
+ hprinter.printNextItemCheckin(pw, rec, now);
+ pw.println();
}
+ finishIteratingHistoryLocked();
}
-
+
if (apps != null) {
SparseArray<ArrayList<String>> uids = new SparseArray<ArrayList<String>>();
for (int i=0; i<apps.size(); i++) {
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 7ffd30b..7b91418 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -17,6 +17,7 @@
package android.os;
import android.util.Log;
+import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -288,7 +289,7 @@
*/
public void dump(FileDescriptor fd, String[] args) {
FileOutputStream fout = new FileOutputStream(fd);
- PrintWriter pw = new PrintWriter(fout);
+ PrintWriter pw = new FastPrintWriter(fout);
try {
final String disabled;
synchronized (Binder.class) {
@@ -310,7 +311,7 @@
*/
public void dumpAsync(final FileDescriptor fd, final String[] args) {
final FileOutputStream fout = new FileOutputStream(fd);
- final PrintWriter pw = new PrintWriter(fout);
+ final PrintWriter pw = new FastPrintWriter(fout);
Thread thr = new Thread("Binder.dumpAsync") {
public void run() {
try {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6c9f2d1..71c3e4a 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -436,6 +436,11 @@
* Android 4.3: Jelly Bean MR2, the revenge of the beans.
*/
public static final int JELLY_BEAN_MR2 = 18;
+
+ /**
+ * Android X.X: Key Lime Pie, another tasty treat.
+ */
+ public static final int KEY_LIME_PIE = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/CancellationSignal.java b/core/java/android/os/CancellationSignal.java
index dcba9b7..e8053d5 100644
--- a/core/java/android/os/CancellationSignal.java
+++ b/core/java/android/os/CancellationSignal.java
@@ -17,7 +17,6 @@
package android.os;
import android.os.ICancellationSignal;
-import android.os.ICancellationSignal.Stub;
/**
* Provides the ability to cancel an operation in progress.
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index fd01da9..c5f473e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -16,6 +16,7 @@
package android.os;
+import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.TypedProperties;
import android.util.Log;
@@ -108,31 +109,78 @@
* process. The returns info broken down by dalvik, native, and other. All results are in kB.
*/
public static class MemoryInfo implements Parcelable {
- /** The proportional set size for dalvik. */
+ /** The proportional set size for dalvik heap. (Doesn't include other Dalvik overhead.) */
public int dalvikPss;
- /** The private dirty pages used by dalvik. */
+ /** The proportional set size that is swappable for dalvik heap. */
+ /** @hide We may want to expose this, eventually. */
+ public int dalvikSwappablePss;
+ /** The private dirty pages used by dalvik heap. */
public int dalvikPrivateDirty;
- /** The shared dirty pages used by dalvik. */
+ /** The shared dirty pages used by dalvik heap. */
public int dalvikSharedDirty;
+ /** The private clean pages used by dalvik heap. */
+ /** @hide We may want to expose this, eventually. */
+ public int dalvikPrivateClean;
+ /** The shared clean pages used by dalvik heap. */
+ /** @hide We may want to expose this, eventually. */
+ public int dalvikSharedClean;
/** The proportional set size for the native heap. */
public int nativePss;
+ /** The proportional set size that is swappable for the native heap. */
+ /** @hide We may want to expose this, eventually. */
+ public int nativeSwappablePss;
/** The private dirty pages used by the native heap. */
public int nativePrivateDirty;
/** The shared dirty pages used by the native heap. */
public int nativeSharedDirty;
+ /** The private clean pages used by the native heap. */
+ /** @hide We may want to expose this, eventually. */
+ public int nativePrivateClean;
+ /** The shared clean pages used by the native heap. */
+ /** @hide We may want to expose this, eventually. */
+ public int nativeSharedClean;
/** The proportional set size for everything else. */
public int otherPss;
+ /** The proportional set size that is swappable for everything else. */
+ /** @hide We may want to expose this, eventually. */
+ public int otherSwappablePss;
/** The private dirty pages used by everything else. */
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;
+ /** The private clean pages used by everything else. */
+ /** @hide We may want to expose this, eventually. */
+ public int otherPrivateClean;
+ /** The shared clean pages used by everything else. */
+ /** @hide We may want to expose this, eventually. */
+ public int otherSharedClean;
/** @hide */
- public static final int NUM_OTHER_STATS = 10;
+ public static final int NUM_OTHER_STATS = 13;
- private int[] otherStats = new int[NUM_OTHER_STATS*3];
+ /** @hide */
+ public static final int NUM_DVK_STATS = 5;
+
+ /** @hide */
+ public static final int NUM_CATEGORIES = 6;
+
+ /** @hide */
+ public static final int offsetPss = 0;
+ /** @hide */
+ public static final int offsetSwappablePss = 1;
+ /** @hide */
+ public static final int offsetPrivateDirty = 2;
+ /** @hide */
+ public static final int offsetSharedDirty = 3;
+ /** @hide */
+ public static final int offsetPrivateClean = 4;
+ /** @hide */
+ public static final int offsetSharedClean = 5;
+
+
+ private int[] otherStats = new int[(NUM_OTHER_STATS+NUM_DVK_STATS)*NUM_CATEGORIES];
public MemoryInfo() {
}
@@ -144,6 +192,14 @@
return dalvikPss + nativePss + otherPss;
}
+
+ /**
+ * Return total PSS memory usage in kB.
+ */
+ public int getTotalSwappablePss() {
+ return dalvikSwappablePss + nativeSwappablePss + otherSwappablePss;
+ }
+
/**
* Return total private dirty memory usage in kB.
*/
@@ -158,35 +214,74 @@
return dalvikSharedDirty + nativeSharedDirty + otherSharedDirty;
}
- /* @hide */
- public int getOtherPss(int which) {
- return otherStats[which*3];
+ /**
+ * Return total shared clean memory usage in kB.
+ */
+ public int getTotalPrivateClean() {
+ return dalvikPrivateClean + nativePrivateClean + otherPrivateClean;
+ }
+
+ /**
+ * Return total shared clean memory usage in kB.
+ */
+ public int getTotalSharedClean() {
+ return dalvikSharedClean + nativeSharedClean + otherSharedClean;
}
/* @hide */
+ public int getOtherPss(int which) {
+ return otherStats[which*NUM_CATEGORIES + offsetPss];
+ }
+
+
+ /* @hide */
+ public int getOtherSwappablePss(int which) {
+ return otherStats[which*NUM_CATEGORIES + offsetSwappablePss];
+ }
+
+
+ /* @hide */
public int getOtherPrivateDirty(int which) {
- return otherStats[which*3 + 1];
+ return otherStats[which*NUM_CATEGORIES + offsetPrivateDirty];
}
/* @hide */
public int getOtherSharedDirty(int which) {
- return otherStats[which*3 + 2];
+ return otherStats[which*NUM_CATEGORIES + offsetSharedDirty];
+ }
+
+ /* @hide */
+ public int getOtherPrivateClean(int which) {
+ return otherStats[which*NUM_CATEGORIES + offsetPrivateClean];
}
/* @hide */
+ public int getOtherSharedClean(int which) {
+ return otherStats[which*NUM_CATEGORIES + offsetSharedClean];
+ }
+
+ /* @hide */
public static String getOtherLabel(int which) {
switch (which) {
- case 0: return "Stack";
- case 1: return "Cursor";
- case 2: return "Ashmem";
- case 3: return "Other dev";
- case 4: return ".so mmap";
- case 5: return ".jar mmap";
- case 6: return ".apk mmap";
- case 7: return ".ttf mmap";
- case 8: return ".dex mmap";
- case 9: return "Other mmap";
+ case 0: return "Dalvik Other";
+ case 1: return "Stack";
+ case 2: return "Cursor";
+ case 3: return "Ashmem";
+ case 4: return "Other dev";
+ case 5: return ".so mmap";
+ case 6: return ".jar mmap";
+ case 7: return ".apk mmap";
+ case 8: return ".ttf mmap";
+ case 9: return ".dex mmap";
+ case 10: return "code mmap";
+ case 11: return "image mmap";
+ case 12: return "Other mmap";
+ case 13: return ".Heap";
+ case 14: return ".LOS";
+ case 15: return ".LinearAlloc";
+ case 16: return ".GC";
+ case 17: return ".JITCache";
default: return "????";
}
}
@@ -197,27 +292,45 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(dalvikPss);
+ dest.writeInt(dalvikSwappablePss);
dest.writeInt(dalvikPrivateDirty);
dest.writeInt(dalvikSharedDirty);
+ dest.writeInt(dalvikPrivateClean);
+ dest.writeInt(dalvikSharedClean);
dest.writeInt(nativePss);
+ dest.writeInt(nativeSwappablePss);
dest.writeInt(nativePrivateDirty);
dest.writeInt(nativeSharedDirty);
+ dest.writeInt(nativePrivateClean);
+ dest.writeInt(nativeSharedClean);
dest.writeInt(otherPss);
+ dest.writeInt(otherSwappablePss);
dest.writeInt(otherPrivateDirty);
dest.writeInt(otherSharedDirty);
+ dest.writeInt(otherPrivateClean);
+ dest.writeInt(otherSharedClean);
dest.writeIntArray(otherStats);
}
public void readFromParcel(Parcel source) {
dalvikPss = source.readInt();
+ dalvikSwappablePss = source.readInt();
dalvikPrivateDirty = source.readInt();
dalvikSharedDirty = source.readInt();
+ dalvikPrivateClean = source.readInt();
+ dalvikSharedClean = source.readInt();
nativePss = source.readInt();
+ nativeSwappablePss = source.readInt();
nativePrivateDirty = source.readInt();
nativeSharedDirty = source.readInt();
+ nativePrivateClean = source.readInt();
+ nativeSharedClean = source.readInt();
otherPss = source.readInt();
+ otherSwappablePss = source.readInt();
otherPrivateDirty = source.readInt();
otherSharedDirty = source.readInt();
+ otherPrivateClean = source.readInt();
+ otherSharedClean = source.readInt();
otherStats = source.createIntArray();
}
@@ -364,7 +477,7 @@
PrintWriter outStream = null;
try {
FileOutputStream fos = new FileOutputStream(SYSFS_QEMU_TRACE_STATE);
- outStream = new PrintWriter(new OutputStreamWriter(fos));
+ outStream = new FastPrintWriter(fos);
outStream.println("1");
} catch (Exception e) {
} finally {
@@ -392,7 +505,7 @@
PrintWriter outStream = null;
try {
FileOutputStream fos = new FileOutputStream(SYSFS_QEMU_TRACE_STATE);
- outStream = new PrintWriter(new OutputStreamWriter(fos));
+ outStream = new FastPrintWriter(fos);
outStream.println("0");
} catch (Exception e) {
// We could print an error message here but we probably want
@@ -1397,7 +1510,7 @@
/**
* Return a String describing the calling method and location at a particular stack depth.
- * @param callStack the Thread stack
+ * @param callStack the Thread stack
* @param depth the depth of stack to return information for.
* @return the String describing the caller at that depth.
*/
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 61eef1f..70a1edc 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -19,6 +19,7 @@
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
+import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
@@ -59,6 +60,10 @@
private static volatile StorageVolume sPrimaryVolume;
private static StorageVolume getPrimaryVolume() {
+ if (SystemProperties.getBoolean("config.disable_storage", false)) {
+ return null;
+ }
+
if (sPrimaryVolume == null) {
synchronized (sLock) {
if (sPrimaryVolume == null) {
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 9666d9a..97ea99d 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -60,6 +60,8 @@
public static native int setPermissions(String file, int mode, int uid, int gid);
+ public static native int getUid(String file);
+
/** returns the FAT file system volume ID for the volume mounted
* at the given mount point, or -1 for failure
* @param mountPoint point for FAT volume
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 14d8f07..e6886c4 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -73,6 +73,9 @@
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
+ *
+ * @param msg A {@link android.os.Message Message} object
+ * @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 45524c8..30885856 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -344,6 +344,30 @@
void setFirewallUidRule(int uid, boolean allow);
/**
+ * Set all packets from users [uid_start,uid_end] to go through interface iface
+ * iface must already be set for marked forwarding by {@link setMarkedForwarding}
+ */
+ void setUidRangeRoute(String iface, int uid_start, int uid_end);
+
+ /**
+ * Clears the special routing rules for users [uid_start,uid_end]
+ */
+ void clearUidRangeRoute(String iface, int uid_start, int uid_end);
+
+ /**
+ * Setup an interface for routing packets marked by {@link setUidRangeRoute}
+ *
+ * This sets up a dedicated routing table for packets marked for {@code iface} and adds
+ * source-NAT rules so that the marked packets have the correct source address.
+ */
+ void setMarkedForwarding(String iface);
+
+ /**
+ * Removes marked forwarding for an interface
+ */
+ void clearMarkedForwarding(String iface);
+
+ /**
* Set a process (pid) to use the name servers associated with the specified interface.
*/
void setDnsInterfaceForPid(String iface, int pid);
@@ -354,6 +378,21 @@
void clearDnsInterfaceForPid(int pid);
/**
+ * Set a range of user ids to use the name servers associated with the specified interface.
+ */
+ void setDnsInterfaceForUidRange(String iface, int uid_start, int uid_end);
+
+ /**
+ * Clear a user range from being associated with an interface.
+ */
+ void clearDnsInterfaceForUidRange(int uid_start, int uid_end);
+
+ /**
+ * Clear the mappings from pid to Dns interface and from uid range to Dns interface.
+ */
+ void clearDnsInterfaceMaps();
+
+ /**
* Start the clatd (464xlat) service
*/
void startClatd(String interfaceName);
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 6d6d147..23492ff 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -25,7 +25,7 @@
{
// WARNING: The first two methods must remain the first two methods because their
// transaction numbers must not change unless IPowerManager.cpp is also updated.
- void acquireWakeLock(IBinder lock, int flags, String tag, in WorkSource ws);
+ void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws);
void releaseWakeLock(IBinder lock, int flags);
void updateWakeLockWorkSource(IBinder lock, in WorkSource ws);
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index a11358a..7589a5a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -46,4 +46,7 @@
int userHandle);
Bundle getApplicationRestrictions(in String packageName);
Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle);
+ boolean changeRestrictionsPin(in String newPin);
+ int checkRestrictionsPin(in String pin);
+ boolean hasRestrictionsPin();
}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index d5cf771..78c859e 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -289,6 +289,16 @@
return mQueue;
}
+ /**
+ * Return whether this looper's thread is currently idle, waiting for new work
+ * to do. This is intrinsically racy, since its state can change before you get
+ * the result back.
+ * @hide
+ */
+ public boolean isIdling() {
+ return mQueue.isIdling();
+ }
+
public void dump(Printer pw, String prefix) {
pw = PrefixPrinter.create(pw, prefix);
pw.println(this.toString());
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index bf7e5ca..1e8983e 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -52,6 +52,7 @@
private native static void nativeDestroy(int ptr);
private native static void nativePollOnce(int ptr, int timeoutMillis);
private native static void nativeWake(int ptr);
+ private native static boolean nativeIsIdling(int ptr);
/**
* Callback interface for discovering when a thread is going to block
@@ -379,6 +380,10 @@
}
}
+ boolean isIdling() {
+ return nativeIsIdling(mPtr);
+ }
+
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
diff --git a/core/java/android/os/ParcelableParcel.java b/core/java/android/os/ParcelableParcel.java
new file mode 100644
index 0000000..11785f1
--- /dev/null
+++ b/core/java/android/os/ParcelableParcel.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Parcelable containing a raw Parcel of data.
+ * @hide
+ */
+public class ParcelableParcel implements Parcelable {
+ final Parcel mParcel;
+ final ClassLoader mClassLoader;
+
+ public ParcelableParcel(ClassLoader loader) {
+ mParcel = Parcel.obtain();
+ mClassLoader = loader;
+ }
+
+ public ParcelableParcel(Parcel src, ClassLoader loader) {
+ mParcel = Parcel.obtain();
+ mClassLoader = loader;
+ int size = src.readInt();
+ int pos = src.dataPosition();
+ mParcel.appendFrom(src, src.dataPosition(), size);
+ src.setDataPosition(pos + size);
+ }
+
+ public Parcel getParcel() {
+ mParcel.setDataPosition(0);
+ return mParcel;
+ }
+
+ public ClassLoader getClassLoader() {
+ return mClassLoader;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mParcel.dataSize());
+ dest.appendFrom(mParcel, 0, mParcel.dataSize());
+ }
+
+ public static final Parcelable.ClassLoaderCreator<ParcelableParcel> CREATOR
+ = new Parcelable.ClassLoaderCreator<ParcelableParcel>() {
+ public ParcelableParcel createFromParcel(Parcel in) {
+ return new ParcelableParcel(in, null);
+ }
+
+ public ParcelableParcel createFromParcel(Parcel in, ClassLoader loader) {
+ return new ParcelableParcel(in, loader);
+ }
+
+ public ParcelableParcel[] newArray(int size) {
+ return new ParcelableParcel[size];
+ }
+ };
+}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 736762f..52e5f38 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -224,7 +224,7 @@
/**
* Flag for {@link WakeLock#release release(int)} to defer releasing a
- * {@link #WAKE_BIT_PROXIMITY_SCREEN_OFF} wake lock until the proximity sensor returns
+ * {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} wake lock until the proximity sensor returns
* a negative value.
*
* {@hide}
@@ -407,7 +407,7 @@
*/
public WakeLock newWakeLock(int levelAndFlags, String tag) {
validateWakeLockParameters(levelAndFlags, tag);
- return new WakeLock(levelAndFlags, tag);
+ return new WakeLock(levelAndFlags, tag, mContext.getBasePackageName());
}
/** @hide */
@@ -624,6 +624,7 @@
public final class WakeLock {
private final int mFlags;
private final String mTag;
+ private final String mPackageName;
private final IBinder mToken;
private int mCount;
private boolean mRefCounted = true;
@@ -636,9 +637,10 @@
}
};
- WakeLock(int flags, String tag) {
+ WakeLock(int flags, String tag, String packageName) {
mFlags = flags;
mTag = tag;
+ mPackageName = packageName;
mToken = new Binder();
}
@@ -714,7 +716,7 @@
// been explicitly released by the keyguard.
mHandler.removeCallbacks(mReleaser);
try {
- mService.acquireWakeLock(mToken, mFlags, mTag, mWorkSource);
+ mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource);
} catch (RemoteException e) {
}
mHeld = true;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 476b4ea..159d3eb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -650,6 +650,12 @@
public static final native int myPid();
/**
+ * Returns the identifier of this process' parent.
+ * @hide
+ */
+ public static native int myPpid();
+
+ /**
* Returns the identifier of the calling thread, which be used with
* {@link #setThreadPriority(int, int)}.
*/
@@ -970,6 +976,8 @@
/** @hide */
public static final int PROC_PARENS = 0x200;
/** @hide */
+ public static final int PROC_QUOTES = 0x400;
+ /** @hide */
public static final int PROC_OUT_STRING = 0x1000;
/** @hide */
public static final int PROC_OUT_LONG = 0x2000;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 480fe7d..5e20dec 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -244,12 +244,17 @@
// The signature cert matches a trusted key. Now verify that
// the digest in the cert matches the actual file data.
- // The verifier in recovery *only* handles SHA1withRSA
- // signatures. SignApk.java always uses SHA1withRSA, no
- // matter what the cert says to use. Ignore
- // cert.getSigAlgName(), and instead use whatever
- // algorithm is used by the signature (which should be
- // SHA1withRSA).
+ // The verifier in recovery only handles SHA1withRSA and
+ // SHA256withRSA signatures. SignApk chooses which to use
+ // based on the signature algorithm of the cert:
+ //
+ // "SHA256withRSA" cert -> "SHA256withRSA" signature
+ // "SHA1withRSA" cert -> "SHA1withRSA" signature
+ // "MD5withRSA" cert -> "SHA1withRSA" signature (for backwards compatibility)
+ // any other cert -> SignApk fails
+ //
+ // Here we ignore whatever the cert says, and instead use
+ // whatever algorithm is used by the signature.
String da = sigInfo.getDigestAlgorithm();
String dea = sigInfo.getDigestEncryptionAlgorithm();
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index d02a320..c1d4ae9 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -16,6 +16,8 @@
package android.os;
+import android.util.ArrayMap;
+
import java.util.HashMap;
/**
@@ -47,8 +49,8 @@
* implements the {@link #onCallbackDied} method.
*/
public class RemoteCallbackList<E extends IInterface> {
- /*package*/ HashMap<IBinder, Callback> mCallbacks
- = new HashMap<IBinder, Callback>();
+ /*package*/ ArrayMap<IBinder, Callback> mCallbacks
+ = new ArrayMap<IBinder, Callback>();
private Object[] mActiveBroadcast;
private int mBroadcastCount = -1;
private boolean mKilled = false;
@@ -159,7 +161,8 @@
*/
public void kill() {
synchronized (mCallbacks) {
- for (Callback cb : mCallbacks.values()) {
+ for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
+ Callback cb = mCallbacks.valueAt(cbi);
cb.mCallback.asBinder().unlinkToDeath(cb, 0);
}
mCallbacks.clear();
@@ -238,11 +241,10 @@
if (active == null || active.length < N) {
mActiveBroadcast = active = new Object[N];
}
- int i=0;
- for (Callback cb : mCallbacks.values()) {
- active[i++] = cb;
+ for (int i=0; i<N; i++) {
+ active[i] = mCallbacks.valueAt(i);
}
- return i;
+ return N;
}
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 3267939..d794ca6 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Printer;
import android.util.Singleton;
@@ -31,6 +32,7 @@
import com.android.internal.os.RuntimeInit;
+import com.android.internal.util.FastPrintWriter;
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
import dalvik.system.VMDebug;
@@ -782,13 +784,15 @@
BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
return;
}
- BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
- if (!(policy instanceof AndroidBlockGuardPolicy)) {
- BlockGuard.setThreadPolicy(new AndroidBlockGuardPolicy(policyMask));
+ final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+ final AndroidBlockGuardPolicy androidPolicy;
+ if (policy instanceof AndroidBlockGuardPolicy) {
+ androidPolicy = (AndroidBlockGuardPolicy) policy;
} else {
- AndroidBlockGuardPolicy androidPolicy = (AndroidBlockGuardPolicy) policy;
- androidPolicy.setPolicyMask(policyMask);
+ androidPolicy = threadAndroidPolicy.get();
+ BlockGuard.setThreadPolicy(androidPolicy);
}
+ androidPolicy.setPolicyMask(policyMask);
}
// Sets up CloseGuard in Dalvik/libcore
@@ -1059,6 +1063,14 @@
}
};
+ private static final ThreadLocal<AndroidBlockGuardPolicy>
+ threadAndroidPolicy = new ThreadLocal<AndroidBlockGuardPolicy>() {
+ @Override
+ protected AndroidBlockGuardPolicy initialValue() {
+ return new AndroidBlockGuardPolicy(0);
+ }
+ };
+
private static boolean tooManyViolationsThisLoop() {
return violationsBeingTimed.get().size() >= MAX_OFFENSES_PER_LOOP;
}
@@ -1069,7 +1081,7 @@
// Map from violation stacktrace hashcode -> uptimeMillis of
// last violation. No locking needed, as this is only
// accessed by the same thread.
- private final HashMap<Integer, Long> mLastViolationTime = new HashMap<Integer, Long>();
+ private ArrayMap<Integer, Long> mLastViolationTime;
public AndroidBlockGuardPolicy(final int policyMask) {
mPolicyMask = policyMask;
@@ -1279,8 +1291,13 @@
// Not perfect, but fast and good enough for dup suppression.
Integer crashFingerprint = info.hashCode();
long lastViolationTime = 0;
- if (mLastViolationTime.containsKey(crashFingerprint)) {
- lastViolationTime = mLastViolationTime.get(crashFingerprint);
+ if (mLastViolationTime != null) {
+ Long vtime = mLastViolationTime.get(crashFingerprint);
+ if (vtime != null) {
+ lastViolationTime = vtime;
+ }
+ } else {
+ mLastViolationTime = new ArrayMap<Integer, Long>(1);
}
long now = SystemClock.uptimeMillis();
mLastViolationTime.put(crashFingerprint, now);
@@ -1684,7 +1701,9 @@
/* package */ static void readAndHandleBinderCallViolations(Parcel p) {
// Our own stack trace to append
StringWriter sw = new StringWriter();
- new LogStackTrace().printStackTrace(new PrintWriter(sw));
+ PrintWriter pw = new FastPrintWriter(sw, false, 256);
+ new LogStackTrace().printStackTrace(pw);
+ pw.flush();
String ourStack = sw.toString();
int policyMask = getThreadPolicyMask();
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index e53cb5e..bb3d296 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -67,6 +67,8 @@
public static final long TRACE_TAG_RESOURCES = 1L << 13;
/** @hide */
public static final long TRACE_TAG_DALVIK = 1L << 14;
+ /** @hide */
+ public static final long TRACE_TAG_RS = 1L << 15;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index d205253..6e693a4 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -168,8 +168,11 @@
if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
sb.append('i');
sb.append(appId - Process.FIRST_ISOLATED_UID);
- } else {
+ } else if (appId >= Process.FIRST_APPLICATION_UID) {
sb.append('a');
+ sb.append(appId - Process.FIRST_APPLICATION_UID);
+ } else {
+ sb.append('s');
sb.append(appId);
}
}
@@ -190,8 +193,11 @@
if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
pw.print('i');
pw.print(appId - Process.FIRST_ISOLATED_UID);
- } else {
+ } else if (appId >= Process.FIRST_APPLICATION_UID) {
pw.print('a');
+ pw.print(appId - Process.FIRST_APPLICATION_UID);
+ } else {
+ pw.print('s');
pw.print(appId);
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index cb5ed4f..c33a28a 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -140,6 +140,13 @@
*/
public static final String DISALLOW_REMOVE_USER = "no_remove_user";
+ /** @hide */
+ public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
+ /** @hide */
+ public static final int PIN_VERIFICATION_FAILED_NOT_SET = -2;
+ /** @hide */
+ public static final int PIN_VERIFICATION_SUCCESS = -1;
+
private static UserManager sInstance = null;
/** @hide */
@@ -620,4 +627,55 @@
Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier());
}
}
+
+ /**
+ * @hide
+ * Sets a new restrictions PIN. This should only be called after verifying that there
+ * currently isn't a PIN set, or after the user successfully enters the current PIN.
+ * @param newPin
+ * @return Returns true if the PIN was changed successfully.
+ */
+ public boolean changeRestrictionsPin(String newPin) {
+ try {
+ return mService.changeRestrictionsPin(newPin);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not change restrictions pin");
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ * @param pin The PIN to verify, or null to get the number of milliseconds to wait for before
+ * allowing the user to enter the PIN.
+ * @return Returns a positive number (including zero) for how many milliseconds before
+ * you can accept another PIN, when the input is null or the input doesn't match the saved PIN.
+ * Returns {@link #PIN_VERIFICATION_SUCCESS} if the input matches the saved PIN. Returns
+ * {@link #PIN_VERIFICATION_FAILED_NOT_SET} if there is no PIN set.
+ */
+ public int checkRestrictionsPin(String pin) {
+ try {
+ return mService.checkRestrictionsPin(pin);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not check restrictions pin");
+ }
+ return PIN_VERIFICATION_FAILED_INCORRECT;
+ }
+
+ /**
+ * Checks whether the user has restrictions that are PIN-protected. An application that
+ * participates in restrictions can check if the owner has requested a PIN challenge for
+ * any restricted operations. If there is a PIN in effect, the application should launch
+ * the PIN challenge activity {@link android.content.Intent#ACTION_RESTRICTIONS_PIN_CHALLENGE}.
+ * @see android.content.Intent#ACTION_RESTRICTIONS_PIN_CHALLENGE
+ * @return whether a restrictions PIN is in effect.
+ */
+ public boolean hasRestrictionsPin() {
+ try {
+ return mService.hasRestrictionsPin();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not change restrictions pin");
+ }
+ return false;
+ }
}
diff --git a/core/java/android/print/IPrintAdapter.aidl b/core/java/android/print/IPrintAdapter.aidl
new file mode 100644
index 0000000..f3ff8c4
--- /dev/null
+++ b/core/java/android/print/IPrintAdapter.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.os.ParcelFileDescriptor;
+import android.print.IPrintResultCallback;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+
+/**
+ * Interface for communication with the print adapter object.
+ *
+ * @hide
+ */
+oneway interface IPrintAdapter {
+ void start();
+ void printAttributesChanged(in PrintAttributes attributes);
+ void print(in List<PageRange> pages, in ParcelFileDescriptor fd,
+ IPrintResultCallback callback);
+ void finish();
+}
diff --git a/core/java/android/print/IPrintClient.aidl b/core/java/android/print/IPrintClient.aidl
new file mode 100644
index 0000000..3f39d08
--- /dev/null
+++ b/core/java/android/print/IPrintClient.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.content.IntentSender;
+
+/**
+ * Interface for communication with a printing app.
+ *
+ * @see android.print.IPrintClientCallback
+ *
+ * @hide
+ */
+oneway interface IPrintClient {
+ void startPrintJobConfigActivity(in IntentSender intent);
+}
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
new file mode 100644
index 0000000..ff9877e
--- /dev/null
+++ b/core/java/android/print/IPrintManager.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.os.ICancellationSignal;
+import android.print.IPrintAdapter;
+import android.print.IPrintClient;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrinterId;
+import android.print.PrintJobInfo;
+import android.print.PrintAttributes;
+
+/**
+ * Interface for communication with the core print manager service.
+ *
+ * @hide
+ */
+interface IPrintManager {
+ List<PrintJobInfo> getPrintJobs(int appId, int userId);
+ PrintJobInfo getPrintJob(int printJobId, int appId, int userId);
+ PrintJobInfo print(String printJobName, in IPrintClient client, in IPrintAdapter printAdapter,
+ in PrintAttributes attributes, int appId, int userId);
+ void cancelPrintJob(int printJobId, int appId, int userId);
+ void onPrintJobQueued(in PrinterId printerId, in PrintJobInfo printJob);
+ void startDiscoverPrinters(IPrinterDiscoveryObserver observer);
+ void stopDiscoverPrinters();
+}
diff --git a/core/java/android/print/IPrintResultCallback.aidl b/core/java/android/print/IPrintResultCallback.aidl
new file mode 100644
index 0000000..838377e
--- /dev/null
+++ b/core/java/android/print/IPrintResultCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.os.ICancellationSignal;
+import android.print.PageRange;
+import android.print.PrintAdapterInfo;
+
+/**
+ * Callbacks for observing the print progress (writing of printed content)
+ * of a PrintAdapter.
+ *
+ * @hide
+ */
+oneway interface IPrintResultCallback {
+ void onPrintStarted(in PrintAdapterInfo info, ICancellationSignal cancellationSignal);
+ void onPrintFinished(in List<PageRange> pages);
+ void onPrintFailed(CharSequence error);
+}
diff --git a/core/java/android/print/IPrintSpoolerService.aidl b/core/java/android/print/IPrintSpoolerService.aidl
new file mode 100644
index 0000000..e84d592
--- /dev/null
+++ b/core/java/android/print/IPrintSpoolerService.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.content.ComponentName;
+import android.os.ParcelFileDescriptor;
+import android.print.IPrintAdapter;
+import android.print.IPrintClient;
+import android.print.IPrintSpoolerServiceCallbacks;
+import android.print.PrinterInfo;
+import android.print.PrintAttributes;
+
+/**
+ * Interface for communication with the print spooler service.
+ *
+ * @see android.print.IPrintSpoolerServiceCallbacks
+ *
+ * @hide
+ */
+oneway interface IPrintSpoolerService {
+ void getPrintJobs(IPrintSpoolerServiceCallbacks callback, in ComponentName componentName,
+ int state, int appId, int sequence);
+ void getPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ int appId, int sequence);
+ void createPrintJob(String printJobName, in IPrintClient client, in IPrintAdapter printAdapter,
+ in PrintAttributes attributes, IPrintSpoolerServiceCallbacks callback, int appId,
+ int sequence);
+ void cancelPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ int appId, int sequence);
+ void setPrintJobState(int printJobId, int status, IPrintSpoolerServiceCallbacks callback,
+ int sequence);
+ void setPrintJobTag(int printJobId, String tag, IPrintSpoolerServiceCallbacks callback,
+ int sequence);
+ void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
+}
\ No newline at end of file
diff --git a/core/java/android/print/IPrintSpoolerServiceCallbacks.aidl b/core/java/android/print/IPrintSpoolerServiceCallbacks.aidl
new file mode 100644
index 0000000..0c51913
--- /dev/null
+++ b/core/java/android/print/IPrintSpoolerServiceCallbacks.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.print.PrintJobInfo;
+import java.util.List;
+
+/**
+ * Callbacks for communication with the print spooler service.
+ *
+ * @see android.print.IPrintSpoolerService
+ *
+ * @hide
+ */
+oneway interface IPrintSpoolerServiceCallbacks {
+ void onGetPrintJobsResult(in List<PrintJobInfo> printJob, int sequence);
+ void onGetPrintJobInfoResult(in PrintJobInfo printJob, int sequence);
+ void onCreatePrintJobResult(in PrintJobInfo printJob, int sequence);
+ void onCancelPrintJobResult(boolean canceled, int sequence);
+ void onSetPrintJobStateResult(boolean success, int sequence);
+ void onSetPrintJobTagResult(boolean success, int sequence);
+}
diff --git a/core/java/android/print/IPrinterDiscoveryObserver.aidl b/core/java/android/print/IPrinterDiscoveryObserver.aidl
new file mode 100644
index 0000000..39aeb8c
--- /dev/null
+++ b/core/java/android/print/IPrinterDiscoveryObserver.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+
+/**
+ * Interface for observing printer discovery.
+ *
+ * @hide
+ */
+oneway interface IPrinterDiscoveryObserver {
+ void addDiscoveredPrinters(in List<PrinterInfo> printers);
+ void removeDiscoveredPrinters(in List<PrinterId> printers);
+}
diff --git a/core/java/android/print/PageRange.aidl b/core/java/android/print/PageRange.aidl
new file mode 100644
index 0000000..b1ae14f
--- /dev/null
+++ b/core/java/android/print/PageRange.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+parcelable PageRange;
diff --git a/core/java/android/print/PageRange.java b/core/java/android/print/PageRange.java
new file mode 100644
index 0000000..60e6229
--- /dev/null
+++ b/core/java/android/print/PageRange.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a range of pages. The start and end page indices of
+ * the range are zero based and inclusive.
+ */
+public final class PageRange implements Parcelable {
+
+ /**
+ * Constant for specifying all pages.
+ */
+ public static final PageRange ALL_PAGES = new PageRange(0, Integer.MAX_VALUE);
+
+ private final int mStart;
+ private final int mEnd;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param start The start page index (zero based and inclusive).
+ * @param end The end page index (zero based and inclusive).
+ *
+ * @throws IllegalArgumentException If start is less than zero.
+ * @throws IllegalArgumentException If end is less than zero.
+ * @throws IllegalArgumentException If start greater than end.
+ */
+ PageRange(int start, int end) {
+ if (start < 0) {
+ throw new IllegalArgumentException("start cannot be less than zero.");
+ }
+ if (end < 0) {
+ throw new IllegalArgumentException("end cannot be less than zero.");
+ }
+ if (start > end) {
+ throw new IllegalArgumentException("start must be lesser than end.");
+ }
+ mStart = start;
+ mEnd = end;
+ }
+
+ private PageRange (Parcel parcel) {
+ this(parcel.readInt(), parcel.readInt());
+ }
+
+ /**
+ * Gets the start page index (zero based and inclusive).
+ *
+ * @return The start page index.
+ */
+ public int getStart() {
+ return mStart;
+ }
+
+ /**
+ * Gets the end page index (zero based and inclusive).
+ *
+ * @return The end page index.
+ */
+ public int getEnd() {
+ return mEnd;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mStart);
+ parcel.writeInt(mEnd);
+ }
+
+ @Override
+ public String toString() {
+ if (this == ALL_PAGES) {
+ return "PageRange[<all pages>]";
+ }
+ StringBuilder builder = new StringBuilder();
+ builder.append("PageRange[")
+ .append(mStart)
+ .append(" - ")
+ .append(mEnd)
+ .append("]");
+ return builder.toString();
+ }
+
+ public static final Parcelable.Creator<PageRange> CREATOR =
+ new Creator<PageRange>() {
+ @Override
+ public PageRange createFromParcel(Parcel parcel) {
+ return new PageRange(parcel);
+ }
+
+ @Override
+ public PageRange[] newArray(int size) {
+ return new PageRange[size];
+ }
+ };
+}
diff --git a/core/java/android/print/PrintAdapter.java b/core/java/android/print/PrintAdapter.java
new file mode 100644
index 0000000..6547c55
--- /dev/null
+++ b/core/java/android/print/PrintAdapter.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.os.CancellationSignal;
+
+import java.io.FileDescriptor;
+import java.util.List;
+
+/**
+ * Base class that provides data to be printed.
+ *
+ * <h3>Lifecycle</h3>
+ * <p>
+ * <ul>
+ * <li>
+ * You will receive a call on {@link #onStart()} when printing starts.
+ * This callback can be used to allocate resources.
+ * </li>
+ * <li>
+ * Next you will get one or more calls to {@link #onPrintAttributesChanged(
+ * PrintAttributes) to informs you that the print attributes (page size, density,
+ * etc) changed giving you an opportunity to re-layout the content.
+ * </li>
+ * <li>
+ * After every {@link #onPrintAttributesChanged(PrintAttributes) you will receive
+ * one or more calls to {@link #onPrint(List, FileDescriptor, CancellationSignal,
+ * PrintResultCallback)} asking you to write a PDF file with the content for
+ * specific pages.
+ * </li>
+ * <li>
+ * Finally, you will receive a call on {@link #onFinish()} right after printing.
+ * You can use this callback to release resources.
+ * </li>
+ * <li>
+ * You can receive calls to {@link #getInfo()} at any point after a call to
+ * {@link #onPrintAttributesChanged(PrintAttributes)} which should return
+ * a {@link PrintAdapterInfo} describing your {@link PrintAdapter}.
+ * </li>
+ * </ul>
+ * </p>
+ * <p>
+ */
+public abstract class PrintAdapter {
+
+ /**
+ * Called when printing started. You can use this callback to
+ * allocate resources.
+ * <p>
+ * <strong>Note:</strong> Invoked on the main thread.
+ * </p>
+ */
+ public void onStart() {
+ /* do nothing - stub */
+ }
+
+ /**
+ * Called when the print job attributes (page size, density, etc)
+ * changed giving you a chance to re-layout the content such that
+ * it matches the new constraints.
+ * <p>
+ * <strong>Note:</strong> Invoked on the main thread.
+ * </p>
+ *
+ * @param attributes The print job attributes.
+ * @return Whether the content changed based on the provided attributes.
+ */
+ public boolean onPrintAttributesChanged(PrintAttributes attributes) {
+ return false;
+ }
+
+ /**
+ * Called when specific pages of the content have to be printed in the from of
+ * a PDF file to the given file descriptor. You should <strong>not</strong>
+ * close the file descriptor instead you have to invoke {@link PrintResultCallback
+ * #onPrintFinished()} or {@link PrintResultCallback#onPrintFailed(CharSequence)}.
+ * <p>
+ * <strong>Note:</strong> If the printed content is large, it is a good
+ * practice to schedule writing it on a dedicated thread and register a
+ * callback in the provided {@link CancellationSignal} upon invocation of
+ * which you should stop writing data. The cancellation callback will not
+ * be made on the main thread.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> Invoked on the main thread.
+ * </p>
+ *
+ * @param pages The pages whose content to print.
+ * @param destination The destination file descriptor to which to start writing.
+ * @param cancellationSignal Signal for observing cancel print requests.
+ * @param progressListener Callback to inform the system with the write progress.
+ *
+ * @see CancellationSignal
+ */
+ public abstract void onPrint(List<PageRange> pages, FileDescriptor destination,
+ CancellationSignal cancellationSignal, PrintResultCallback progressListener);
+
+ /**
+ * Called when printing finished. You can use this callback to release
+ * resources.
+ * <p>
+ * <strong>Note:</strong> Invoked on the main thread.
+ * </p>
+ */
+ public void onFinish() {
+ /* do nothing - stub */
+ }
+
+ /**
+ * Gets a {@link PrinterInfo} object that contains metadata about the
+ * printed content.
+ * <p>
+ * <strong>Note:</strong> Invoked on the main thread.
+ * </p>
+ *
+ * @return The info object for this {@link PrintAdapter}.
+ *
+ * @see PrintAdapterInfo
+ */
+ public abstract PrintAdapterInfo getInfo();
+
+ /**
+ * Base class for implementing a listener for the print result
+ * of a {@link PrintAdapter}.
+ */
+ public static abstract class PrintResultCallback {
+
+ PrintResultCallback() {
+ /* do nothing - hide constructor */
+ }
+
+ /**
+ * Notifies that all the data was printed.
+ *
+ * @param pages The pages that were printed.
+ */
+ public void onPrintFinished(List<PageRange> pages) {
+ /* do nothing - stub */
+ }
+
+ /**
+ * Notifies that an error occurred while printing the data.
+ *
+ * @param error Error message. May be null if error is unknown.
+ */
+ public void onPrintFailed(CharSequence error) {
+ /* do nothing - stub */
+ }
+ }
+}
diff --git a/core/java/android/print/PrintAdapterInfo.aidl b/core/java/android/print/PrintAdapterInfo.aidl
new file mode 100644
index 0000000..27bf717
--- /dev/null
+++ b/core/java/android/print/PrintAdapterInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+parcelable PrintAdapterInfo;
diff --git a/core/java/android/print/PrintAdapterInfo.java b/core/java/android/print/PrintAdapterInfo.java
new file mode 100644
index 0000000..06e6b10
--- /dev/null
+++ b/core/java/android/print/PrintAdapterInfo.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class encapsulates information about a {@link PrintAdapter} object.
+ */
+public final class PrintAdapterInfo implements Parcelable {
+
+ /**
+ * Constant for unknown page count.
+ */
+ public static final int PAGE_COUNT_UNKNOWN = -1;
+
+ private int mPageCount;
+ private int mFlags;
+
+ /**
+ * Creates a new instance.
+ */
+ private PrintAdapterInfo() {
+ /* do nothing */
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param parcel Data from which to initialize.
+ */
+ private PrintAdapterInfo(Parcel parcel) {
+ mPageCount = parcel.readInt();
+ mFlags = parcel.readInt();
+ }
+
+ /**
+ * Gets the total number of pages.
+ *
+ * @return The number of pages.
+ */
+ public int getPageCount() {
+ return mPageCount;
+ }
+
+ /**
+ * @return The flags of this printable info.
+ *
+ * @see #FLAG_NOTIFY_FOR_ATTRIBUTES_CHANGE
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mPageCount);
+ parcel.writeInt(mFlags);
+ }
+
+ /**
+ * Builder for creating an {@link PrintAdapterInfo}.
+ */
+ public static final class Builder {
+ private final PrintAdapterInfo mPrintableInfo = new PrintAdapterInfo();
+
+ /**
+ * Sets the total number of pages.
+ *
+ * @param pageCount The number of pages. Must be
+ * greater than zero.
+ */
+ public Builder setPageCount(int pageCount) {
+ if (pageCount < 0) {
+ throw new IllegalArgumentException("pageCount"
+ + " must be greater than or euqal to zero!");
+ }
+ mPrintableInfo.mPageCount = pageCount;
+ return this;
+ }
+
+ /**
+ * Sets the flags of this printable info.
+ *
+ * @param flags The flags.
+ *
+ * @see #FLAG_NOTIFY_FOR_ATTRIBUTES_CHANGE
+ */
+ public Builder setFlags(int flags) {
+ mPrintableInfo.mFlags = flags;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link PrintAdapterInfo} instance.
+ *
+ * @return The new instance.
+ */
+ public PrintAdapterInfo create() {
+ return mPrintableInfo;
+ }
+ }
+
+ public static final Parcelable.Creator<PrintAdapterInfo> CREATOR =
+ new Creator<PrintAdapterInfo>() {
+ @Override
+ public PrintAdapterInfo createFromParcel(Parcel parcel) {
+ return new PrintAdapterInfo(parcel);
+ }
+
+ @Override
+ public PrintAdapterInfo[] newArray(int size) {
+ return new PrintAdapterInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/print/PrintAttributes.aidl b/core/java/android/print/PrintAttributes.aidl
new file mode 100644
index 0000000..0b765a2
--- /dev/null
+++ b/core/java/android/print/PrintAttributes.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+parcelable PrintAttributes;
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
new file mode 100644
index 0000000..8511d0b
--- /dev/null
+++ b/core/java/android/print/PrintAttributes.java
@@ -0,0 +1,1226 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources.NotFoundException;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.R;
+
+/**
+ * This class represents the attributes of a print job.
+ */
+public final class PrintAttributes implements Parcelable {
+ /** Duplex mode: No duplexing */
+ public static final int DUPLEX_MODE_NONE = 1 << 0;
+ /** Duplex mode: Turn a page along its long edge, e.g. like a book */
+ public static final int DUPLEX_MODE_LONG_EDGE = 1 << 1;
+ /** Duplex mode: Turn a page along its short edge, e.g. like a notepad */
+ public static final int DUPLEX_MODE_SHORT_EDGE = 1 << 2;
+
+
+ /** Orientation: Portrait page orientation. */
+ public static final int ORIENTATION_PORTRAIT = 1 << 0;
+ /** Orientation: Landscape page orientation. */
+ public static final int ORIENTATION_LANDSCAPE = 1 << 1;
+
+
+ /** Color mode: Monochrome color scheme, e.g. one color is used. */
+ public static final int COLOR_MODE_MONOCHROME = 1 << 0;
+ /** Color mode: Color color scheme, e.g. many colors are used. */
+ public static final int COLOR_MODE_COLOR = 1 << 1;
+
+
+ /** Fitting mode: No fitting. */
+ public static final int FITTING_MODE_NONE = 0x00000001;
+ /** Fitting mode: Fit the content to the page. */
+ public static final int FITTING_MODE_FIT_TO_PAGE = 0x00000002;
+
+
+ private static final int VALID_DUPLEX_MODES =
+ DUPLEX_MODE_NONE | DUPLEX_MODE_LONG_EDGE | DUPLEX_MODE_SHORT_EDGE;
+
+ private static final int VALID_COLOR_MODES =
+ COLOR_MODE_MONOCHROME | COLOR_MODE_COLOR;
+
+ private static final int VALID_FITTING_MODES =
+ FITTING_MODE_NONE | FITTING_MODE_FIT_TO_PAGE;
+
+ private static final int VALID_ORIENTATIONS =
+ ORIENTATION_PORTRAIT | ORIENTATION_LANDSCAPE;
+
+ private MediaSize mMediaSize;
+ private Resolution mResolution;
+ private Margins mMargins;
+ private Tray mInputTray;
+ private Tray mOutputTray;
+
+ private int mDuplexMode;
+ private int mColorMode;
+ private int mFittingMode;
+ private int mOrientation;
+ private int mCopies;
+
+ PrintAttributes() {
+ /* hide constructor */
+ }
+
+ private PrintAttributes(Parcel parcel) {
+ mMediaSize = (parcel.readInt() == 1) ? MediaSize.createFromParcel(parcel) : null;
+ mResolution = (parcel.readInt() == 1) ? Resolution.createFromParcel(parcel) : null;
+ mMargins = (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
+ mInputTray = (parcel.readInt() == 1) ? Tray.createFromParcel(parcel) : null;
+ mOutputTray = (parcel.readInt() == 1) ? Tray.createFromParcel(parcel) : null;
+ mDuplexMode = parcel.readInt();
+ mColorMode = parcel.readInt();
+ mFittingMode = parcel.readInt();
+ mOrientation = parcel.readInt();
+ mCopies = parcel.readInt();
+ }
+
+ /**
+ * Gets the media size.
+ *
+ * @return The media size or <code>null</code> if not set.
+ */
+ public MediaSize getMediaSize() {
+ return mMediaSize;
+ }
+
+ /**
+ * Sets the media size.
+ *
+ * @param The media size.
+ *
+ * @hide
+ */
+ public void setMediaSize(MediaSize mediaSize) {
+ mMediaSize = mediaSize;
+ }
+
+ /**
+ * Gets the resolution.
+ *
+ * @return The resolution or <code>null</code> if not set.
+ */
+ public Resolution getResolution() {
+ return mResolution;
+ }
+
+ /**
+ * Sets the resolution.
+ *
+ * @param The resolution.
+ *
+ * @hide
+ */
+ public void setResolution(Resolution resolution) {
+ mResolution = resolution;
+ }
+
+ /**
+ * Gets the margins.
+ *
+ * @return The margins or <code>null</code> if not set.
+ */
+ public Margins getMargins() {
+ return mMargins;
+ }
+
+ /**
+ * Sets the margins.
+ *
+ * @param The margins.
+ *
+ * @hide
+ */
+ public void setMargins(Margins margins) {
+ mMargins = margins;
+ }
+
+ /**
+ * Sets the input tray.
+ *
+ * @return The input tray or <code>null</code> if not set.
+ */
+ public Tray getInputTray() {
+ return mInputTray;
+ }
+
+ /**
+ * Gets the input tray.
+ *
+ * @param The input tray.
+ *
+ * @hide
+ */
+ public void setInputTray(Tray inputTray) {
+ mInputTray = inputTray;
+ }
+
+ /**
+ * Gets the output tray.
+ *
+ * @return The output tray or <code>null</code> if not set.
+ */
+ public Tray getOutputTray() {
+ return mOutputTray;
+ }
+
+ /**
+ * Sets the output tray.
+ *
+ * @param The output tray.
+ *
+ * @hide
+ */
+ public void setOutputTray(Tray outputTray) {
+ mOutputTray = outputTray;
+ }
+
+ /**
+ * Gets the duplex mode.
+ *
+ * @return The duplex mode or zero if not set.
+ *
+ * @see #DUPLEX_MODE_NONE
+ * @see #DUPLEX_MODE_SHORT_EDGE
+ * @see #DUPLEX_MODE_LONG_EDGE
+ */
+ public int getDuplexMode() {
+ return mDuplexMode;
+ }
+
+ /**
+ * Sets the duplex mode.
+ *
+ * @param The duplex mode.
+ *
+ * @hide
+ */
+ public void setDuplexMode(int duplexMode) {
+ enforceValidDuplexMode(duplexMode);
+ mDuplexMode = duplexMode;
+ }
+
+ /**
+ * Gets the color mode.
+ *
+ * @return The color mode or zero if not set.
+ *
+ * @see #COLOR_MODE_COLOR
+ * @see #COLOR_MODE_MONOCHROME
+ */
+ public int getColorMode() {
+ return mColorMode;
+ }
+
+ /**
+ * Sets the color mode.
+ *
+ * @param The color mode.
+ *
+ * @see #COLOR_MODE_MONOCHROME
+ * @see #COLOR_MODE_COLOR
+ *
+ * @hide
+ */
+ public void setColorMode(int colorMode) {
+ enforceValidColorMode(colorMode);
+ mColorMode = colorMode;
+ }
+
+ /**
+ * Gets the fitting mode.
+ *
+ * @return The fitting mode or zero if not set.
+ *
+ * @see #FITTING_MODE_NONE
+ * @see #FITTING_MODE_FIT_TO_PAGE
+ */
+ public int getFittingMode() {
+ return mFittingMode;
+ }
+
+ /**
+ * Sets the fitting mode.
+ *
+ * @param The fitting mode.
+ *
+ * @see #FITTING_MODE_NONE
+ * @see #FITTING_MODE_FIT_TO_PAGE
+ *
+ * @hide
+ */
+ public void setFittingMode(int fittingMode) {
+ enfoceValidFittingMode(fittingMode);
+ mFittingMode = fittingMode;
+ }
+
+ /**
+ * Gets the orientation.
+ *
+ * @return The orientation or zero if not set.
+ *
+ * @see #ORIENTATION_PORTRAIT
+ * @see #ORIENTATION_LANDSCAPE
+ */
+ public int getOrientation() {
+ return mOrientation;
+ }
+
+ /**
+ * Sets the orientation.
+ *
+ * @param The orientation.
+ *
+ * @see #ORIENTATION_PORTRAIT
+ * @see #ORIENTATION_LANDSCAPE
+ *
+ * @hide
+ */
+ public void setOrientation(int orientation) {
+ enforceValidOrientation(orientation);
+ mOrientation = orientation;
+ }
+
+ /**
+ * Gets the number of copies.
+ *
+ * @return The number of copies or zero if not set.
+ */
+ public int getCopies() {
+ return mCopies;
+ }
+
+ /**
+ * Sets the number of copies.
+ *
+ * @param copyCount The number of copies.
+ *
+ * @hide
+ */
+ public void setCopies(int copyCount) {
+ if (copyCount < 1) {
+ throw new IllegalArgumentException("Copies must be more than one.");
+ }
+ mCopies = copyCount;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ if (mMediaSize != null) {
+ parcel.writeInt(1);
+ mMediaSize.writeToParcel(parcel);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (mResolution != null) {
+ parcel.writeInt(1);
+ mResolution.writeToParcel(parcel);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (mMargins != null) {
+ parcel.writeInt(1);
+ mMargins.writeToParcel(parcel);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (mInputTray != null) {
+ parcel.writeInt(1);
+ mInputTray.writeToParcel(parcel);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (mOutputTray != null) {
+ parcel.writeInt(1);
+ mOutputTray.writeToParcel(parcel);
+ } else {
+ parcel.writeInt(0);
+ }
+ parcel.writeInt(mDuplexMode);
+ parcel.writeInt(mColorMode);
+ parcel.writeInt(mFittingMode);
+ parcel.writeInt(mOrientation);
+ parcel.writeInt(mCopies);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** hide */
+ public void clear() {
+ mMediaSize = null;
+ mResolution = null;
+ mMargins = null;
+ mInputTray = null;
+ mOutputTray = null;
+ mDuplexMode = 0;
+ mColorMode = 0;
+ mFittingMode = 0;
+ mOrientation = 0;
+ mCopies = 0;
+ }
+
+ /**
+ * This class specifies a supported media size.
+ */
+ public static final class MediaSize {
+ private static final String LOG_TAG = "MediaSize";
+
+ // TODO: Verify media sizes and add more standard ones.
+
+ // ISO sizes
+
+ /** ISO A0 media size: 841mm x 1189mm (33.11" x 46.81") */
+ public static final MediaSize ISO_A0 =
+ new MediaSize("ISO_A0", "android", R.string.mediaSize_iso_a0, 33110, 46810);
+ /** ISO A1 media size: 594mm x 841mm (23.39" x 33.11") */
+ public static final MediaSize ISO_A1 =
+ new MediaSize("ISO_A1", "android", R.string.mediaSize_iso_a1, 23390, 33110);
+ /** ISO A2 media size: 420mm x 594mm (16.54" x 23.39") */
+ public static final MediaSize ISO_A2 =
+ new MediaSize("ISO_A2", "android", R.string.mediaSize_iso_a2, 16540, 23390);
+ /** ISO A3 media size: 297mm x 420mm (11.69" x 16.54") */
+ public static final MediaSize ISO_A3 =
+ new MediaSize("ISO_A3", "android", R.string.mediaSize_iso_a3, 11690, 16540);
+ /** ISO A4 media size: 210mm x 297mm (8.27" x 11.69") */
+ public static final MediaSize ISO_A4 =
+ new MediaSize("ISO_A4", "android", R.string.mediaSize_iso_a4, 8270, 11690);
+ /** ISO A5 media size: 148mm x 210mm (5.83" x 8.27") */
+ public static final MediaSize ISO_A5 =
+ new MediaSize("ISO_A5", "android", R.string.mediaSize_iso_a5, 5830, 8270);
+ /** ISO A6 media size: 105mm x 148mm (4.13" x 5.83") */
+ public static final MediaSize ISO_A6 =
+ new MediaSize("ISO_A6", "android", R.string.mediaSize_iso_a6, 4130, 5830);
+ /** ISO A7 media size: 74mm x 105mm (2.91" x 4.13") */
+ public static final MediaSize ISO_A7 =
+ new MediaSize("ISO_A7", "android", R.string.mediaSize_iso_a7, 2910, 4130);
+ /** ISO A8 media size: 52mm x 74mm (2.05" x 2.91") */
+ public static final MediaSize ISO_A8 =
+ new MediaSize("ISO_A8", "android", R.string.mediaSize_iso_a8, 2050, 2910);
+ /** ISO A9 media size: 37mm x 52mm (1.46" x 2.05") */
+ public static final MediaSize ISO_A9 =
+ new MediaSize("ISO_A9", "android", R.string.mediaSize_iso_a9, 1460, 2050);
+ /** ISO A10 media size: 26mm x 37mm (1.02" x 1.46") */
+ public static final MediaSize ISO_A10 =
+ new MediaSize("ISO_A10", "android", R.string.mediaSize_iso_a10, 1020, 1460);
+
+ /** ISO B0 media size: 1000mm x 1414mm (39.37" x 55.67") */
+ public static final MediaSize ISO_B0 =
+ new MediaSize("ISO_B0", "android", R.string.mediaSize_iso_b0, 39370, 55670);
+ /** ISO B1 media size: 707mm x 1000mm (27.83" x 39.37") */
+ public static final MediaSize ISO_B1 =
+ new MediaSize("ISO_B1", "android", R.string.mediaSize_iso_b1, 27830, 39370);
+ /** ISO B2 media size: 500mm x 707mm (19.69" x 27.83") */
+ public static final MediaSize ISO_B2 =
+ new MediaSize("ISO_B2", "android", R.string.mediaSize_iso_b2, 19690, 27830);
+ /** ISO B3 media size: 353mm x 500mm (13.90" x 19.69") */
+ public static final MediaSize ISO_B3 =
+ new MediaSize("ISO_B3", "android", R.string.mediaSize_iso_b3, 13900, 19690);
+ /** ISO B4 media size: 250mm x 353mm (9.84" x 13.90") */
+ public static final MediaSize ISO_B4 =
+ new MediaSize("ISO_B4", "android", R.string.mediaSize_iso_b4, 9840, 13900);
+ /** ISO B5 media size: 176mm x 250mm (6.93" x 9.84") */
+ public static final MediaSize ISO_B5 =
+ new MediaSize("ISO_B5", "android", R.string.mediaSize_iso_b5, 6930, 9840);
+ /** ISO B6 media size: 125mm x 176mm (4.92" x 6.93") */
+ public static final MediaSize ISO_B6 =
+ new MediaSize("ISO_B6", "android", R.string.mediaSize_iso_b6, 4920, 6930);
+ /** ISO B7 media size: 88mm x 125mm (3.46" x 4.92") */
+ public static final MediaSize ISO_B7 =
+ new MediaSize("ISO_B7", "android", R.string.mediaSize_iso_b7, 3460, 4920);
+ /** ISO B8 media size: 62mm x 88mm (2.44" x 3.46") */
+ public static final MediaSize ISO_B8 =
+ new MediaSize("ISO_B8", "android", R.string.mediaSize_iso_b8, 2440, 3460);
+ /** ISO B9 media size: 44mm x 62mm (1.73" x 2.44") */
+ public static final MediaSize ISO_B9 =
+ new MediaSize("ISO_B9", "android", R.string.mediaSize_iso_b9, 1730, 2440);
+ /** ISO B10 media size: 31mm x 44mm (1.22" x 1.73") */
+ public static final MediaSize ISO_B10 =
+ new MediaSize("ISO_B10", "android", R.string.mediaSize_iso_b10, 1220, 1730);
+
+ /** ISO C0 media size: 917mm x 1297mm (36.10" x 51.06") */
+ public static final MediaSize ISO_C0 =
+ new MediaSize("ISO_C0", "android", R.string.mediaSize_iso_c0, 36100, 51060);
+ /** ISO C1 media size: 648mm x 917mm (25.51" x 36.10") */
+ public static final MediaSize ISO_C1 =
+ new MediaSize("ISO_C1", "android", R.string.mediaSize_iso_c1, 25510, 36100);
+ /** ISO C2 media size: 458mm x 648mm (18.03" x 25.51") */
+ public static final MediaSize ISO_C2 =
+ new MediaSize("ISO_C2", "android", R.string.mediaSize_iso_c2, 18030, 25510);
+ /** ISO C3 media size: 324mm x 458mm (12.76" x 18.03") */
+ public static final MediaSize ISO_C3 =
+ new MediaSize("ISO_C3", "android", R.string.mediaSize_iso_c3, 12760, 18030);
+ /** ISO C4 media size: 229mm x 324mm (9.02" x 12.76") */
+ public static final MediaSize ISO_C4 =
+ new MediaSize("ISO_C4", "android", R.string.mediaSize_iso_c4, 9020, 12760);
+ /** ISO C5 media size: 162mm x 229mm (6.38" x 9.02") */
+ public static final MediaSize ISO_C5 =
+ new MediaSize("ISO_C5", "android", R.string.mediaSize_iso_c5, 6380, 9020);
+ /** ISO C6 media size: 114mm x 162mm (4.49" x 6.38") */
+ public static final MediaSize ISO_C6 =
+ new MediaSize("ISO_C6", "android", R.string.mediaSize_iso_c6, 4490, 6380);
+ /** ISO C7 media size: 81mm x 114mm (3.19" x 4.49") */
+ public static final MediaSize ISO_C7 =
+ new MediaSize("ISO_C7", "android", R.string.mediaSize_iso_c7, 3190, 4490);
+ /** ISO C8 media size: 57mm x 81mm (2.24" x 3.19") */
+ public static final MediaSize ISO_C8 =
+ new MediaSize("ISO_C8", "android", R.string.mediaSize_iso_c8, 2240, 3190);
+ /** ISO C9 media size: 40mm x 57mm (1.57" x 2.24") */
+ public static final MediaSize ISO_C9 =
+ new MediaSize("ISO_C9", "android", R.string.mediaSize_iso_c9, 1570, 2240);
+ /** ISO C10 media size: 28mm x 40mm (1.10" x 1.57") */
+ public static final MediaSize ISO_C10 =
+ new MediaSize("ISO_C10", "android", R.string.mediaSize_iso_c10, 1100, 1570);
+
+ // North America
+
+ /** North America Letter media size: 8.5" x 11" */
+ public static final MediaSize NA_LETTER =
+ new MediaSize("NA_LETTER", "android", R.string.mediaSize_na_letter, 8500, 11000);
+ /** North America Government-Letter media size: 8.0" x 10.5" */
+ public static final MediaSize NA_GOVT_LETTER =
+ new MediaSize("NA_GOVT_LETTER", "android",
+ R.string.mediaSize_na_gvrnmt_letter, 8000, 10500);
+ /** North America Legal media size: 8.5" x 14" */
+ public static final MediaSize NA_LEGAL =
+ new MediaSize("NA_LEGAL", "android", R.string.mediaSize_na_legal, 8500, 14000);
+ /** North America Junior Legal media size: 8.0" x 5.0" */
+ public static final MediaSize NA_JUNIOR_LEGAL =
+ new MediaSize("NA_JUNIOR_LEGAL", "android",
+ R.string.mediaSize_na_junior_legal, 8000, 5000);
+ /** North America Ledger media size: 17" x 11" */
+ public static final MediaSize NA_LEDGER =
+ new MediaSize("NA_LEDGER", "android", R.string.mediaSize_na_ledger, 17000, 11000);
+ /** North America Tabloid media size: 11" x 17" */
+ public static final MediaSize NA_TBLOID =
+ new MediaSize("NA_TABLOID", "android",
+ R.string.mediaSize_na_tabloid, 11000, 17000);
+
+ private final String mId;
+ private final String mPackageName;
+ private final int mLabelResId;
+ private final int mWidthMils;
+ private final int mHeightMils;
+
+ /**
+ * Gets the unique media size id.
+ *
+ * @return The unique media size id.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Gets the human readable media size label.
+ *
+ * @return The human readable label.
+ */
+ public CharSequence getLabel(PackageManager packageManager) {
+ try {
+ return packageManager.getResourcesForApplication(
+ mPackageName).getString(mLabelResId);
+ } catch (NotFoundException nfe) {
+ Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
+ + " from package " + mPackageName);
+ } catch (NameNotFoundException nnfee) {
+ Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
+ + " from package " + mPackageName);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the media width in mils (thousands of an inch).
+ *
+ * @return The media width.
+ */
+ public int getWidthMils() {
+ return mWidthMils;
+ }
+
+ /**
+ * Gets the media height in mils (thousands of an inch).
+ *
+ * @return The media height.
+ */
+ public int getHeightMils() {
+ return mHeightMils;
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param id The unique media size id.
+ * @param packageName The name of the creating package.
+ * @param labelResId The resource if of a human readable label.
+ * @param widthMils The width in mils (thousands of an inch).
+ * @param heightMils The height in mils (thousands of an inch).
+ *
+ * @throws IllegalArgumentException If the id is empty.
+ * @throws IllegalArgumentException If the label is empty.
+ * @throws IllegalArgumentException If the widthMils is less than or equal to zero.
+ * @throws IllegalArgumentException If the heightMils is less than or equal to zero.
+ */
+ public MediaSize(String id, String packageName, int labelResId,
+ int widthMils, int heightMils) {
+ if (TextUtils.isEmpty(id)) {
+ throw new IllegalArgumentException("id cannot be empty.");
+ }
+ if (TextUtils.isEmpty(packageName)) {
+ throw new IllegalArgumentException("packageName cannot be empty.");
+ }
+ if (labelResId <= 0) {
+ throw new IllegalArgumentException("labelResId must be greater than zero.");
+ }
+ if (widthMils <= 0) {
+ throw new IllegalArgumentException("widthMils "
+ + "cannot be less than or equal to zero.");
+ }
+ if (heightMils <= 0) {
+ throw new IllegalArgumentException("heightMils "
+ + "cannot be less than or euqual to zero.");
+ }
+ mPackageName = packageName;
+ mId = id;
+ mLabelResId = labelResId;
+ mWidthMils = widthMils;
+ mHeightMils = heightMils;
+ }
+
+ void writeToParcel(Parcel parcel) {
+ parcel.writeString(mId);
+ parcel.writeString(mPackageName);
+ parcel.writeInt(mLabelResId);
+ parcel.writeInt(mWidthMils);
+ parcel.writeInt(mHeightMils);
+ }
+
+ static MediaSize createFromParcel(Parcel parcel) {
+ return new MediaSize(
+ parcel.readString(),
+ parcel.readString(),
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readInt());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("MediaSize{");
+ builder.append("id: ").append(mId);
+ builder.append(", packageName: ").append(mPackageName);
+ builder.append(", labelResId: ").append(mLabelResId);
+ builder.append(", heightMils: ").append(mHeightMils);
+ builder.append(", widthMils: ").append(mWidthMils);
+ builder.append("}");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * This class specifies a supported resolution in dpi (dots per inch).
+ */
+ public static final class Resolution {
+ private static final String LOG_TAG = "Resolution";
+
+ private final String mId;
+ private final String mPackageName;
+ private final int mLabelResId;
+ private final int mHorizontalDpi;
+ private final int mVerticalDpi;
+
+ /**
+ * Gets the unique resolution id.
+ *
+ * @return The unique resolution id.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Gets the resolution human readable label.
+ *
+ * @return The human readable label.
+ */
+ public CharSequence getLabel(PackageManager packageManager) {
+ try {
+ return packageManager.getResourcesForApplication(
+ mPackageName).getString(mLabelResId);
+ } catch (NotFoundException nfe) {
+ Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
+ + " from package " + mPackageName);
+ } catch (NameNotFoundException nnfee) {
+ Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
+ + " from package " + mPackageName);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the horizontal resolution in dpi.
+ *
+ * @return The horizontal resolution.
+ */
+ public int getHorizontalDpi() {
+ return mHorizontalDpi;
+ }
+
+ /**
+ * Gets the vertical resolution in dpi.
+ *
+ * @return The vertical resolution.
+ */
+ public int getVerticalDpi() {
+ return mVerticalDpi;
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param id The unique resolution id.
+ * @param packageName The name of the creating package.
+ * @param labelResId The resource id of a human readable label.
+ * @param horizontalDpi The horizontal resolution in dpi.
+ * @param verticalDpi The vertical resolution in dpi.
+ *
+ * @throws IllegalArgumentException If the id is empty.
+ * @throws IllegalArgumentException If the label is empty.
+ * @throws IllegalArgumentException If the horizontalDpi is less than or equal to zero.
+ * @throws IllegalArgumentException If the verticalDpi is less than or equal to zero.
+ */
+ public Resolution(String id, String packageName, int labelResId,
+ int horizontalDpi, int verticalDpi) {
+ if (TextUtils.isEmpty(id)) {
+ throw new IllegalArgumentException("id cannot be empty.");
+ }
+ if (TextUtils.isEmpty(packageName)) {
+ throw new IllegalArgumentException("packageName cannot be empty.");
+ }
+ if (labelResId <= 0) {
+ throw new IllegalArgumentException("labelResId must be greater than zero.");
+ }
+ if (horizontalDpi <= 0) {
+ throw new IllegalArgumentException("horizontalDpi "
+ + "cannot be less than or equal to zero.");
+ }
+ if (verticalDpi <= 0) {
+ throw new IllegalArgumentException("verticalDpi"
+ + " cannot be less than or equal to zero.");
+ }
+ mId = id;
+ mPackageName = packageName;
+ mLabelResId = labelResId;
+ mHorizontalDpi = horizontalDpi;
+ mVerticalDpi = verticalDpi;
+ }
+
+ void writeToParcel(Parcel parcel) {
+ parcel.writeString(mId);
+ parcel.writeString(mPackageName);
+ parcel.writeInt(mLabelResId);
+ parcel.writeInt(mHorizontalDpi);
+ parcel.writeInt(mVerticalDpi);
+ }
+
+ static Resolution createFromParcel(Parcel parcel) {
+ return new Resolution(
+ parcel.readString(),
+ parcel.readString(),
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readInt());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Resolution{");
+ builder.append("id: ").append(mId);
+ builder.append(", packageName: ").append(mPackageName);
+ builder.append(", labelResId: ").append(mLabelResId);
+ builder.append(", horizontalDpi: ").append(mHorizontalDpi);
+ builder.append(", verticalDpi: ").append(mVerticalDpi);
+ builder.append("}");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * This class specifies content margins.
+ */
+ public static final class Margins {
+ private final int mLeftMils;
+ private final int mTopMils;
+ private final int mRightMils;
+ private final int mBottomMils;
+
+ /**
+ * Gets the left margin in mils (thousands of an inch).
+ *
+ * @return The left margin.
+ */
+ public int getLeftMils() {
+ return mLeftMils;
+ }
+
+ /**
+ * Gets the top margin in mils (thousands of an inch).
+ *
+ * @return The top margin.
+ */
+ public int getTopMils() {
+ return mTopMils;
+ }
+
+ /**
+ * Gets the right margin in mils (thousands of an inch).
+ *
+ * @return The right margin.
+ */
+ public int getRightMils() {
+ return mRightMils;
+ }
+
+ /**
+ * Gets the bottom margin in mils (thousands of an inch).
+ *
+ * @return The bottom margin.
+ */
+ public int getBottomMils() {
+ return mBottomMils;
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param leftMils The left margin in mils (thousands of an inch).
+ * @param topMils The top margin in mils (thousands of an inch).
+ * @param rightMils The right margin in mils (thousands of an inch).
+ * @param bottomMils The bottom margin in mils (thousands of an inch).
+ *
+ * @throws IllegalArgumentException If the leftMils is less than zero.
+ * @throws IllegalArgumentException If the topMils is less than zero.
+ * @throws IllegalArgumentException If the rightMils is less than zero.
+ * @throws IllegalArgumentException If the bottomMils is less than zero.
+ */
+ public Margins(int leftMils, int topMils, int rightMils, int bottomMils) {
+ if (leftMils < 0) {
+ throw new IllegalArgumentException("leftMils cannot be less than zero.");
+ }
+ if (topMils < 0) {
+ throw new IllegalArgumentException("topMils cannot be less than zero.");
+ }
+ if (rightMils < 0) {
+ throw new IllegalArgumentException("rightMils cannot be less than zero.");
+ }
+ if (bottomMils < 0) {
+ throw new IllegalArgumentException("bottomMils cannot be less than zero.");
+ }
+ mTopMils = topMils;
+ mLeftMils = leftMils;
+ mRightMils = rightMils;
+ mBottomMils = bottomMils;
+ }
+
+ void writeToParcel(Parcel parcel) {
+ parcel.writeInt(mLeftMils);
+ parcel.writeInt(mTopMils);
+ parcel.writeInt(mRightMils);
+ parcel.writeInt(mBottomMils);
+ }
+
+ static Margins createFromParcel(Parcel parcel) {
+ return new Margins(
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readInt());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Margins{");
+ builder.append("leftMils: ").append(mLeftMils);
+ builder.append(", topMils: ").append(mTopMils);
+ builder.append(", rightMils: ").append(mRightMils);
+ builder.append(", bottomMils: ").append(mBottomMils);
+ builder.append("}");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Represents a printer tray.
+ */
+ public static final class Tray {
+ private static final String LOG_TAG = "Tray";
+
+ private final String mId;
+ private final String mPackageName;
+ private final int mLabelResId;
+
+ /**
+ * Gets the unique tray id.
+ *
+ * @return The unique tray id.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Gets the tray human readable label.
+ *
+ * @return The human readable label.
+ */
+ public CharSequence getLabel(PackageManager packageManager) {
+ try {
+ return packageManager.getResourcesForApplication(
+ mPackageName).getString(mLabelResId);
+ } catch (NotFoundException nfe) {
+ Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
+ + " from package " + mPackageName);
+ } catch (NameNotFoundException nnfee) {
+ Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
+ + " from package " + mPackageName);
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param id The unique tray id.
+ * @param packageName The name of the creating package.
+ * @param labelResId The resource id of a human readable label.
+ *
+ * @throws IllegalArgumentException If the id is empty.
+ * @throws IllegalArgumentException If the label is empty.
+ */
+ public Tray(String id, String packageName, int labelResId) {
+ if (TextUtils.isEmpty(id)) {
+ throw new IllegalArgumentException("id cannot be empty.");
+ }
+ if (TextUtils.isEmpty(packageName)) {
+ throw new IllegalArgumentException("packageName cannot be empty.");
+ }
+ if (labelResId <= 0) {
+ throw new IllegalArgumentException("label must be greater than zero.");
+ }
+ mId = id;
+ mPackageName = packageName;
+ mLabelResId = labelResId;
+ }
+
+ void writeToParcel(Parcel parcel) {
+ parcel.writeString(mId);
+ parcel.writeString(mPackageName);
+ parcel.writeInt(mLabelResId);
+ }
+
+ static Tray createFromParcel(Parcel parcel) {
+ return new Tray(
+ parcel.readString(),
+ parcel.readString(),
+ parcel.readInt());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Tray{");
+ builder.append("id: ").append(mId);
+ builder.append("id: ").append(mId);
+ builder.append(", packageName: ").append(mPackageName);
+ builder.append(", labelResId: ").append(mLabelResId);
+ builder.append("}");
+ return builder.toString();
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PrintAttributes{");
+ builder.append("mediaSize: ").append(mMediaSize);
+ builder.append(", resolution: ").append(mResolution);
+ builder.append(", margins: ").append(mMargins);
+ builder.append(", duplexMode: ").append(duplexModeToString(mDuplexMode));
+ builder.append(", colorMode: ").append(colorModeToString(mColorMode));
+ builder.append(", fittingMode: ").append(fittingModeToString(mFittingMode));
+ builder.append(", orientation: ").append(orientationToString(mOrientation));
+ builder.append(", copies: ").append(mCopies);
+ return builder.toString();
+ }
+
+ private static String duplexModeToString(int duplexMode) {
+ switch (duplexMode) {
+ case DUPLEX_MODE_NONE: {
+ return "DUPLEX_MODE_NONE";
+ }
+ case DUPLEX_MODE_LONG_EDGE: {
+ return "DUPLEX_MODE_LONG_EDGE";
+ }
+ case DUPLEX_MODE_SHORT_EDGE: {
+ return "DUPLEX_MODE_SHORT_EDGE";
+ }
+ default:
+ return "DUPLEX_MODE_UNKNOWN";
+ }
+ }
+
+ private static String colorModeToString(int colorMode) {
+ switch (colorMode) {
+ case COLOR_MODE_MONOCHROME: {
+ return "COLOR_MODE_MONOCHROME";
+ }
+ case COLOR_MODE_COLOR: {
+ return "COLOR_MODE_COLOR";
+ }
+ default:
+ return "COLOR_MODE_UNKNOWN";
+ }
+ }
+
+ private static String orientationToString(int orientation) {
+ switch (orientation) {
+ case ORIENTATION_PORTRAIT: {
+ return "ORIENTATION_PORTRAIT";
+ }
+ case ORIENTATION_LANDSCAPE: {
+ return "ORIENTATION_LANDSCAPE";
+ }
+ default:
+ return "ORIENTATION_UNKNOWN";
+ }
+ }
+
+ private static String fittingModeToString(int fittingMode) {
+ switch (fittingMode) {
+ case FITTING_MODE_NONE: {
+ return "FITTING_MODE_NONE";
+ }
+ case FITTING_MODE_FIT_TO_PAGE: {
+ return "FITTING_MODE_FIT_TO_PAGE";
+ }
+ default:
+ return "FITTING_MODE_UNKNOWN";
+ }
+ }
+
+ static void enforceValidDuplexMode(int duplexMode) {
+ if ((duplexMode & VALID_DUPLEX_MODES) == 0) {
+ throw new IllegalArgumentException("invalid duplex mode: " + duplexMode);
+ }
+ }
+
+ static void enforceValidColorMode(int colorMode) {
+ if ((colorMode & VALID_COLOR_MODES) == 0) {
+ throw new IllegalArgumentException("invalid color mode: " + colorMode);
+ }
+ }
+
+ static void enfoceValidFittingMode(int fittingMode) {
+ if ((fittingMode & VALID_FITTING_MODES) == 0) {
+ throw new IllegalArgumentException("invalid fitting mode: " + fittingMode);
+ }
+ }
+
+ static void enforceValidOrientation(int orientation) {
+ if ((orientation & VALID_ORIENTATIONS) == 0) {
+ throw new IllegalArgumentException("invalid orientation: " + orientation);
+ }
+ }
+
+ /**
+ * Builder for creating {@link PrintAttributes}.
+ */
+ public static final class Builder {
+ private final PrintAttributes mAttributes = new PrintAttributes();
+
+ /**
+ * Sets the media size.
+ *
+ * @param mediaSize The media size.
+ * @return This builder.
+ */
+ public Builder setMediaSize(MediaSize mediaSize) {
+ mAttributes.setMediaSize(mediaSize);
+ return this;
+ }
+
+ /**
+ * Sets the resolution.
+ *
+ * @param resolution The resolution.
+ * @return This builder.
+ */
+ public Builder setResolution(Resolution resolution) {
+ mAttributes.setResolution(resolution);
+ return this;
+ }
+
+ /**
+ * Sets the margins.
+ *
+ * @param margins The margins.
+ * @return This builder.
+ */
+ public Builder setMargins(Margins margins) {
+ mAttributes.setMargins(margins);
+ return this;
+ }
+
+ /**
+ * Sets the input tray.
+ *
+ * @param inputTray The tray.
+ * @return This builder.
+ */
+ public Builder setInputTray(Tray inputTray) {
+ mAttributes.setInputTray(inputTray);
+ return this;
+ }
+
+ /**
+ * Sets the output tray.
+ *
+ * @param outputTray The tray.
+ * @return This builder.
+ */
+ public Builder setOutputTray(Tray outputTray) {
+ mAttributes.setOutputTray(outputTray);
+ return this;
+ }
+
+ /**
+ * Sets the duplex mode.
+ *
+ * @param duplexMode A valid duplex mode or zero.
+ * @return This builder.
+ *
+ * @see PrintAttributes#DUPLEX_MODE_NONE
+ * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
+ * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
+ */
+ public Builder setDuplexMode(int duplexMode) {
+ if (Integer.bitCount(duplexMode) != 1) {
+ throw new IllegalArgumentException("can specify at most one duplexMode bit.");
+ }
+ mAttributes.setDuplexMode(duplexMode);
+ return this;
+ }
+
+ /**
+ * Sets the color mode.
+ *
+ * @param colorMode A valid color mode or zero.
+ * @return This builder.
+ *
+ * @see PrintAttributes#COLOR_MODE_MONOCHROME
+ * @see PrintAttributes#COLOR_MODE_COLOR
+ */
+ public Builder setColorMode(int colorMode) {
+ if (Integer.bitCount(colorMode) > 1) {
+ throw new IllegalArgumentException("can specify at most one colorMode bit.");
+ }
+ mAttributes.setColorMode(colorMode);
+ return this;
+ }
+
+ /**
+ * Sets the fitting mode.
+ *
+ * @param fittingMode A valid fitting mode or zero.
+ * @return This builder.
+ *
+ * @see PrintAttributes#FITTING_MODE_NONE
+ * @see PrintAttributes#FITTING_MODE_FIT_TO_PAGE
+ */
+ public Builder setFittingMode(int fittingMode) {
+ if (Integer.bitCount(fittingMode) > 1) {
+ throw new IllegalArgumentException("can specify at most one fittingMode bit.");
+ }
+ mAttributes.setFittingMode(fittingMode);
+ return this;
+ }
+
+ /**
+ * Sets the orientation.
+ *
+ * @param orientation A valid orientation or zero.
+ * @return This builder.
+ *
+ * @see PrintAttributes#ORIENTATION_PORTRAIT
+ * @see PrintAttributes#ORIENTATION_LANDSCAPE
+ */
+ public Builder setOrientation(int orientation) {
+ if (Integer.bitCount(orientation) > 1) {
+ throw new IllegalArgumentException("can specify at most one orientation bit.");
+ }
+ mAttributes.setOrientation(orientation);
+ return this;
+ }
+
+ /**
+ * Sets the number of copies.
+ *
+ * @param copyCount A greater or equal to zero copy count.
+ * @return This builder.
+ */
+ public Builder setCopyCount(int copyCount) {
+ mAttributes.setCopies(copyCount);
+ return this;
+ }
+
+ /**
+ * Creates a new {@link PrintAttributes} instance.
+ *
+ * @return The new instance.
+ */
+ public PrintAttributes create() {
+ return mAttributes;
+ }
+ }
+
+ public static final Parcelable.Creator<PrintAttributes> CREATOR =
+ new Creator<PrintAttributes>() {
+ @Override
+ public PrintAttributes createFromParcel(Parcel parcel) {
+ return new PrintAttributes(parcel);
+ }
+
+ @Override
+ public PrintAttributes[] newArray(int size) {
+ return new PrintAttributes[size];
+ }
+ };
+}
diff --git a/core/java/android/print/PrintFileAdapter.java b/core/java/android/print/PrintFileAdapter.java
new file mode 100644
index 0000000..dab9648
--- /dev/null
+++ b/core/java/android/print/PrintFileAdapter.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.os.AsyncTask;
+import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adapter for printing files.
+ */
+class PrintFileAdapter extends PrintAdapter {
+
+ private static final String LOG_TAG = "PrintFileAdapter";
+
+ private final File mFile;
+
+ private WriteFileAsyncTask mWriteFileAsyncTask;
+
+ public PrintFileAdapter(File file) {
+ if (file == null) {
+ throw new IllegalArgumentException("File cannot be null!");
+ }
+ mFile = file;
+ }
+
+ @Override
+ public void onPrint(List<PageRange> pages, FileDescriptor destination,
+ CancellationSignal cancellationSignal, PrintResultCallback callback) {
+ mWriteFileAsyncTask = new WriteFileAsyncTask(mFile, destination, cancellationSignal,
+ callback);
+ mWriteFileAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
+ (Void[]) null);
+
+ }
+
+ @Override
+ public PrintAdapterInfo getInfo() {
+ // TODO: When we have PDF render library we should query the page count.
+ return new PrintAdapterInfo.Builder().create();
+ }
+
+ private static final class WriteFileAsyncTask extends AsyncTask<Void, Void, Void> {
+
+ private final File mSource;
+
+ private final FileDescriptor mDestination;
+
+ private final PrintResultCallback mResultCallback;
+
+ private final CancellationSignal mCancellationSignal;
+
+ public WriteFileAsyncTask(File source, FileDescriptor destination,
+ CancellationSignal cancellationSignal, PrintResultCallback callback) {
+ mSource = source;
+ mDestination = destination;
+ mResultCallback = callback;
+ mCancellationSignal = cancellationSignal;
+ mCancellationSignal.setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel() {
+ cancel(true);
+ }
+ });
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ InputStream in = null;
+ OutputStream out = new FileOutputStream(mDestination);
+ final byte[] buffer = new byte[8192];
+ try {
+ in = new FileInputStream(mSource);
+ while (true) {
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ break;
+ }
+ out.write(buffer, 0, readByteCount);
+ }
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error writing data!", ioe);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ if (!isCancelled()) {
+ List<PageRange> pages = new ArrayList<PageRange>();
+ pages.add(PageRange.ALL_PAGES);
+ mResultCallback.onPrintFinished(pages);
+ } else {
+ mResultCallback.onPrintFailed("Cancelled");
+ }
+ }
+ return null;
+ }
+ }
+}
+
diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java
new file mode 100644
index 0000000..f7cca87
--- /dev/null
+++ b/core/java/android/print/PrintJob.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+
+/**
+ * This class represents a print job from the perspective of
+ * an application.
+ */
+public final class PrintJob {
+
+ private final int mId;
+
+ private final PrintManager mPrintManager;
+
+ private PrintJobInfo mCachedInfo;
+
+ PrintJob(PrintJobInfo info, PrintManager printManager) {
+ mCachedInfo = info;
+ mPrintManager = printManager;
+ mId = info.getId();
+ }
+
+ /**
+ * Gets the unique print job id.
+ *
+ * @return The id.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Gets the {@link PrintJobInfo} that describes this job.
+ * <p>
+ * <strong>Node:</strong>The returned info object is a snapshot of the
+ * current print job state. Every call to this method returns a fresh
+ * info object that reflects the current print job state.
+ * </p>
+ *
+ * @return The print job info.
+ */
+ public PrintJobInfo getInfo() {
+ PrintJobInfo info = mPrintManager.getPrintJob(mId);
+ if (info != null) {
+ mCachedInfo = info;
+ }
+ return mCachedInfo;
+ }
+
+ /**
+ * Cancels this print job.
+ */
+ public void cancel() {
+ mPrintManager.cancelPrintJob(mId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ PrintJob other = (PrintJob) obj;
+ return mId == other.mId;
+ }
+
+ @Override
+ public int hashCode() {
+ return mId;
+ }
+}
diff --git a/core/java/android/print/PrintJobInfo.aidl b/core/java/android/print/PrintJobInfo.aidl
new file mode 100644
index 0000000..fbca99c
--- /dev/null
+++ b/core/java/android/print/PrintJobInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+parcelable PrintJobInfo;
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
new file mode 100644
index 0000000..72d6057
--- /dev/null
+++ b/core/java/android/print/PrintJobInfo.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the description of a print job.
+ */
+public final class PrintJobInfo implements Parcelable {
+
+ /** Undefined print job id. */
+ public static final int PRINT_JOB_ID_UNDEFINED = -1;
+
+ /**
+ * Constant for matching any print job state.
+ *
+ * @hide
+ */
+ public static final int STATE_ANY = -1;
+
+ /**
+ * Print job state: The print job is being created but not yet
+ * ready to be printed.
+ * <p>
+ * Next valid states: {@link #STATE_QUEUED}
+ * </p>
+ */
+ public static final int STATE_CREATED = 1;
+
+ /**
+ * Print job status: The print jobs is created, it is ready
+ * to be printed and should be processed.
+ * <p>
+ * Next valid states: {@link #STATE_STARTED}, {@link #STATE_FAILED},
+ * {@link #STATE_CANCELED}
+ * </p>
+ */
+ public static final int STATE_QUEUED = 2;
+
+ /**
+ * Print job status: The print job is being printed.
+ * <p>
+ * Next valid states: {@link #STATE_COMPLETED}, {@link #STATE_FAILED},
+ * {@link #STATE_CANCELED}
+ * </p>
+ */
+ public static final int STATE_STARTED = 3;
+
+ /**
+ * Print job status: The print job was successfully printed.
+ * This is a terminal state.
+ * <p>
+ * Next valid states: None
+ * </p>
+ */
+ public static final int STATE_COMPLETED = 4;
+
+ /**
+ * Print job status: The print job was printing but printing failed.
+ * This is a terminal state.
+ * <p>
+ * Next valid states: None
+ * </p>
+ */
+ public static final int STATE_FAILED = 5;
+
+ /**
+ * Print job status: The print job was canceled.
+ * This is a terminal state.
+ * <p>
+ * Next valid states: None
+ * </p>
+ */
+ public static final int STATE_CANCELED = 6;
+
+ /** The unique print job id. */
+ private int mId;
+
+ /** The human readable print job label. */
+ private CharSequence mLabel;
+
+ /** The unique id of the printer. */
+ private PrinterId mPrinterId;
+
+ /** The status of the print job. */
+ private int mState;
+
+ /** The id of the app that created the job. */
+ private int mAppId;
+
+ /** The id of the user that created the job. */
+ private int mUserId;
+
+ /** Optional tag assigned by a print service.*/
+ private String mTag;
+
+ /** The pages to print */
+ private PageRange[] mPageRanges;
+
+ /** The print job attributes size. */
+ private PrintAttributes mAttributes;
+
+ /**
+ * Gets the unique print job id.
+ *
+ * @return The id.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Sets the unique print job id.
+ *
+ * @param The job id.
+ *
+ * @hide
+ */
+ public void setId(int id) {
+ this.mId = id;
+ }
+
+ /**
+ * Gets the human readable job label.
+ *
+ * @return The label.
+ */
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * Sets the human readable job label.
+ *
+ * @param label The label.
+ *
+ * @hide
+ */
+ public void setLabel(CharSequence label) {
+ mLabel = label;
+ }
+
+ /**
+ * Gets the unique target printer id.
+ *
+ * @return The target printer id.
+ */
+ public PrinterId getPrinterId() {
+ return mPrinterId;
+ }
+
+ /**
+ * Sets the unique target pritner id.
+ *
+ * @param printerId The target printer id.
+ *
+ * @hide
+ */
+ public void setPrinterId(PrinterId printerId) {
+ mPrinterId = printerId;
+ }
+
+ /**
+ * Gets the current job state.
+ *
+ * @return The job state.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Sets the current job state.
+ *
+ * @param state The job state.
+ *
+ * @hide
+ */
+ public void setState(int state) {
+ mState = state;
+ }
+
+ /**
+ * Sets the owning application id.
+ *
+ * @return The owning app id.
+ *
+ * @hide
+ */
+ public int getAppId() {
+ return mAppId;
+ }
+
+ /**
+ * Sets the owning application id.
+ *
+ * @param appId The owning app id.
+ *
+ * @hide
+ */
+ public void setAppId(int appId) {
+ mAppId = appId;
+ }
+
+ /**
+ * Gets the owning user id.
+ *
+ * @return The user id.
+ *
+ * @hide
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * Sets the owning user id.
+ *
+ * @param userId The user id.
+ *
+ * @hide
+ */
+ public void setUserId(int userId) {
+ mUserId = userId;
+ }
+
+ /**
+ * Gets the optional tag assigned by a print service.
+ *
+ * @return The tag.
+ */
+ public String getTag() {
+ return mTag;
+ }
+
+ /**
+ * Sets the optional tag assigned by a print service.
+ *
+ * @param tag The tag.
+ *
+ * @hide
+ */
+ public void setTag(String tag) {
+ mTag = tag;
+ }
+
+ /**
+ * Gets the included pages.
+ *
+ * @return The included pages or <code>null</code> if not set.
+ */
+ public PageRange[] getPages() {
+ return mPageRanges;
+ }
+
+ /**
+ * Sets the included pages.
+ *
+ * @return The included pages.
+ *
+ * @hide
+ */
+ public void setPages(PageRange[] pageRanges) {
+ mPageRanges = pageRanges;
+ }
+
+ /**
+ * Gets the print job attributes.
+ *
+ * @return The attributes.
+ */
+ public PrintAttributes getAttributes() {
+ return mAttributes;
+ }
+
+ /**
+ * Sets the print job attributes.
+ *
+ * @param attributes The attributes.
+ *
+ * @hide
+ */
+ public void setAttributes(PrintAttributes attributes) {
+ mAttributes = attributes;
+ }
+
+ /** @hide*/
+ public PrintJobInfo() {
+ /* do nothing */
+ }
+
+ /** @hide */
+ public PrintJobInfo(PrintJobInfo other) {
+ mId = other.mId;
+ mLabel = other.mLabel;
+ mPrinterId = other.mPrinterId;
+ mState = other.mState;
+ mAppId = other.mAppId;
+ mUserId = other.mUserId;
+ mAttributes = other.mAttributes;
+ }
+
+ private PrintJobInfo(Parcel parcel) {
+ mId = parcel.readInt();
+ mLabel = parcel.readCharSequence();
+ mPrinterId = parcel.readParcelable(null);
+ mState = parcel.readInt();
+ mAppId = parcel.readInt();
+ mUserId = parcel.readInt();
+ if (parcel.readInt() == 1) {
+ mPageRanges = (PageRange[]) parcel.readParcelableArray(null);
+ }
+ if (parcel.readInt() == 1) {
+ mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mId);
+ parcel.writeCharSequence(mLabel);
+ parcel.writeParcelable(mPrinterId, flags);
+ parcel.writeInt(mState);
+ parcel.writeInt(mAppId);
+ parcel.writeInt(mUserId);
+ if (mPageRanges != null) {
+ parcel.writeInt(1);
+ parcel.writeParcelableArray(mPageRanges, flags);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (mAttributes != null) {
+ parcel.writeInt(1);
+ mAttributes.writeToParcel(parcel, flags);
+ } else {
+ parcel.writeInt(0);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PrintJobInfo{");
+ builder.append("label: ").append(mLabel);
+ builder.append(", id: ").append(mId);
+ builder.append(", status: ").append(stateToString(mState));
+ builder.append(", printer: " + mPrinterId);
+ builder.append(", attributes: " + (mAttributes != null ? mAttributes.toString() : null));
+ builder.append("}");
+ return builder.toString();
+ }
+
+ /** @hide */
+ public static String stateToString(int state) {
+ switch (state) {
+ case STATE_CREATED: {
+ return "STATUS_CREATED";
+ }
+ case STATE_QUEUED: {
+ return "STATE_QUEUED";
+ }
+ case STATE_STARTED: {
+ return "STATE_STARTED";
+ }
+ case STATE_FAILED: {
+ return "STATUS_FAILED";
+ }
+ case STATE_COMPLETED: {
+ return "STATUS_COMPLETED";
+ }
+ case STATE_CANCELED: {
+ return "STATUS_CANCELED";
+ }
+ default: {
+ return "STATUS_UNKNOWN";
+ }
+ }
+ }
+
+
+ public static final Parcelable.Creator<PrintJobInfo> CREATOR =
+ new Creator<PrintJobInfo>() {
+ @Override
+ public PrintJobInfo createFromParcel(Parcel parcel) {
+ return new PrintJobInfo(parcel);
+ }
+
+ @Override
+ public PrintJobInfo[] newArray(int size) {
+ return new PrintJobInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
new file mode 100644
index 0000000..be9b596
--- /dev/null
+++ b/core/java/android/print/PrintManager.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.IntentSender.SendIntentException;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.PrintAdapter.PrintResultCallback;
+import android.util.Log;
+
+import com.android.internal.os.SomeArgs;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * System level service for accessing the printing capabilities of the platform.
+ * <p>
+ * To obtain a handle to the print manager do the following:
+ * </p>
+ * <pre>
+ * PrintManager printManager =
+ * (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
+ * </pre>
+ */
+public final class PrintManager {
+
+ private static final String LOG_TAG = "PrintManager";
+
+ /** @hide */
+ public static final int APP_ID_ANY = -2;
+
+ private final Context mContext;
+
+ private final IPrintManager mService;
+
+ private final int mUserId;
+
+ private final int mAppId;
+
+ private final PrintClient mPrintClient;
+
+ private final Handler mHandler;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context The current context in which to operate.
+ * @param service The backing system service.
+ *
+ * @hide
+ */
+ public PrintManager(Context context, IPrintManager service, int userId, int appId) {
+ mContext = context;
+ mService = service;
+ mUserId = userId;
+ mAppId = appId;
+ mPrintClient = new PrintClient(this);
+ mHandler = new Handler(context.getMainLooper(), null, false) {
+ @Override
+ public void handleMessage(Message message) {
+ SomeArgs args = (SomeArgs) message.obj;
+ Context context = (Context) args.arg1;
+ IntentSender intent = (IntentSender) args.arg2;
+ args.recycle();
+ try {
+ context.startIntentSender(intent, null, 0, 0, 0);
+ } catch (SendIntentException sie) {
+ Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
+ }
+ }
+ };
+ }
+
+ /**
+ * Creates an instance that can access all print jobs.
+ *
+ * @param userId The user id for which to get all print jobs.
+ * @return An instance of the caller has the permission to access
+ * all print jobs, null otherwise.
+ *
+ * @hide
+ */
+ public PrintManager getGlobalPrintManagerForUser(int userId) {
+ return new PrintManager(mContext, mService, userId, APP_ID_ANY);
+ }
+
+ PrintJobInfo getPrintJob(int printJobId) {
+ try {
+ return mService.getPrintJob(printJobId, mAppId, mUserId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error getting print job:" + printJobId, re);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the print jobs for this application.
+ *
+ * @return The print job list.
+ *
+ * @see PrintJob
+ */
+ public List<PrintJob> getPrintJobs() {
+ try {
+ List<PrintJobInfo> printJobInfos = mService.getPrintJobs(mAppId, mUserId);
+ if (printJobInfos == null) {
+ return Collections.emptyList();
+ }
+ final int printJobCount = printJobInfos.size();
+ List<PrintJob> printJobs = new ArrayList<PrintJob>(printJobCount);
+ for (int i = 0; i < printJobCount; i++) {
+ printJobs.add(new PrintJob(printJobInfos.get(i), this));
+ }
+ return printJobs;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error getting print jobs!", re);
+ }
+ return Collections.emptyList();
+ }
+
+ ICancellationSignal cancelPrintJob(int printJobId) {
+ try {
+ mService.cancelPrintJob(printJobId, mAppId, mUserId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error cancleing a print job:" + printJobId, re);
+ }
+ return null;
+ }
+
+ /**
+ * Creates a print job for printing a file with default print attributes.
+ *
+ * @param printJobName A name for the new print job.
+ * @param pdfFile The PDF file to print.
+ * @param attributes The default print job attributes.
+ * @return The created print job.
+ *
+ * @see PrintJob
+ */
+ public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) {
+ PrintFileAdapter printable = new PrintFileAdapter(pdfFile);
+ return print(printJobName, printable, attributes);
+ }
+
+ /**
+ * Creates a print job for printing a {@link PrintAdapter} with default print
+ * attributes.
+ *
+ * @param printJobName A name for the new print job.
+ * @param printAdapter The printable adapter to print.
+ * @param attributes The default print job attributes.
+ * @return The created print job.
+ *
+ * @see PrintJob
+ */
+ public PrintJob print(String printJobName, PrintAdapter printAdapter,
+ PrintAttributes attributes) {
+ PrintAdapterDelegate delegate = new PrintAdapterDelegate(printAdapter,
+ mContext.getMainLooper());
+ try {
+ PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
+ attributes, mAppId, mUserId);
+ if (printJob != null) {
+ return new PrintJob(printJob, this);
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error creating a print job", re);
+ }
+ return null;
+ }
+
+ private static final class PrintClient extends IPrintClient.Stub {
+
+ private final WeakReference<PrintManager> mWeakPrintManager;
+
+ public PrintClient(PrintManager manager) {
+ mWeakPrintManager = new WeakReference<PrintManager>(manager);
+ }
+
+ @Override
+ public void startPrintJobConfigActivity(IntentSender intent) {
+ PrintManager manager = mWeakPrintManager.get();
+ if (manager != null) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = manager.mContext;
+ args.arg2 = intent;
+ manager.mHandler.obtainMessage(0, args).sendToTarget();
+ }
+ }
+ }
+
+ private static final class PrintAdapterDelegate extends IPrintAdapter.Stub {
+ private final Object mLock = new Object();
+
+ private PrintAdapter mPrintAdapter;
+
+ private Handler mHandler;
+
+ public PrintAdapterDelegate(PrintAdapter printAdapter, Looper looper) {
+ mPrintAdapter = printAdapter;
+ mHandler = new MyHandler(looper);
+ }
+
+ @Override
+ public void start() {
+ synchronized (mLock) {
+ if (isFinishedLocked()) {
+ return;
+ }
+ mHandler.obtainMessage(MyHandler.MESSAGE_START,
+ mPrintAdapter).sendToTarget();
+ }
+ }
+
+ @Override
+ public void printAttributesChanged(PrintAttributes attributes) {
+ synchronized (mLock) {
+ if (isFinishedLocked()) {
+ return;
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = mPrintAdapter;
+ args.arg2 = attributes;
+ mHandler.obtainMessage(MyHandler.MESSAGE_PRINT_ATTRIBUTES_CHANGED,
+ args).sendToTarget();
+ }
+ }
+
+ @Override
+ public void print(List<PageRange> pages, ParcelFileDescriptor fd,
+ IPrintResultCallback callback) {
+ synchronized (mLock) {
+ if (isFinishedLocked()) {
+ return;
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = mPrintAdapter;
+ args.arg2 = pages;
+ args.arg3 = fd.getFileDescriptor();
+ args.arg4 = callback;
+ mHandler.obtainMessage(MyHandler.MESSAGE_PRINT, args).sendToTarget();
+ }
+ }
+
+ @Override
+ public void finish() {
+ synchronized (mLock) {
+ if (isFinishedLocked()) {
+ return;
+ }
+ mHandler.obtainMessage(MyHandler.MESSAGE_FINIS,
+ mPrintAdapter).sendToTarget();
+ }
+ }
+
+ private boolean isFinishedLocked() {
+ return mPrintAdapter == null;
+ }
+
+ private void finishLocked() {
+ mPrintAdapter = null;
+ mHandler = null;
+ }
+
+ private final class MyHandler extends Handler {
+ public static final int MESSAGE_START = 1;
+ public static final int MESSAGE_PRINT_ATTRIBUTES_CHANGED = 2;
+ public static final int MESSAGE_PRINT = 3;
+ public static final int MESSAGE_FINIS = 4;
+
+ public MyHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_START: {
+ PrintAdapter adapter = (PrintAdapter) message.obj;
+ adapter.onStart();
+ } break;
+
+ case MESSAGE_PRINT_ATTRIBUTES_CHANGED: {
+ SomeArgs args = (SomeArgs) message.obj;
+ PrintAdapter adapter = (PrintAdapter) args.arg1;
+ PrintAttributes attributes = (PrintAttributes) args.arg2;
+ args.recycle();
+ adapter.onPrintAttributesChanged(attributes);
+ } break;
+
+ case MESSAGE_PRINT: {
+ SomeArgs args = (SomeArgs) message.obj;
+ PrintAdapter adapter = (PrintAdapter) args.arg1;
+ @SuppressWarnings("unchecked")
+ List<PageRange> pages = (List<PageRange>) args.arg2;
+ final FileDescriptor fd = (FileDescriptor) args.arg3;
+ IPrintResultCallback callback = (IPrintResultCallback) args.arg4;
+ args.recycle();
+ try {
+ ICancellationSignal remoteSignal = CancellationSignal.createTransport();
+ callback.onPrintStarted(adapter.getInfo(), remoteSignal);
+
+ CancellationSignal localSignal = CancellationSignal.fromTransport(
+ remoteSignal);
+ adapter.onPrint(pages, fd, localSignal,
+ new PrintResultCallbackWrapper(callback) {
+ @Override
+ public void onPrintFinished(List<PageRange> pages) {
+ IoUtils.closeQuietly(fd);
+ super.onPrintFinished(pages);
+ }
+
+ @Override
+ public void onPrintFailed(CharSequence error) {
+ IoUtils.closeQuietly(fd);
+ super.onPrintFailed(error);
+ }
+ });
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error printing", re);
+ IoUtils.closeQuietly(fd);
+ }
+ } break;
+
+ case MESSAGE_FINIS: {
+ PrintAdapter adapter = (PrintAdapter) message.obj;
+ adapter.onFinish();
+ synchronized (mLock) {
+ finishLocked();
+ }
+ } break;
+
+ default: {
+ throw new IllegalArgumentException("Unknown message: "
+ + message.what);
+ }
+ }
+ }
+ }
+ }
+
+ private static abstract class PrintResultCallbackWrapper extends PrintResultCallback {
+
+ private final IPrintResultCallback mWrappedCallback;
+
+ public PrintResultCallbackWrapper(IPrintResultCallback callback) {
+ mWrappedCallback = callback;
+ }
+
+ @Override
+ public void onPrintFinished(List<PageRange> pages) {
+ try {
+ mWrappedCallback.onPrintFinished(pages);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling onPrintFinished", re);
+ }
+ }
+
+ @Override
+ public void onPrintFailed(CharSequence error) {
+ try {
+ mWrappedCallback.onPrintFailed(error);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling onPrintFailed", re);
+ }
+ }
+ }
+}
diff --git a/core/java/android/print/PrinterId.aidl b/core/java/android/print/PrinterId.aidl
new file mode 100644
index 0000000..84f422f
--- /dev/null
+++ b/core/java/android/print/PrinterId.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+parcelable PrinterId;
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
new file mode 100644
index 0000000..b853eb0
--- /dev/null
+++ b/core/java/android/print/PrinterId.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the unique id of a printer.
+ */
+public final class PrinterId implements Parcelable {
+
+ private final ComponentName mServiceComponentName;
+
+ private final String mLocalId;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param serviceComponentName The managing print service.
+ * @param localId The unique id within the managing service.
+ *
+ * @hide
+ */
+ public PrinterId(ComponentName serviceComponentName, String localId) {
+ mServiceComponentName = serviceComponentName;
+ mLocalId = localId;
+ }
+
+ private PrinterId(Parcel parcel) {
+ mServiceComponentName = parcel.readParcelable(null);
+ mLocalId = parcel.readString();
+ }
+
+ /**
+ * The id of the print service this printer is managed by.
+ *
+ * @return The print service component name.
+ *
+ * @hide
+ */
+ public ComponentName getServiceComponentName() {
+ return mServiceComponentName;
+ }
+
+ /**
+ * Gets the local id of this printer in the context
+ * of the print service that manages it.
+ *
+ * @return The local id.
+ */
+ public String getLocalId() {
+ return mLocalId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mServiceComponentName, flags);
+ parcel.writeString(mLocalId);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null) {
+ return false;
+ }
+ if (getClass() != object.getClass()) {
+ return false;
+ }
+ PrinterId other = (PrinterId) object;
+ if (mServiceComponentName == null) {
+ if (other.mServiceComponentName != null) {
+ return false;
+ }
+ } else if (!mServiceComponentName.equals(other.mServiceComponentName)) {
+ return false;
+ }
+ if (mLocalId != other.mLocalId) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int hashCode = 1;
+ hashCode = prime * hashCode + ((mServiceComponentName != null)
+ ? mServiceComponentName.hashCode() : 1);
+ hashCode = prime * hashCode + mLocalId.hashCode();
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PrinterId{");
+ builder.append(mServiceComponentName.flattenToString());
+ builder.append(":");
+ builder.append(mLocalId);
+ builder.append('}');
+ return builder.toString();
+ }
+
+ /**
+ * @hide
+ */
+ public String flattenToString() {
+ return mServiceComponentName.flattenToString() + ":" + mLocalId;
+ }
+
+ /**
+ * @hide
+ */
+ public static PrinterId unflattenFromString(String string) {
+ String[] split = string.split(":");
+ if (split.length != 2) {
+ throw new IllegalArgumentException("Not well-formed printer id:" + string);
+ }
+ ComponentName component = ComponentName.unflattenFromString(split[0]);
+ String localId = String.valueOf(split[1]);
+ return new PrinterId(component, localId);
+ }
+
+ public static final Parcelable.Creator<PrinterId> CREATOR =
+ new Creator<PrinterId>() {
+ @Override
+ public PrinterId createFromParcel(Parcel parcel) {
+ return new PrinterId(parcel);
+ }
+
+ @Override
+ public PrinterId[] newArray(int size) {
+ return new PrinterId[size];
+ }
+ };
+}
diff --git a/core/java/android/print/PrinterInfo.aidl b/core/java/android/print/PrinterInfo.aidl
new file mode 100644
index 0000000..6ec5a58
--- /dev/null
+++ b/core/java/android/print/PrinterInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+parcelable PrinterInfo;
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
new file mode 100644
index 0000000..9283472
--- /dev/null
+++ b/core/java/android/print/PrinterInfo.java
@@ -0,0 +1,798 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintAttributes.Tray;
+import android.text.TextUtils;
+import android.util.SparseIntArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents the description of a printer. A description
+ * contains the printer id, human readable name, status, and available
+ * options for various printer capabilities, such as media size, etc.
+ */
+public final class PrinterInfo implements Parcelable {
+ /**
+ * Undefined default value.
+ *
+ * @hide
+ */
+ public static final int DEFAULT_UNDEFINED = -1;
+
+ private static final int MIN_COPIES = 1;
+
+ private static final int PROPERTY_MEDIA_SIZE = 0;
+ private static final int PROPERTY_RESOLUTION = 1;
+ private static final int PROPERTY_INPUT_TRAY = 2;
+ private static final int PROPERTY_OUTPUT_TRAY = 3;
+ private static final int PROPERTY_DUPLEX_MODE = 4;
+ private static final int PROPERTY_COLOR_MODE = 5;
+ private static final int PROPERTY_FITTING_MODE = 6;
+ private static final int PROPERTY_ORIENTATION = 7;
+
+ /** Printer status: the printer is ready to print. */
+ public static final int STATUS_READY = 1;
+
+ // TODO: Add printer status constants.
+
+ private PrinterId mId;
+ private CharSequence mLabel;
+ private int mStatus;
+
+ private Margins mMinMargins;
+ private final List<MediaSize> mMediaSizes = new ArrayList<MediaSize>(); // required
+ private final List<Resolution> mResolutions = new ArrayList<Resolution>(); // required
+ private List<Tray> mInputTrays;
+ private List<Tray> mOutputTrays;
+
+ private int mDuplexModes;
+ private int mColorModes;
+ private int mFittingModes;
+ private int mOrientations;
+
+ private final SparseIntArray mDefaults = new SparseIntArray();
+ private Margins mDefaultMargins;
+
+ private PrinterInfo() {
+ mDefaults.put(PROPERTY_MEDIA_SIZE, DEFAULT_UNDEFINED);
+ mDefaults.put(PROPERTY_RESOLUTION, DEFAULT_UNDEFINED);
+ mDefaults.put(PROPERTY_INPUT_TRAY, DEFAULT_UNDEFINED);
+ mDefaults.put(PROPERTY_OUTPUT_TRAY, DEFAULT_UNDEFINED);
+ mDefaults.put(PROPERTY_DUPLEX_MODE, DEFAULT_UNDEFINED);
+ mDefaults.put(PROPERTY_COLOR_MODE, DEFAULT_UNDEFINED);
+ mDefaults.put(PROPERTY_FITTING_MODE, DEFAULT_UNDEFINED);
+ mDefaults.put(PROPERTY_ORIENTATION, DEFAULT_UNDEFINED);
+ }
+
+ /**
+ * Get the globally unique printer id.
+ *
+ * @return The printer id.
+ */
+ public PrinterId getId() {
+ return mId;
+ }
+
+ /**
+ * Gets the human readable printer label.
+ *
+ * @return The human readable label.
+ */
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * Gets the status of the printer.
+ *
+ * @return The status.
+ */
+ public int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Gets the supported media sizes.
+ *
+ * @return The supported media sizes.
+ */
+ public List<MediaSize> getMediaSizes() {
+ return mMediaSizes;
+ }
+
+ /**
+ * Gets the supported resolutions.
+ *
+ * @return The supported resolutions.
+ */
+ public List<Resolution> getResolutions() {
+ return mResolutions;
+ }
+
+ /**
+ * Gets the minimal supported margins.
+ *
+ * @return The minimal margins.
+ */
+ public Margins getMinMargins() {
+ return mMinMargins;
+ }
+
+ /**
+ * Gets the available input trays.
+ *
+ * @return The input trays.
+ */
+ public List<Tray> getInputTrays() {
+ return mInputTrays;
+ }
+
+ /**
+ * Gets the available output trays.
+ *
+ * @return The output trays.
+ */
+ public List<Tray> getOutputTrays() {
+ return mOutputTrays;
+ }
+
+ /**
+ * Gets the supported duplex modes.
+ *
+ * @return The duplex modes.
+ *
+ * @see PrintAttributes#DUPLEX_MODE_NONE
+ * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
+ * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
+ */
+ public int getDuplexModes() {
+ return mDuplexModes;
+ }
+
+ /**
+ * Gets the supported color modes.
+ *
+ * @return The color modes.
+ *
+ * @see PrintAttributes#COLOR_MODE_COLOR
+ * @see PrintAttributes#COLOR_MODE_MONOCHROME
+ */
+ public int getColorModes() {
+ return mColorModes;
+ }
+
+ /**
+ * Gets the supported fitting modes.
+ *
+ * @return The fitting modes.
+ *
+ * @see PrintAttributes#FITTING_MODE_NONE
+ * @see PrintAttributes#FITTING_MODE_FIT_TO_PAGE
+ */
+ public int getFittingModes() {
+ return mFittingModes;
+ }
+
+ /**
+ * Gets the supported orientations.
+ *
+ * @return The orientations.
+ *
+ * @see PrintAttributes#ORIENTATION_PORTRAIT
+ * @see PrintAttributes#ORIENTATION_LANDSCAPE
+ */
+ public int getOrientations() {
+ return mOrientations;
+ }
+
+ /**
+ * Gets the default print attributes.
+ *
+ * @param outAttributes The attributes to populated.
+ */
+ public void getDefaults(PrintAttributes outAttributes) {
+ outAttributes.clear();
+
+ // TODO: Do we want a printer to specify default copies?
+ outAttributes.setCopies(MIN_COPIES);
+
+ outAttributes.setMargins(mDefaultMargins);
+
+ final int mediaSizeIndex = mDefaults.get(PROPERTY_MEDIA_SIZE);
+ if (mediaSizeIndex >= 0) {
+ outAttributes.setMediaSize(mMediaSizes.get(mediaSizeIndex));
+ }
+
+ final int resolutionIndex = mDefaults.get(PROPERTY_RESOLUTION);
+ if (resolutionIndex >= 0) {
+ outAttributes.setResolution(mResolutions.get(resolutionIndex));
+ }
+
+ final int inputTrayIndex = mDefaults.get(PROPERTY_INPUT_TRAY);
+ if (inputTrayIndex >= 0) {
+ outAttributes.setInputTray(mInputTrays.get(inputTrayIndex));
+ }
+
+ final int outputTrayIndex = mDefaults.get(PROPERTY_OUTPUT_TRAY);
+ if (outputTrayIndex >= 0) {
+ outAttributes.setOutputTray(mOutputTrays.get(outputTrayIndex));
+ }
+
+ final int duplexMode = mDefaults.get(PROPERTY_DUPLEX_MODE);
+ if (duplexMode > 0) {
+ outAttributes.setDuplexMode(duplexMode);
+ }
+
+ final int colorMode = mDefaults.get(PROPERTY_COLOR_MODE);
+ if (colorMode > 0) {
+ outAttributes.setColorMode(mColorModes & colorMode);
+ }
+
+ final int fittingMode = mDefaults.get(PROPERTY_FITTING_MODE);
+ if (fittingMode > 0) {
+ outAttributes.setFittingMode(fittingMode);
+ }
+
+ final int orientation = mDefaults.get(PROPERTY_ORIENTATION);
+ if (orientation > 0) {
+ outAttributes.setOrientation(orientation);
+ }
+ }
+
+ private PrinterInfo(Parcel parcel) {
+ mId = parcel.readParcelable(null);
+ mLabel = parcel.readCharSequence();
+ mStatus = parcel.readInt();
+
+ mMinMargins = readMargins(parcel);
+ readMediaSizes(parcel);
+ readResolutions(parcel);
+ mInputTrays = readInputTrays(parcel);
+ mOutputTrays = readOutputTrays(parcel);
+
+ mColorModes = parcel.readInt();
+ mDuplexModes = parcel.readInt();
+ mFittingModes = parcel.readInt();
+ mOrientations = parcel.readInt();
+
+ readDefaults(parcel);
+ mDefaultMargins = readMargins(parcel);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mId, flags);
+ parcel.writeCharSequence(mLabel);
+ parcel.writeInt(mStatus);
+
+ writeMargins(mMinMargins, parcel);
+ writeMediaSizes(parcel);
+ writeResolutions(parcel);
+ writeInputTrays(parcel);
+ writeOutputTrays(parcel);
+
+ parcel.writeInt(mColorModes);
+ parcel.writeInt(mDuplexModes);
+ parcel.writeInt(mFittingModes);
+ parcel.writeInt(mOrientations);
+
+ writeDefaults(parcel);
+ writeMargins(mDefaultMargins, parcel);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PrinterInfo{");
+ builder.append(mId).append(", \"");
+ builder.append(mLabel);
+ builder.append("\"}");
+ return builder.toString();
+ }
+
+ private void writeMediaSizes(Parcel parcel) {
+ if (mMediaSizes == null) {
+ parcel.writeInt(0);
+ return;
+ }
+ final int mediaSizeCount = mMediaSizes.size();
+ parcel.writeInt(mediaSizeCount);
+ for (int i = 0; i < mediaSizeCount; i++) {
+ mMediaSizes.get(i).writeToParcel(parcel);
+ }
+ }
+
+ private void readMediaSizes(Parcel parcel) {
+ final int mediaSizeCount = parcel.readInt();
+ for (int i = 0; i < mediaSizeCount; i++) {
+ mMediaSizes.add(MediaSize.createFromParcel(parcel));
+ }
+ }
+
+ private void writeResolutions(Parcel parcel) {
+ final int resolutionCount = mResolutions.size();
+ parcel.writeInt(resolutionCount);
+ for (int i = 0; i < resolutionCount; i++) {
+ mResolutions.get(i).writeToParcel(parcel);
+ }
+ }
+
+ private void readResolutions(Parcel parcel) {
+ final int resolutionCount = parcel.readInt();
+ for (int i = 0; i < resolutionCount; i++) {
+ mResolutions.add(Resolution.createFromParcel(parcel));
+ }
+ }
+
+ private void writeMargins(Margins margins, Parcel parcel) {
+ if (margins == null) {
+ parcel.writeInt(0);
+ } else {
+ parcel.writeInt(1);
+ margins.writeToParcel(parcel);
+ }
+ }
+
+ private Margins readMargins(Parcel parcel) {
+ return (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
+ }
+
+ private void writeInputTrays(Parcel parcel) {
+ if (mInputTrays == null) {
+ parcel.writeInt(0);
+ return;
+ }
+ final int inputTrayCount = mInputTrays.size();
+ parcel.writeInt(inputTrayCount);
+ for (int i = 0; i < inputTrayCount; i++) {
+ mInputTrays.get(i).writeToParcel(parcel);
+ }
+ }
+
+ private List<Tray> readInputTrays(Parcel parcel) {
+ final int inputTrayCount = parcel.readInt();
+ if (inputTrayCount <= 0) {
+ return null;
+ }
+ List<Tray> inputTrays = new ArrayList<Tray>(inputTrayCount);
+ for (int i = 0; i < inputTrayCount; i++) {
+ inputTrays.add(Tray.createFromParcel(parcel));
+ }
+ return inputTrays;
+ }
+
+ private void writeOutputTrays(Parcel parcel) {
+ if (mOutputTrays == null) {
+ parcel.writeInt(0);
+ return;
+ }
+ final int outputTrayCount = mOutputTrays.size();
+ parcel.writeInt(outputTrayCount);
+ for (int i = 0; i < outputTrayCount; i++) {
+ mOutputTrays.get(i).writeToParcel(parcel);
+ }
+ }
+
+ private List<Tray> readOutputTrays(Parcel parcel) {
+ final int outputTrayCount = parcel.readInt();
+ if (outputTrayCount <= 0) {
+ return null;
+ }
+ List<Tray> outputTrays = new ArrayList<Tray>(outputTrayCount);
+ for (int i = 0; i < outputTrayCount; i++) {
+ outputTrays.add(Tray.createFromParcel(parcel));
+ }
+ return outputTrays;
+ }
+
+ private void readDefaults(Parcel parcel) {
+ final int defaultCount = parcel.readInt();
+ for (int i = 0; i < defaultCount; i++) {
+ mDefaults.append(mDefaults.size(), parcel.readInt());
+ }
+ }
+
+ private void writeDefaults(Parcel parcel) {
+ final int defaultCount = mDefaults.size();
+ parcel.writeInt(defaultCount);
+ for (int i = 0; i < defaultCount; i++) {
+ parcel.writeInt(mDefaults.valueAt(i));
+ }
+ }
+
+ /**
+ * Builder for creating of a {@link PrinterInfo}. This class is responsible
+ * to enforce that all required attributes have at least one default value.
+ * In other words, this class creates only well-formed {@link PrinterInfo}s.
+ * <p>
+ * Look at the individual methods for a reference whether a property is
+ * required or if it is optional.
+ * </p>
+ */
+ public static final class Builder {
+ private final PrinterInfo mPrinterInfo;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param printerId The printer id.
+ * @param label The human readable printer label.
+ *
+ * @throws IllegalArgumentException IF the printer id is null.
+ * @throws IllegalArgumentException IF the label is empty.
+ */
+ public Builder(PrinterId printerId, CharSequence label) {
+ if (printerId == null) {
+ throw new IllegalArgumentException("printerId cannot be null.");
+ }
+ if (TextUtils.isEmpty(label)) {
+ throw new IllegalArgumentException("label cannot be empty.");
+ }
+ mPrinterInfo = new PrinterInfo();
+ mPrinterInfo.mLabel = label;
+ mPrinterInfo.mId = printerId;
+ }
+
+ /**
+ * Sets the printer status.
+ * <p>
+ * <strong>Required:</strong> Yes
+ * </p>
+ *
+ * @param status The status.
+ * @return This builder.
+ */
+ public Builder setStatus(int status) {
+ mPrinterInfo.mStatus = status;
+ return this;
+ }
+
+ /**
+ * Adds a supported media size.
+ * <p>
+ * <strong>Required:</strong> Yes
+ * </p>
+ *
+ * @param mediaSize A media size.
+ * @param isDefault Whether this is the default.
+ * @return This builder.
+ * @throws IllegalArgumentException If set as default and there
+ * is already a default.
+ *
+ * @see PrintAttributes.MediaSize
+ */
+ public Builder addMediaSize(MediaSize mediaSize, boolean isDefault) {
+ final int insertionIndex = mPrinterInfo.mMediaSizes.size();
+ mPrinterInfo.mMediaSizes.add(mediaSize);
+ if (isDefault) {
+ throwIfDefaultAlreadySpecified(PROPERTY_MEDIA_SIZE);
+ mPrinterInfo.mDefaults.put(PROPERTY_MEDIA_SIZE, insertionIndex);
+ }
+ return this;
+ }
+
+ /**
+ * Adds a supported resolution.
+ * <p>
+ * <strong>Required:</strong> Yes
+ * </p>
+ *
+ * @param resolution A resolution.
+ * @param isDefault Whether this is the default.
+ * @return This builder.
+ *
+ * @throws IllegalArgumentException If set as default and there
+ * is already a default.
+ *
+ * @see PrintAttributes.Resolution
+ */
+ public Builder addResolution(Resolution resolution, boolean isDefault) {
+ final int insertionIndex = mPrinterInfo.mResolutions.size();
+ mPrinterInfo.mResolutions.add(resolution);
+ if (isDefault) {
+ throwIfDefaultAlreadySpecified(PROPERTY_RESOLUTION);
+ mPrinterInfo.mDefaults.put(PROPERTY_RESOLUTION, insertionIndex);
+ }
+ return this;
+ }
+
+ /**
+ * Sets the minimal margins.
+ * <p>
+ * <strong>Required:</strong> No
+ * </p>
+ *
+ * @param margins The margins.
+ * @param defaultMargins The default margins.
+ * @return This builder.
+ *
+ * @see PrintAttributes.Margins
+ */
+ public Builder setMinMargins(Margins margins, Margins defaultMargins) {
+ if (margins.getLeftMils() > defaultMargins.getLeftMils()
+ || margins.getTopMils() > defaultMargins.getTopMils()
+ || margins.getRightMils() < defaultMargins.getRightMils()
+ || margins.getBottomMils() < defaultMargins.getBottomMils()) {
+ throw new IllegalArgumentException("Default margins"
+ + " cannot be outside of the min margins.");
+ }
+ mPrinterInfo.mMinMargins = margins;
+ mPrinterInfo.mDefaultMargins = defaultMargins;
+ return this;
+ }
+
+ /**
+ * Adds an input tray.
+ * <p>
+ * <strong>Required:</strong> No
+ * </p>
+ *
+ * @param inputTray A tray.
+ * @param isDefault Whether this is the default.
+ * @return This builder.
+ *
+ * @throws IllegalArgumentException If set as default and there
+ * is already a default.
+ *
+ * @see PrintAttributes.Tray
+ */
+ public Builder addInputTray(Tray inputTray, boolean isDefault) {
+ if (mPrinterInfo.mInputTrays == null) {
+ mPrinterInfo.mInputTrays = new ArrayList<Tray>();
+ }
+ final int insertionIndex = mPrinterInfo.mInputTrays.size();
+ mPrinterInfo.mInputTrays.add(inputTray);
+ if (isDefault) {
+ throwIfDefaultAlreadySpecified(PROPERTY_INPUT_TRAY);
+ mPrinterInfo.mDefaults.put(PROPERTY_INPUT_TRAY, insertionIndex);
+ }
+ return this;
+ }
+
+ /**
+ * Adds an output tray.
+ * <p>
+ * <strong>Required:</strong> No
+ * </p>
+ *
+ * @param outputTray A tray.
+ * @param isDefault Whether this is the default.
+ * @return This builder.
+ *
+ * @throws IllegalArgumentException If set as default and there
+ * is already a default.
+ *
+ * @see PrintAttributes.Tray
+ */
+ public Builder addOutputTray(Tray outputTray, boolean isDefault) {
+ if (mPrinterInfo.mOutputTrays == null) {
+ mPrinterInfo.mOutputTrays = new ArrayList<Tray>();
+ }
+ final int insertionIndex = mPrinterInfo.mOutputTrays.size();
+ mPrinterInfo.mOutputTrays.add(outputTray);
+ if (isDefault) {
+ throwIfDefaultAlreadySpecified(PROPERTY_OUTPUT_TRAY);
+ mPrinterInfo.mDefaults.put(PROPERTY_OUTPUT_TRAY, insertionIndex);
+ }
+ return this;
+ }
+
+ /**
+ * Sets the color modes.
+ * <p>
+ * <strong>Required:</strong> Yes
+ * </p>
+ *
+ * @param colorModes The color mode bit mask.
+ * @param defaultColorMode The default color mode.
+ * @return This builder.
+ *
+ * @throws IllegalArgumentException If color modes contains an invalid
+ * mode bit or if the default color mode is invalid.
+ *
+ * @see PrintAttributes#COLOR_MODE_COLOR
+ * @see PrintAttributes#COLOR_MODE_MONOCHROME
+ */
+ public Builder setColorModes(int colorModes, int defaultColorMode) {
+ int currentModes = colorModes;
+ while (currentModes > 0) {
+ final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
+ currentModes &= ~currentMode;
+ PrintAttributes.enforceValidColorMode(currentMode);
+ }
+ if ((colorModes & defaultColorMode) == 0) {
+ throw new IllegalArgumentException("Default color mode not in color modes.");
+ }
+ PrintAttributes.enforceValidColorMode(colorModes);
+ mPrinterInfo.mColorModes = colorModes;
+ mPrinterInfo.mDefaults.put(PROPERTY_COLOR_MODE, defaultColorMode);
+ return this;
+ }
+
+ /**
+ * Set the duplex modes.
+ * <p>
+ * <strong>Required:</strong> No
+ * </p>
+ *
+ * @param duplexModes The duplex mode bit mask.
+ * @param defaultDuplexMode The default duplex mode.
+ * @return This builder.
+ *
+ * @throws IllegalArgumentException If duplex modes contains an invalid
+ * mode bit or if the default duplex mode is invalid.
+ *
+ * @see PrintAttributes#DUPLEX_MODE_NONE
+ * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
+ * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
+ */
+ public Builder setDuplexModes(int duplexModes, int defaultDuplexMode) {
+ int currentModes = duplexModes;
+ while (currentModes > 0) {
+ final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
+ currentModes &= ~currentMode;
+ PrintAttributes.enforceValidDuplexMode(currentMode);
+ }
+ if ((duplexModes & defaultDuplexMode) == 0) {
+ throw new IllegalArgumentException("Default duplex mode not in duplex modes.");
+ }
+ PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
+ mPrinterInfo.mDuplexModes = duplexModes;
+ mPrinterInfo.mDefaults.put(PROPERTY_DUPLEX_MODE, defaultDuplexMode);
+ return this;
+ }
+
+ /**
+ * Sets the fitting modes.
+ * <p>
+ * <strong>Required:</strong> No
+ * </p>
+ *
+ * @param fittingModes The fitting mode bit mask.
+ * @param defaultFittingMode The default fitting mode.
+ * @return This builder.
+ *
+ * @throws IllegalArgumentException If fitting modes contains an invalid
+ * mode bit or if the default fitting mode is invalid.
+ *
+ * @see PrintAttributes#FITTING_MODE_NONE
+ * @see PrintAttributes#FITTING_MODE_FIT_TO_PAGE
+ */
+ public Builder setFittingModes(int fittingModes, int defaultFittingMode) {
+ int currentModes = fittingModes;
+ while (currentModes > 0) {
+ final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
+ currentModes &= ~currentMode;
+ PrintAttributes.enfoceValidFittingMode(currentMode);
+ }
+ if ((fittingModes & defaultFittingMode) == 0) {
+ throw new IllegalArgumentException("Default fitting mode not in fiting modes.");
+ }
+ PrintAttributes.enfoceValidFittingMode(defaultFittingMode);
+ mPrinterInfo.mFittingModes = fittingModes;
+ mPrinterInfo.mDefaults.put(PROPERTY_FITTING_MODE, defaultFittingMode);
+ return this;
+ }
+
+ /**
+ * Sets the orientations.
+ * <p>
+ * <strong>Required:</strong> Yes
+ * </p>
+ *
+ * @param orientations The orientation bit mask.
+ * @param defaultOrientation The default orientation.
+ * @return This builder.
+ *
+ * @throws IllegalArgumentException If orientations contains an invalid
+ * mode bit or if the default orientation is invalid.
+ *
+ * @see PrintAttributes#ORIENTATION_PORTRAIT
+ * @see PrintAttributes#ORIENTATION_LANDSCAPE
+ */
+ public Builder setOrientations(int orientations, int defaultOrientation) {
+ int currentOrientaions = orientations;
+ while (currentOrientaions > 0) {
+ final int currentOrnt = (1 << Integer.numberOfTrailingZeros(currentOrientaions));
+ currentOrientaions &= ~currentOrnt;
+ PrintAttributes.enforceValidOrientation(currentOrnt);
+ }
+ if ((orientations & defaultOrientation) == 0) {
+ throw new IllegalArgumentException("Default orientation not in orientations.");
+ }
+ PrintAttributes.enforceValidOrientation(defaultOrientation);
+ mPrinterInfo.mOrientations = orientations;
+ mPrinterInfo.mDefaults.put(PROPERTY_ORIENTATION, defaultOrientation);
+ return this;
+ }
+
+ /**
+ * Crates a new {@link PrinterInfo} enforcing that all required properties
+ * have need specified. See individual methods in this class for reference
+ * about required attributes.
+ *
+ * @return A new {@link PrinterInfo}.
+ *
+ * @throws IllegalStateException If a required attribute was not specified.
+ */
+ public PrinterInfo create() {
+ if (mPrinterInfo.mMediaSizes == null || mPrinterInfo.mMediaSizes.isEmpty()) {
+ throw new IllegalStateException("No media size specified.");
+ }
+ if (mPrinterInfo.mDefaults.valueAt(PROPERTY_MEDIA_SIZE) == DEFAULT_UNDEFINED) {
+ throw new IllegalStateException("No default media size specified.");
+ }
+ if (mPrinterInfo.mResolutions == null || mPrinterInfo.mResolutions.isEmpty()) {
+ throw new IllegalStateException("No resolution specified.");
+ }
+ if (mPrinterInfo.mDefaults.valueAt(PROPERTY_RESOLUTION) == DEFAULT_UNDEFINED) {
+ throw new IllegalStateException("No default resolution specified.");
+ }
+ if (mPrinterInfo.mColorModes == 0) {
+ throw new IllegalStateException("No color mode specified.");
+ }
+ if (mPrinterInfo.mDefaults.valueAt(PROPERTY_COLOR_MODE) == DEFAULT_UNDEFINED) {
+ throw new IllegalStateException("No default color mode specified.");
+ }
+ if (mPrinterInfo.mOrientations == 0) {
+ throw new IllegalStateException("No oprientation specified.");
+ }
+ if (mPrinterInfo.mDefaults.valueAt(PROPERTY_ORIENTATION) == DEFAULT_UNDEFINED) {
+ throw new IllegalStateException("No default orientation specified.");
+ }
+ if (mPrinterInfo.mMinMargins == null) {
+ mPrinterInfo.mMinMargins = new Margins(0, 0, 0, 0);
+ }
+ if (mPrinterInfo.mDefaultMargins == null) {
+ mPrinterInfo.mDefaultMargins = mPrinterInfo.mMinMargins;
+ }
+ return mPrinterInfo;
+ }
+
+ private void throwIfDefaultAlreadySpecified(int propertyIndex) {
+ if (mPrinterInfo.mDefaults.get(propertyIndex) != DEFAULT_UNDEFINED) {
+ throw new IllegalArgumentException("Default already specified.");
+ }
+ }
+ }
+
+ public static final Parcelable.Creator<PrinterInfo> CREATOR =
+ new Parcelable.Creator<PrinterInfo>() {
+ @Override
+ public PrinterInfo createFromParcel(Parcel parcel) {
+ return new PrinterInfo(parcel);
+ }
+
+ @Override
+ public PrinterInfo[] newArray(int size) {
+ return new PrinterInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/print/pdf/PdfDocument.java b/core/java/android/print/pdf/PdfDocument.java
new file mode 100644
index 0000000..7ce036d
--- /dev/null
+++ b/core/java/android/print/pdf/PdfDocument.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.pdf;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+
+import dalvik.system.CloseGuard;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>
+ * This class enables generating a PDF document from native Android content. You
+ * open a new document and then for every page you want to add you start a page,
+ * write content to the page, and finish the page. After you are done with all
+ * pages, you write the document to an output stream and close the document.
+ * After a document is closed you should not use it anymore.
+ * </p>
+ * <p>
+ * A typical use of the APIs looks like this:
+ * </p>
+ * <pre>
+ * // open a new document
+ * PdfDocument document = PdfDocument.open();
+ *
+ * // crate a page description
+ * PageInfo pageInfo = new PageInfo.Builder(new Rect(0, 0, 100, 100), 1, 300).create();
+ *
+ * // start a page
+ * Page page = document.startPage(pageInfo);
+ *
+ * // draw something on the page
+ * View content = getContentView();
+ * content.draw(page.getCanvas());
+ *
+ * // finish the page
+ * document.finishPage(page);
+ * . . .
+ * add more pages
+ * . . .
+ * // write the document content
+ * document.writeTo(getOutputStream());
+ *
+ * //close the document
+ * document.close();
+ * </pre>
+ */
+public final class PdfDocument {
+
+ private final byte[] mChunk = new byte[4096];
+
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ private final List<PageInfo> mPages = new ArrayList<PageInfo>();
+
+ private int mNativeDocument;
+
+ private Page mCurrentPage;
+
+ /**
+ * Opens a new document.
+ * <p>
+ * <strong>Note:</strong> You must close the document after you are
+ * done by calling {@link #close()}
+ * </p>
+ *
+ * @return The document.
+ *
+ * @see #close()
+ */
+ public static PdfDocument open() {
+ return new PdfDocument();
+ }
+
+ /**
+ * Creates a new instance.
+ */
+ private PdfDocument() {
+ mNativeDocument = nativeCreateDocument();
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * Starts a page using the provided {@link PageInfo}. After the page
+ * is created you can draw arbitrary content on the page's canvas which
+ * you can get by calling {@link Page#getCanvas()}. After you are done
+ * drawing the content you should finish the page by calling
+ * {@link #finishPage(Page)}. After the page is finished you should
+ * no longer access the page or its canvas.
+ * <p>
+ * <strong>Note:</strong> Do not call this method after {@link #close()}.
+ * </p>
+ *
+ * @param pageInfo The page info.
+ * @return A blank page.
+ *
+ * @see #finishPage(Page)
+ */
+ public Page startPage(PageInfo pageInfo) {
+ throwIfClosed();
+ if (pageInfo == null) {
+ throw new IllegalArgumentException("page cannot be null!");
+ }
+ if (mCurrentPage != null) {
+ throw new IllegalStateException("Previous page not finished!");
+ }
+ Canvas canvas = new PdfCanvas(nativeCreatePage(pageInfo.mPageSize,
+ pageInfo.mContentSize, pageInfo.mInitialTransform.native_instance),
+ pageInfo.mDensity);
+ mCurrentPage = new Page(canvas, pageInfo);
+ return mCurrentPage;
+ }
+
+ /**
+ * Finishes a started page. You should always finish the last started page.
+ * <p>
+ * <strong>Note:</strong> Do not call this method after {@link #close()}.
+ * </p>
+ *
+ * @param page The page.
+ *
+ * @see #startPage(PageInfo)
+ */
+ public void finishPage(Page page) {
+ throwIfClosed();
+ if (page == null) {
+ throw new IllegalArgumentException("page cannot be null");
+ }
+ if (page != mCurrentPage) {
+ throw new IllegalStateException("invalid page");
+ }
+ mPages.add(page.getInfo());
+ mCurrentPage = null;
+ nativeAppendPage(mNativeDocument, page.mCanvas.mNativeCanvas);
+ page.finish();
+ }
+
+ /**
+ * Writes the document to an output stream.
+ * <p>
+ * <strong>Note:</strong> Do not call this method after {@link #close()}.
+ * </p>
+ *
+ * @param out The output stream.
+ */
+ public void writeTo(OutputStream out) {
+ throwIfClosed();
+ if (out == null) {
+ throw new IllegalArgumentException("out cannot be null!");
+ }
+ nativeWriteTo(mNativeDocument, out, mChunk);
+ }
+
+ /**
+ * Gets the pages of the document.
+ *
+ * @return The pages.
+ */
+ public List<PageInfo> getPages() {
+ return Collections.unmodifiableList(mPages);
+ }
+
+ /**
+ * Closes this document. This method should be called after you
+ * are done working with the document. After this call the document
+ * is considered closed and none of its methods should be called.
+ */
+ public void close() {
+ dispose();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ dispose();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void dispose() {
+ if (mNativeDocument != 0) {
+ nativeFinalize(mNativeDocument);
+ mCloseGuard.close();
+ mNativeDocument = 0;
+ }
+ }
+
+ /**
+ * Throws an exception if the document is already closed.
+ */
+ private void throwIfClosed() {
+ if (mNativeDocument == 0) {
+ throw new IllegalStateException("document is closed!");
+ }
+ }
+
+ private native int nativeCreateDocument();
+
+ private native void nativeFinalize(int document);
+
+ private native void nativeAppendPage(int document, int page);
+
+ private native void nativeWriteTo(int document, OutputStream out, byte[] chunk);
+
+ private static native int nativeCreatePage(Rect pageSize,
+ Rect contentSize, int nativeMatrix);
+
+
+ private final class PdfCanvas extends Canvas {
+
+ public PdfCanvas(int nativeCanvas, int density) {
+ super(nativeCanvas);
+ super.setDensity(density);
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setDensity(int density) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setScreenDensity(int density) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * This class represents meta-data that describes a PDF {@link Page}.
+ */
+ public static final class PageInfo {
+ private Rect mPageSize;
+ private Rect mContentSize;
+ private Matrix mInitialTransform;
+ private int mPageNumber;
+ private int mDensity;
+
+ /**
+ * Creates a new instance.
+ */
+ private PageInfo() {
+ /* do nothing */
+ }
+
+ /**
+ * Gets the page size in pixels.
+ *
+ * @return The page size.
+ */
+ public Rect getPageSize() {
+ return mPageSize;
+ }
+
+ /**
+ * Get the content size in pixels.
+ *
+ * @return The content size.
+ */
+ public Rect getContentSize() {
+ return mContentSize;
+ }
+
+ /**
+ * Gets the initial transform which is applied to the page. This may be
+ * useful to move the origin to account for a margin, apply scale, or
+ * apply a rotation.
+ *
+ * @return The initial transform.
+ */
+ public Matrix getInitialTransform() {
+ return mInitialTransform;
+ }
+
+ /**
+ * Gets the page number.
+ *
+ * @return The page number.
+ */
+ public int getPageNumber() {
+ return mPageNumber;
+ }
+
+ /**
+ * Gets the density of the page in DPI.
+ *
+ * @return The density.
+ */
+ public int getDesity() {
+ return mDensity;
+ }
+
+ /**
+ * Builder for creating a {@link PageInfo}.
+ */
+ public static final class Builder {
+ private final PageInfo mPageInfo = new PageInfo();
+
+ /**
+ * Creates a new builder with the mandatory page info attributes.
+ *
+ * @param pageSize The page size in pixels.
+ * @param pageNumber The page number.
+ * @param density The page density in DPI.
+ */
+ public Builder(Rect pageSize, int pageNumber, int density) {
+ if (pageSize.width() == 0 || pageSize.height() == 0) {
+ throw new IllegalArgumentException("page width and height" +
+ " must be greater than zero!");
+ }
+ if (pageNumber < 0) {
+ throw new IllegalArgumentException("pageNumber cannot be less than zero!");
+ }
+ if (density <= 0) {
+ throw new IllegalArgumentException("density must be greater than zero!");
+ }
+ mPageInfo.mPageSize = pageSize;
+ mPageInfo.mPageNumber = pageNumber;
+ mPageInfo.mDensity = density;
+ }
+
+ /**
+ * Sets the content size in pixels.
+ *
+ * @param contentSize The content size.
+ */
+ public Builder setContentSize(Rect contentSize) {
+ Rect pageSize = mPageInfo.mPageSize;
+ if (contentSize != null && (pageSize.left > contentSize.left
+ || pageSize.top > contentSize.top
+ || pageSize.right < contentSize.right
+ || pageSize.bottom < contentSize.bottom)) {
+ throw new IllegalArgumentException("contentSize does not fit the pageSize!");
+ }
+ mPageInfo.mContentSize = contentSize;
+ return this;
+ }
+
+ /**
+ * Sets the initial transform which is applied to the page. This may be
+ * useful to move the origin to account for a margin, apply scale, or
+ * apply a rotation.
+ *
+ * @param transform The initial transform.
+ */
+ public Builder setInitialTransform(Matrix transform) {
+ mPageInfo.mInitialTransform = transform;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link PageInfo}.
+ *
+ * @return The new instance.
+ */
+ public PageInfo create() {
+ if (mPageInfo.mContentSize == null) {
+ mPageInfo.mContentSize = mPageInfo.mPageSize;
+ }
+ if (mPageInfo.mInitialTransform == null) {
+ mPageInfo.mInitialTransform = new Matrix();
+ }
+ return mPageInfo;
+ }
+ }
+ }
+
+ /**
+ * This class represents a PDF document page. It has associated
+ * a canvas on which you can draw content and is acquired by a
+ * call to {@link #getCanvas()}. It also has associated a
+ * {@link PageInfo} instance that describes its attributes.
+ */
+ public static final class Page {
+ private final PageInfo mPageInfo;
+ private Canvas mCanvas;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param canvas The canvas of the page.
+ * @param pageInfo The info with meta-data.
+ */
+ private Page(Canvas canvas, PageInfo pageInfo) {
+ mCanvas = canvas;
+ mPageInfo = pageInfo;
+ }
+
+ /**
+ * Gets the {@link Canvas} of the page.
+ *
+ * @return The canvas if the page is not finished, null otherwise.
+ *
+ * @see PdfDocument#finishPage(Page)
+ */
+ public Canvas getCanvas() {
+ return mCanvas;
+ }
+
+ /**
+ * Gets the {@link PageInfo} with meta-data for the page.
+ *
+ * @return The page info.
+ *
+ * @see PdfDocument#finishPage(Page)
+ */
+ public PageInfo getInfo() {
+ return mPageInfo;
+ }
+
+ private void finish() {
+ if (mCanvas != null) {
+ mCanvas.release();
+ mCanvas = null;
+ }
+ }
+ }
+}
diff --git a/core/java/android/printservice/IPrintService.aidl b/core/java/android/printservice/IPrintService.aidl
new file mode 100644
index 0000000..eabd96d
--- /dev/null
+++ b/core/java/android/printservice/IPrintService.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.printservice;
+
+import android.os.ICancellationSignal;
+import android.print.PrintJobInfo;
+import android.print.PrinterId;
+import android.printservice.IPrintServiceClient;
+
+/**
+ * Top-level interface to a print service component.
+ *
+ * @hide
+ */
+oneway interface IPrintService {
+ void setClient(IPrintServiceClient client);
+ void requestCancelPrintJob(in PrintJobInfo printJob);
+ void onPrintJobQueued(in PrintJobInfo printJob);
+ void startPrinterDiscovery();
+ void stopPrinterDiscovery();
+}
diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl
new file mode 100644
index 0000000..cff8c02
--- /dev/null
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.printservice;
+
+import android.os.ParcelFileDescriptor;
+import android.print.PrintJobInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+
+/**
+ * The top-level interface from a print service to the system.
+ *
+ * @hide
+ */
+interface IPrintServiceClient {
+ List<PrintJobInfo> getPrintJobs();
+ PrintJobInfo getPrintJob(int printJobId);
+ boolean setPrintJobState(int printJobId, int status);
+ boolean setPrintJobTag(int printJobId, String tag);
+ oneway void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
+ oneway void addDiscoveredPrinters(in List<PrinterInfo> printers);
+ oneway void removeDiscoveredPrinters(in List<PrinterId> printers);
+}
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
new file mode 100644
index 0000000..f490f91
--- /dev/null
+++ b/core/java/android/printservice/PrintJob.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.printservice;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.PrintJobInfo;
+import android.util.Log;
+
+/**
+ * This class represents a print job from the perspective of a
+ * print service. It provides APIs for observing the print job
+ * state and performing operations on the print job.
+ */
+public final class PrintJob {
+
+ private static final String LOG_TAG = "PrintJob";
+
+ private final int mId;
+
+ private final IPrintServiceClient mPrintServiceClient;
+
+ private PrintJobInfo mCachedInfo;
+
+ PrintJob(PrintJobInfo info, IPrintServiceClient client) {
+ if (client == null) {
+ throw new IllegalStateException("Print serivice not connected!");
+ }
+ mCachedInfo = info;
+ mId = info.getId();
+ mPrintServiceClient = client;
+ }
+
+ /**
+ * Gets the unique print job id.
+ *
+ * @return The id.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Gets the {@link PrintJobInfo} that describes this job.
+ * <p>
+ * <strong>Node:</strong>The returned info object is a snapshot of the
+ * current print job state. Every call to this method returns a fresh
+ * info object that reflects the current print job state.
+ * </p>
+ *
+ * @return The print job info.
+ */
+ public PrintJobInfo getInfo() {
+ PrintJobInfo info = null;
+ try {
+ info = mPrintServiceClient.getPrintJob(mId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Couldn't get info for job: " + mId, re);
+ }
+ if (info != null) {
+ mCachedInfo = info;
+ }
+ return mCachedInfo;
+ }
+
+ /**
+ * Gets whether this print job is queued. Such a print job is
+ * ready to be printed and can be started.
+ *
+ * @return Whether the print job is queued.
+ *
+ * @see #start()
+ */
+ public boolean isQueued() {
+ return getInfo().getState() == PrintJobInfo.STATE_QUEUED;
+ }
+
+ /**
+ * Gets whether this print job is started. Such a print job is
+ * being printed and can be completed or canceled or failed.
+ *
+ * @return Whether the print job is started.
+ *
+ * @see #complete()
+ * @see #cancel()
+ * @see #fail(CharSequence)
+ */
+ public boolean isStarted() {
+ return getInfo().getState() == PrintJobInfo.STATE_STARTED;
+ }
+
+ /**
+ * Starts the print job. You should call this method if {@link
+ * #isQueued()} returns true and you started printing.
+ *
+ * @return Whether the job as started.
+ *
+ * @see #isQueued()
+ */
+ public boolean start() {
+ if (isQueued()) {
+ return setState(PrintJobInfo.STATE_STARTED);
+ }
+ return false;
+ }
+
+ /**
+ * Completes the print job. You should call this method if {@link
+ * #isStarted()} returns true and you are done printing.
+ *
+ * @return Whether the job as completed.
+ *
+ * @see #isStarted()
+ */
+ public boolean complete() {
+ if (isStarted()) {
+ return setState(PrintJobInfo.STATE_COMPLETED);
+ }
+ return false;
+ }
+
+ /**
+ * Fails the print job. You should call this method if {@link
+ * #isStarted()} returns true you filed while printing.
+ *
+ * @param error The reason for the failure.
+ * @return Whether the job was failed.
+ *
+ * @see #isStarted()
+ */
+ public boolean fail(CharSequence error) {
+ // TODO: Propagate the error message to the UI.
+ if (isStarted()) {
+ return setState(PrintJobInfo.STATE_FAILED);
+ }
+ return false;
+ }
+
+ /**
+ * Cancels the print job. You should call this method if {@link
+ * #isStarted()} returns true and you canceled the print job as a
+ * response to a call to {@link PrintService#onRequestCancelPrintJob(
+ * PrintJob)}.
+ *
+ * @return Whether the job as canceled.
+ *
+ * @see #isStarted()
+ */
+ public boolean cancel() {
+ if (isStarted()) {
+ return setState(PrintJobInfo.STATE_CANCELED);
+ }
+ return false;
+ }
+
+ /**
+ * Sets a tag that is valid in the context of a {@link PrintService}
+ * and is not interpreted by the system. For example, a print service
+ * may set as a tag the key of the print job returned by a remote
+ * print server, if the printing is off handed to a cloud based service.
+ *
+ * @param tag The tag.
+ * @return True if the tag was set, false otherwise.
+ */
+ public boolean setTag(String tag) {
+ try {
+ return mPrintServiceClient.setPrintJobTag(mId, tag);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting tag for job:" + mId, re);
+ }
+ return false;
+ }
+
+ /**
+ * Gets the data associated with this print job. It is a responsibility of
+ * the print service to open a stream to the returned file descriptor
+ * and fully read the content.
+ * <p>
+ * <strong>Note:</strong> It is your responsibility to close the file descriptor.
+ * </p>
+ *
+ * @return A file descriptor for reading the data or <code>null</code>.
+ */
+ public final FileDescriptor getData() {
+ ParcelFileDescriptor source = null;
+ ParcelFileDescriptor sink = null;
+ try {
+ ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+ source = fds[0];
+ sink = fds[1];
+ mPrintServiceClient.writePrintJobData(sink, mId);
+ return source.getFileDescriptor();
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error calling getting print job data!", ioe);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling getting print job data!", re);
+ } finally {
+ if (sink != null) {
+ try {
+ sink.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ PrintJob other = (PrintJob) obj;
+ return (mId == other.mId);
+ }
+
+ @Override
+ public int hashCode() {
+ return mId;
+ }
+
+ private boolean setState(int state) {
+ // Best effort - update the state of the cached info since
+ // we may not be able to re-fetch it later if the job gets
+ // removed from the spooler.
+ mCachedInfo.setState(state);
+ try {
+ return mPrintServiceClient.setPrintJobState(mId, state);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting the state of job:" + mId, re);
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
new file mode 100644
index 0000000..9256966
--- /dev/null
+++ b/core/java/android/printservice/PrintService.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.printservice;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.print.PrintJobInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>
+ * This is the base class for implementing print services. A print service
+ * knows how to discover and interact one or more printers via one or more
+ * protocols.
+ * </p>
+ * <h3>Printer discovery</h3>
+ * <p>
+ * A print service is responsible for discovering and reporting printers.
+ * A printer discovery period starts with a call to
+ * {@link #onStartPrinterDiscovery()} and ends with a call to
+ * {@link #onStopPrinterDiscovery()}. During a printer discovery
+ * period the print service reports newly discovered printers by
+ * calling {@link #addDiscoveredPrinters(List)} and added printers
+ * that disappeared by calling {@link #removeDiscoveredPrinters(List)}.
+ * Calls to {@link #addDiscoveredPrinters(List)} and
+ * {@link #removeDiscoveredPrinters(List)} before a call to
+ * {@link #onStartPrinterDiscovery()} and after a call to
+ * {@link #onStopPrinterDiscovery()} are a no-op.
+ * </p>
+ * <p>
+ * For every printer discovery period all printers have to be added. Each
+ * printer known to this print service should be added only once during a
+ * discovery period, unless it was added and then removed before that.
+ * Only an already added printer can be removed.
+ * </p>
+ * <h3>Print jobs</h3>
+ * <p>
+ * When a new print job targeted to the printers managed by this print
+ * service is queued, i.e. ready for processing by the print service,
+ * a call to {@link #onPrintJobQueued(PrintJob)} is made and the print
+ * service may handle it immediately or schedule that for an appropriate
+ * time in the future. The list of all print jobs for this service
+ * are be available by calling {@link #getPrintJobs()}. A queued print
+ * job is one whose {@link PrintJob#isQueued()} return true.
+ * </p>
+ * <p>
+ * A print service is responsible for setting the print job state as
+ * appropriate while processing it. Initially, a print job is in a
+ * {@link PrintJobInfo#STATE_QUEUED} state which means that the data to
+ * be printed is spooled by the system and the print service can obtain
+ * that data by calling {@link PrintJob#getData()}. After the print
+ * service starts printing the data it should set the print job state
+ * to {@link PrintJobInfo#STATE_STARTED}. Upon successful completion, the
+ * print job state has to be set to {@link PrintJobInfo#STATE_COMPLETED}.
+ * In a case of a failure, the print job state should be set to
+ * {@link PrintJobInfo#STATE_FAILED}. If a print job is in a
+ * {@link PrintJobInfo#STATE_STARTED} state and the user requests to
+ * cancel it, the print service will receive a call to
+ * {@link #onRequestCancelPrintJob(PrintJob)} which requests from the
+ * service to do a best effort in canceling the job. In case the job
+ * is successfully canceled, its state has to be set to
+ * {@link PrintJobInfo#STATE_CANCELED}.
+ * </p>
+ * <h3>Lifecycle</h3>
+ * <p>
+ * The lifecycle of a print service is managed exclusively by the system
+ * and follows the established service lifecycle. Additionally, starting
+ * or stopping a print service is triggered exclusively by an explicit
+ * user action through enabling or disabling it in the device settings.
+ * After the system binds to a print service, it calls {@link #onConnected()}.
+ * This method can be overriden by clients to perform post binding setup.
+ * Also after the system unbinds from a print service, it calls
+ * {@link #onDisconnected()}. This method can be overriden by clients to
+ * perform post unbinding cleanup.
+ * </p>
+ * <h3>Declaration</h3>
+ * <p>
+ * A print service is declared as any other service in an AndroidManifest.xml
+ * but it must also specify that it handles the {@link android.content.Intent}
+ * with action {@link #SERVICE_INTERFACE}. Failure to declare this intent
+ * will cause the system to ignore the print service. Additionally, a print
+ * service must request the {@link android.Manifest.permission#BIND_PRINT_SERVICE}
+ * permission to ensure that only the system can bind to it. Failure to
+ * declare this intent will cause the system to ignore the print service.
+ * Following is an example declaration:
+ * </p>
+ * <pre>
+ * <service android:name=".MyPrintService"
+ * android:permission="android.permission.BIND_PRINT_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.printservice.PrintService" />
+ * </intent-filter>
+ * . . .
+ * </service>
+ * </pre>
+ * <h3>Configuration</h3>
+ * <p>
+ * A print service can be configured by specifying an optional settings
+ * activity which exposes service specific options, an optional add
+ * prints activity which is used for manual addition of printers, etc.
+ * It is a responsibility of the system to launch the settings and add
+ * printers activities when appropriate.
+ * </p>
+ * <p>
+ * A print service is configured by providing a
+ * {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
+ * the service. A service declaration with a meta-data tag is presented
+ * below:
+ * <pre> <service android:name=".MyPrintService"
+ * android:permission="android.permission.BIND_PRINT_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.printservice.PrintService" />
+ * </intent-filter>
+ * <meta-data android:name="android.printservice" android:resource="@xml/printservice" />
+ * </service></pre>
+ * </p>
+ * <p>
+ * For more details refer to {@link #SERVICE_META_DATA} and
+ * <code><{@link android.R.styleable#PrintService print-service}></code>.
+ * </p>
+ */
+public abstract class PrintService extends Service {
+
+ private static final String LOG_TAG = PrintService.class.getSimpleName();
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service
+ * in its manifest to allow the system to recognize it as a print service.
+ */
+ public static final String SERVICE_INTERFACE = "android.printservice.PrintService";
+
+ /**
+ * Name under which a PrintService component publishes additional information
+ * about itself. This meta-data must reference an XML resource containing a
+ * <code><{@link android.R.styleable#PrintService print-service}></code>
+ * tag. This is a a sample XML file configuring a print service:
+ * <pre> <print-service
+ * android:settingsActivity="foo.bar.MySettingsActivity"
+ * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity."
+ * . . .
+ * /></pre>
+ */
+ public static final String SERVICE_META_DATA = "android.printservice";
+
+ private final Object mLock = new Object();
+
+ private Handler mHandler;
+
+ private IPrintServiceClient mClient;
+
+ private boolean mDiscoveringPrinters;
+
+ @Override
+ protected void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ mHandler = new MyHandler(base.getMainLooper());
+ }
+
+ /**
+ * The system has connected to this service.
+ */
+ protected void onConnected() {
+ /* do nothing */
+ }
+
+ /**
+ * The system has disconnected from this service.
+ */
+ protected void onDisconnected() {
+ /* do nothing */
+ }
+
+ /**
+ * Callback requesting from this service to start printer discovery.
+ * At the end of the printer discovery period the system will call
+ * {@link #onStopPrinterDiscovery()}. Discovered printers should be
+ * reported by calling #addDiscoveredPrinters(List) and reported ones
+ * that disappear should be reported by calling
+ * {@link #removeDiscoveredPrinters(List)}.
+ *
+ * @see #onStopPrinterDiscovery()
+ * @see #addDiscoveredPrinters(List)
+ * @see #removeDiscoveredPrinters(List)
+ */
+ protected abstract void onStartPrinterDiscovery();
+
+ /**
+ * Callback requesting from this service to stop printer discovery.
+ *
+ * @see #onStartPrinterDiscovery()
+ * @see #addDiscoveredPrinters(List)
+ * @see #removeDiscoveredPrinters(List)
+ */
+ protected abstract void onStopPrinterDiscovery();
+
+ /**
+ * Adds discovered printers. This method should be called during a
+ * printer discovery period, i.e. after a call to
+ * {@link #onStartPrinterDiscovery()} and before the corresponding
+ * call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing.
+ * <p>
+ * <strong>Note:</strong> For every printer discovery period all
+ * printers have to be added. You can call this method as many times as
+ * necessary during the discovery period but should not pass in already
+ * added printers. If a printer is already added in the same printer
+ * discovery period, it will be ignored.
+ * </p>
+ *
+ * @param printers A list with discovered printers.
+ *
+ * @throws IllegalStateException If this service is not connected.
+ *
+ * @see #removeDiscoveredPrinters(List)
+ * @see #onStartPrinterDiscovery()
+ * @see #onStopPrinterDiscovery()
+ */
+ public final void addDiscoveredPrinters(List<PrinterInfo> printers) {
+ synchronized (mLock) {
+ if (mClient == null) {
+ throw new IllegalStateException("Print serivice not connected!");
+ }
+ if (mDiscoveringPrinters) {
+ try {
+ // Calling with a lock into the system is fine.
+ mClient.addDiscoveredPrinters(printers);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error adding discovered printers!", re);
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes discovered printers given their ids. This method should be called
+ * during a printer discovery period, i.e. after a call to
+ * {@link #onStartPrinterDiscovery()} and before the corresponding
+ * call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing.
+ * <p>
+ * For every printer discovery period all printers have to be added. You
+ * should remove only printers that were added in this printer discovery
+ * period by a call to {@link #addDiscoveredPrinters(List)}. You can call
+ * this method as many times as necessary during the discovery period
+ * but should not pass in already removed printer ids. If a printer with
+ * a given id is already removed in the same discovery period, it will
+ * be ignored.
+ * </p>
+ *
+ * @param printerIds A list with disappeared printer ids.
+ *
+ * @throws IllegalStateException If this service is not connected.
+ *
+ * @see #addDiscoveredPrinters(List)
+ * @see #onStartPrinterDiscovery()
+ * @see #onStopPrinterDiscovery()
+ */
+ public final void removeDiscoveredPrinters(List<PrinterId> printerIds) {
+ synchronized (mLock) {
+ if (mClient == null) {
+ throw new IllegalStateException("Print serivice not connected!");
+ }
+ if (mDiscoveringPrinters) {
+ try {
+ // Calling with a lock into the system is fine.
+ mClient.removeDiscoveredPrinters(printerIds);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error removing discovered printers!", re);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when canceling of a print job is requested. The service
+ * should do best effort to fulfill the request. After the print
+ * job is canceled by calling {@link PrintJob#cancel()}.
+ *
+ * @param printJob The print job to be canceled.
+ */
+ protected void onRequestCancelPrintJob(PrintJob printJob) {
+ }
+
+ /**
+ * Called when there is a queued print job for one of the printers
+ * managed by this print service. A queued print job is ready for
+ * processing by a print service which can get the data to be printed
+ * by calling {@link PrintJob#getData()}. This service may start
+ * processing the passed in print job or schedule handling of queued
+ * print jobs at a convenient time. The service can get the print
+ * jobs by a call to {@link #getPrintJobs()} and examine their state
+ * to find the ones with state {@link PrintJobInfo#STATE_QUEUED}.
+ *
+ * @param printJob The new queued print job.
+ *
+ * @see #getPrintJobs()
+ */
+ protected abstract void onPrintJobQueued(PrintJob printJob);
+
+ /**
+ * Gets the print jobs for the printers managed by this service.
+ *
+ * @return The print jobs.
+ *
+ * @throws IllegalStateException If this service is not connected.
+ */
+ public final List<PrintJob> getPrintJobs() {
+ synchronized (mLock) {
+ if (mClient == null) {
+ throw new IllegalStateException("Print serivice not connected!");
+ }
+ try {
+ List<PrintJob> printJobs = null;
+ List<PrintJobInfo> printJobInfos = mClient.getPrintJobs();
+ if (printJobInfos != null) {
+ final int printJobInfoCount = printJobInfos.size();
+ printJobs = new ArrayList<PrintJob>(printJobInfoCount);
+ for (int i = 0; i < printJobInfoCount; i++) {
+ printJobs.add(new PrintJob(printJobInfos.get(i), mClient));
+ }
+ }
+ if (printJobs != null) {
+ return printJobs;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling getPrintJobs()", re);
+ }
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * Generates a global printer id from a local id. The local id is unique
+ * only within this print service.
+ *
+ * @param localId The local id.
+ * @return Global printer id.
+ */
+ public final PrinterId generatePrinterId(String localId) {
+ return new PrinterId(new ComponentName(getPackageName(),
+ getClass().getName()), localId);
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return new IPrintService.Stub() {
+ @Override
+ public void setClient(IPrintServiceClient client) {
+ mHandler.obtainMessage(MyHandler.MESSAGE_SET_CLEINT, client).sendToTarget();
+ }
+
+ @Override
+ public void startPrinterDiscovery() {
+ mHandler.sendEmptyMessage(MyHandler.MESSAGE_START_PRINTER_DISCOVERY);
+ }
+
+ @Override
+ public void stopPrinterDiscovery() {
+ mHandler.sendEmptyMessage(MyHandler.MESSAGE_STOP_PRINTER_DISCOVERY);
+ }
+
+ @Override
+ public void requestCancelPrintJob(PrintJobInfo printJob) {
+ mHandler.obtainMessage(MyHandler.MESSAGE_CANCEL_PRINTJOB, printJob).sendToTarget();
+ }
+
+ @Override
+ public void onPrintJobQueued(PrintJobInfo printJob) {
+ mHandler.obtainMessage(MyHandler.MESSAGE_ON_PRINTJOB_QUEUED,
+ printJob).sendToTarget();
+ }
+ };
+ }
+
+ private final class MyHandler extends Handler {
+ public static final int MESSAGE_START_PRINTER_DISCOVERY = 1;
+ public static final int MESSAGE_STOP_PRINTER_DISCOVERY = 2;
+ public static final int MESSAGE_CANCEL_PRINTJOB = 3;
+ public static final int MESSAGE_ON_PRINTJOB_QUEUED = 4;
+ public static final int MESSAGE_SET_CLEINT = 5;
+
+ public MyHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ final int action = message.what;
+ switch (action) {
+ case MESSAGE_START_PRINTER_DISCOVERY: {
+ synchronized (mLock) {
+ mDiscoveringPrinters = true;
+ }
+ onStartPrinterDiscovery();
+ } break;
+
+ case MESSAGE_STOP_PRINTER_DISCOVERY: {
+ synchronized (mLock) {
+ mDiscoveringPrinters = false;
+ }
+ onStopPrinterDiscovery();
+ } break;
+
+ case MESSAGE_CANCEL_PRINTJOB: {
+ PrintJobInfo printJob = (PrintJobInfo) message.obj;
+ onRequestCancelPrintJob(new PrintJob(printJob, mClient));
+ } break;
+
+ case MESSAGE_ON_PRINTJOB_QUEUED: {
+ PrintJobInfo printJob = (PrintJobInfo) message.obj;
+ onPrintJobQueued(new PrintJob(printJob, mClient));
+ } break;
+
+ case MESSAGE_SET_CLEINT: {
+ IPrintServiceClient client = (IPrintServiceClient) message.obj;
+ synchronized (mLock) {
+ mClient = client;
+ if (client == null) {
+ mDiscoveringPrinters = false;
+ }
+ }
+ if (client != null) {
+ onConnected();
+ } else {
+ onStopPrinterDiscovery();
+ onDisconnected();
+ }
+ } break;
+
+ default: {
+ throw new IllegalArgumentException("Unknown message: " + action);
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/printservice/PrintServiceInfo.aidl b/core/java/android/printservice/PrintServiceInfo.aidl
new file mode 100644
index 0000000..95b67f2
--- /dev/null
+++ b/core/java/android/printservice/PrintServiceInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.printservice;
+
+parcelable PrintServiceInfo;
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
new file mode 100644
index 0000000..0370a25
--- /dev/null
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.printservice;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * This class describes a {@link PrintService}. A print service knows
+ * how to communicate with one or more printers over one or more protocols
+ * and exposes printers for use by the applications via the platform print
+ * APIs.
+ *
+ * @see PrintService
+ * @see android.print.PrintManager
+ *
+ * @hide
+ */
+public final class PrintServiceInfo implements Parcelable {
+
+ private static final boolean DEBUG = false;
+
+ private static final String LOG_TAG = PrintServiceInfo.class.getSimpleName();
+
+ private static final String TAG_PRINT_SERVICE = "print-service";
+
+ private final String mId;
+
+ private final ResolveInfo mResolveInfo;
+
+ private final String mSettingsActivityName;
+
+ private final String mAddPrintersActivityName;
+
+ /**
+ * Creates a new instance.
+ *
+ * @hide
+ */
+ public PrintServiceInfo(Parcel parcel) {
+ mId = parcel.readString();
+ mResolveInfo = parcel.readParcelable(null);
+ mSettingsActivityName = parcel.readString();
+ mAddPrintersActivityName = parcel.readString();
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param resolveInfo The service resolve info.
+ * @param settingsActivityName Optional settings activity name.
+ * @param addPrintersActivityName Optional add printers activity name.
+ */
+ public PrintServiceInfo(ResolveInfo resolveInfo, String settingsActivityName,
+ String addPrintersActivityName) {
+ mId = new ComponentName(resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name).flattenToString();
+ mResolveInfo = resolveInfo;
+ mSettingsActivityName = settingsActivityName;
+ mAddPrintersActivityName = addPrintersActivityName;
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param resolveInfo The service resolve info.
+ * @param context Context for accessing resources.
+ * @throws XmlPullParserException If a XML parsing error occurs.
+ * @throws IOException If a I/O error occurs.
+ * @hide
+ */
+ public static PrintServiceInfo create(ResolveInfo resolveInfo, Context context) {
+ String settingsActivityName = null;
+ String addPrintersActivityName = null;
+
+ XmlResourceParser parser = null;
+ PackageManager packageManager = context.getPackageManager();
+ parser = resolveInfo.serviceInfo.loadXmlMetaData(packageManager,
+ PrintService.SERVICE_META_DATA);
+ if (parser != null) {
+ try {
+ int type = 0;
+ while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
+
+ String nodeName = parser.getName();
+ if (!TAG_PRINT_SERVICE.equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data does not start with" + TAG_PRINT_SERVICE + " tag");
+ }
+
+ Resources resources = packageManager.getResourcesForApplication(
+ resolveInfo.serviceInfo.applicationInfo);
+ AttributeSet allAttributes = Xml.asAttributeSet(parser);
+ TypedArray attributes = resources.obtainAttributes(allAttributes,
+ com.android.internal.R.styleable.PrintService);
+
+ settingsActivityName = attributes.getString(
+ com.android.internal.R.styleable.PrintService_settingsActivity);
+
+ addPrintersActivityName = attributes.getString(
+ com.android.internal.R.styleable.PrintService_addPrintersActivity);
+
+ attributes.recycle();
+ } catch (IOException ioe) {
+ Log.w(LOG_TAG, "Error reading meta-data:" + ioe);
+ } catch (XmlPullParserException xppe) {
+ Log.w(LOG_TAG, "Error reading meta-data:" + xppe);
+ } catch (NameNotFoundException e) {
+ Log.e(LOG_TAG, "Unable to load resources for: "
+ + resolveInfo.serviceInfo.packageName);
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ }
+
+ return new PrintServiceInfo(resolveInfo, settingsActivityName, addPrintersActivityName);
+ }
+
+ /**
+ * The accessibility service id.
+ * <p>
+ * <strong>Generated by the system.</strong>
+ * </p>
+ *
+ * @return The id.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * The service {@link ResolveInfo}.
+ *
+ * @return The info.
+ */
+ public ResolveInfo getResolveInfo() {
+ return mResolveInfo;
+ }
+
+ /**
+ * The settings activity name.
+ * <p>
+ * <strong>Statically set from
+ * {@link PrintService#SERVICE_META_DATA meta-data}.</strong>
+ * </p>
+ *
+ * @return The settings activity name.
+ */
+ public String getSettingsActivityName() {
+ return mSettingsActivityName;
+ }
+
+ /**
+ * The add printers activity name.
+ * <p>
+ * <strong>Statically set from
+ * {@link PrintService#SERVICE_META_DATA meta-data}.</strong>
+ * </p>
+ *
+ * @return The add printers activity name.
+ */
+ public String getAddPrintersActivityName() {
+ return mAddPrintersActivityName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flagz) {
+ parcel.writeString(mId);
+ parcel.writeParcelable(mResolveInfo, 0);
+ parcel.writeString(mSettingsActivityName);
+ parcel.writeString(mAddPrintersActivityName);
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ PrintServiceInfo other = (PrintServiceInfo) obj;
+ if (mId == null) {
+ if (other.mId != null) {
+ return false;
+ }
+ } else if (!mId.equals(other.mId)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PrintServiceInfo{");
+ builder.append("id:").append(mId).append(", ");
+ builder.append("resolveInfo:").append(mResolveInfo).append(", ");
+ if (DEBUG) {
+ builder.append("settingsActivityName:").append(mSettingsActivityName);
+ builder.append("addPrintersActivityName:").append(mAddPrintersActivityName);
+ } else if (mSettingsActivityName != null || mAddPrintersActivityName != null) {
+ builder.append("<has meta-data>");
+ }
+ builder.append("}");
+ return builder.toString();
+ }
+
+ public static final Parcelable.Creator<PrintServiceInfo> CREATOR =
+ new Parcelable.Creator<PrintServiceInfo>() {
+ public PrintServiceInfo createFromParcel(Parcel parcel) {
+ return new PrintServiceInfo(parcel);
+ }
+
+ public PrintServiceInfo[] newArray(int size) {
+ return new PrintServiceInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 5dca67f..9d52c83 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -137,6 +137,22 @@
public static final String NUMBER = "number";
/**
+ * The number presenting rules set by the network for "allowed",
+ * "payphone", "restricted" or "unknown".
+ * <P>Type: INTEGER</P>
+ */
+ public static final String NUMBER_PRESENTATION = "presentation";
+
+ /** Number is allowed to display for caller id. */
+ public static final int PRESENTATION_ALLOWED = 1;
+ /** Number is blocked by user. */
+ public static final int PRESENTATION_RESTRICTED = 2;
+ /** Number is not specified or unknown by network. */
+ public static final int PRESENTATION_UNKNOWN = 3;
+ /** Number is a pay phone. */
+ public static final int PRESENTATION_PAYPHONE = 4;
+
+ /**
* The ISO 3166-1 two letters country code of the country where the
* user received or made the call.
* <P>
@@ -267,7 +283,8 @@
* if the contact is unknown.
* @param context the context used to get the ContentResolver
* @param number the phone number to be added to the calls db
- * @param presentation the number presenting rules set by the network for
+ * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
+ * is set by the network and denotes the number presenting rules for
* "allowed", "payphone", "restricted" or "unknown"
* @param callType enumerated values for "incoming", "outgoing", or "missed"
* @param start time stamp for the call in milliseconds
@@ -278,24 +295,32 @@
public static Uri addCall(CallerInfo ci, Context context, String number,
int presentation, int callType, long start, int duration) {
final ContentResolver resolver = context.getContentResolver();
+ int numberPresentation = PRESENTATION_ALLOWED;
- // If this is a private number then set the number to Private, otherwise check
- // if the number field is empty and set the number to Unavailable
+ // Remap network specified number presentation types
+ // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
+ // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
+ // from any future radio changes.
+ // If the number field is empty set the presentation type to Unknown.
if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
- number = CallerInfo.PRIVATE_NUMBER;
- if (ci != null) ci.name = "";
+ numberPresentation = PRESENTATION_RESTRICTED;
} else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
- number = CallerInfo.PAYPHONE_NUMBER;
- if (ci != null) ci.name = "";
+ numberPresentation = PRESENTATION_PAYPHONE;
} else if (TextUtils.isEmpty(number)
|| presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
- number = CallerInfo.UNKNOWN_NUMBER;
- if (ci != null) ci.name = "";
+ numberPresentation = PRESENTATION_UNKNOWN;
+ }
+ if (numberPresentation != PRESENTATION_ALLOWED) {
+ number = "";
+ if (ci != null) {
+ ci.name = "";
+ }
}
- ContentValues values = new ContentValues(5);
+ ContentValues values = new ContentValues(6);
values.put(NUMBER, number);
+ values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
values.put(TYPE, Integer.valueOf(callType));
values.put(DATE, Long.valueOf(start));
values.put(DURATION, Long.valueOf(duration));
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 220b997..e59f5b1 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -5105,9 +5105,8 @@
* Value of 1 implies true, 0 implies false when 0 is the default.
* When a cursor is returned to the client, it should check for an extra with the name
* {@link ContactsContract#DEFERRED_SNIPPETING} in the cursor. If it exists, the client
- * should do its own snippeting using {@link ContactsContract#snippetize}. If
- * it doesn't exist, the snippet column in the cursor should already contain a snippetized
- * string.
+ * should do its own snippeting. If it doesn't exist, the snippet column in the cursor
+ * should already contain a snippetized string.
*
* @hide
*/
@@ -8463,138 +8462,4 @@
public static final String DATA_SET = "com.android.contacts.extra.DATA_SET";
}
}
-
- /**
- * Creates a snippet out of the given content that matches the given query.
- * @param content - The content to use to compute the snippet.
- * @param displayName - Display name for the contact - if this already contains the search
- * content, no snippet should be shown.
- * @param query - String to search for in the content.
- * @param snippetStartMatch - Marks the start of the matching string in the snippet.
- * @param snippetEndMatch - Marks the end of the matching string in the snippet.
- * @param snippetEllipsis - Ellipsis string appended to the end of the snippet (if too long).
- * @param snippetMaxTokens - Maximum number of words from the snippet that will be displayed.
- * @return The computed snippet, or null if the snippet could not be computed or should not be
- * shown.
- *
- * @hide
- */
- public static String snippetize(String content, String displayName, String query,
- char snippetStartMatch, char snippetEndMatch, String snippetEllipsis,
- int snippetMaxTokens) {
-
- String lowerQuery = query != null ? query.toLowerCase() : null;
- if (TextUtils.isEmpty(content) || TextUtils.isEmpty(query) ||
- TextUtils.isEmpty(displayName) || !content.toLowerCase().contains(lowerQuery)) {
- return null;
- }
-
- // If the display name already contains the query term, return empty - snippets should
- // not be needed in that case.
- String lowerDisplayName = displayName != null ? displayName.toLowerCase() : "";
- List<String> nameTokens = new ArrayList<String>();
- List<Integer> nameTokenOffsets = new ArrayList<Integer>();
- split(lowerDisplayName.trim(), nameTokens, nameTokenOffsets);
- for (String nameToken : nameTokens) {
- if (nameToken.startsWith(lowerQuery)) {
- return null;
- }
- }
-
- String[] contentLines = content.split("\n");
-
- // Locate the lines of the content that contain the query term.
- for (String contentLine : contentLines) {
- if (contentLine.toLowerCase().contains(lowerQuery)) {
-
- // Line contains the query string - now search for it at the start of tokens.
- List<String> lineTokens = new ArrayList<String>();
- List<Integer> tokenOffsets = new ArrayList<Integer>();
- split(contentLine, lineTokens, tokenOffsets);
-
- // As we find matches against the query, we'll populate this list with the marked
- // (or unchanged) tokens.
- List<String> markedTokens = new ArrayList<String>();
-
- int firstToken = -1;
- int lastToken = -1;
- for (int i = 0; i < lineTokens.size(); i++) {
- String token = lineTokens.get(i);
- String lowerToken = token.toLowerCase();
- if (lowerToken.startsWith(lowerQuery)) {
-
- // Query term matched; surround the token with match markers.
- markedTokens.add(snippetStartMatch + token + snippetEndMatch);
-
- // If this is the first token found with a match, mark the token
- // positions to use for assembling the snippet.
- if (firstToken == -1) {
- firstToken =
- Math.max(0, i - (int) Math.floor(
- Math.abs(snippetMaxTokens)
- / 2.0));
- lastToken =
- Math.min(lineTokens.size(), firstToken +
- Math.abs(snippetMaxTokens));
- }
- } else {
- markedTokens.add(token);
- }
- }
-
- // Assemble the snippet by piecing the tokens back together.
- if (firstToken > -1) {
- StringBuilder sb = new StringBuilder();
- if (firstToken > 0) {
- sb.append(snippetEllipsis);
- }
- for (int i = firstToken; i < lastToken; i++) {
- String markedToken = markedTokens.get(i);
- String originalToken = lineTokens.get(i);
- sb.append(markedToken);
- if (i < lastToken - 1) {
- // Add the characters that appeared between this token and the next.
- sb.append(contentLine.substring(
- tokenOffsets.get(i) + originalToken.length(),
- tokenOffsets.get(i + 1)));
- }
- }
- if (lastToken < lineTokens.size()) {
- sb.append(snippetEllipsis);
- }
- return sb.toString();
- }
- }
- }
- return null;
- }
-
- /**
- * Pattern for splitting a line into tokens. This matches e-mail addresses as a single token,
- * otherwise splitting on any group of non-alphanumeric characters.
- *
- * @hide
- */
- private static Pattern SPLIT_PATTERN =
- Pattern.compile("([\\w-\\.]+)@((?:[\\w]+\\.)+)([a-zA-Z]{2,4})|[\\w]+");
-
- /**
- * Helper method for splitting a string into tokens. The lists passed in are populated with the
- * tokens and offsets into the content of each token. The tokenization function parses e-mail
- * addresses as a single token; otherwise it splits on any non-alphanumeric character.
- * @param content Content to split.
- * @param tokens List of token strings to populate.
- * @param offsets List of offsets into the content for each token returned.
- *
- * @hide
- */
- private static void split(String content, List<String> tokens, List<Integer> offsets) {
- Matcher matcher = SPLIT_PATTERN.matcher(content);
- while (matcher.find()) {
- tokens.add(matcher.group());
- offsets.add(matcher.start());
- }
- }
-
-
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
new file mode 100644
index 0000000..c26f6d4
--- /dev/null
+++ b/core/java/android/provider/DocumentsContract.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * The contract between a storage backend and the platform. Contains definitions
+ * for the supported URIs and columns.
+ */
+public final class DocumentsContract {
+ private static final String TAG = "Documents";
+
+ // content://com.example/docs/0/
+ // content://com.example/docs/0/contents/
+ // content://com.example/search/?query=pony
+
+ /**
+ * MIME type of a document which is a directory that may contain additional
+ * documents.
+ *
+ * @see #buildContentsUri(Uri)
+ */
+ public static final String MIME_TYPE_DIRECTORY = "vnd.android.cursor.dir/doc";
+
+ /** {@hide} */
+ public static final String META_DATA_DOCUMENT_PROVIDER = "android.content.DOCUMENT_PROVIDER";
+
+ /**
+ * {@link DocumentColumns#GUID} value representing the root directory of a
+ * storage backend.
+ */
+ public static final String ROOT_GUID = "0";
+
+ /**
+ * Flag indicating that a document is a directory that supports creation of
+ * new files within it.
+ *
+ * @see DocumentColumns#FLAGS
+ * @see #buildContentsUri(Uri)
+ */
+ public static final int FLAG_SUPPORTS_CREATE = 1;
+
+ /**
+ * Flag indicating that a document is renamable.
+ *
+ * @see DocumentColumns#FLAGS
+ * @see #renameDocument(ContentResolver, Uri, String)
+ */
+ public static final int FLAG_SUPPORTS_RENAME = 1 << 1;
+
+ /**
+ * Flag indicating that a document can be represented as a thumbnail.
+ *
+ * @see DocumentColumns#FLAGS
+ * @see #getThumbnail(ContentResolver, Uri, Point)
+ */
+ public static final int FLAG_SUPPORTS_THUMBNAIL = 1 << 2;
+
+ /**
+ * Optimal dimensions for a document thumbnail request, stored as a
+ * {@link Point} object. This is only a hint, and the returned thumbnail may
+ * have different dimensions.
+ */
+ public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size";
+
+ private static final String PATH_DOCS = "docs";
+ private static final String PATH_CONTENTS = "contents";
+ private static final String PATH_SEARCH = "search";
+
+ private static final String PARAM_QUERY = "query";
+
+ /**
+ * Build URI representing the given {@link DocumentColumns#GUID} in a
+ * storage backend.
+ */
+ public static Uri buildDocumentUri(String authority, String guid) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).appendPath(PATH_DOCS).appendPath(guid).build();
+ }
+
+ /**
+ * Build URI representing a search for matching documents in a storage
+ * backend.
+ */
+ public static Uri buildSearchUri(String authority, String query) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
+ .appendPath(PATH_SEARCH).appendQueryParameter(PARAM_QUERY, query).build();
+ }
+
+ /**
+ * Build URI representing the contents of the given directory in a storage
+ * backend. The given document must be {@link #MIME_TYPE_DIRECTORY}.
+ */
+ public static Uri buildContentsUri(Uri documentUri) {
+ return documentUri.buildUpon().appendPath(PATH_CONTENTS).build();
+ }
+
+ /**
+ * These are standard columns for document URIs. Storage backend providers
+ * <em>must</em> support at least these columns when queried.
+ *
+ * @see Intent#ACTION_OPEN_DOCUMENT
+ * @see Intent#ACTION_CREATE_DOCUMENT
+ */
+ public interface DocumentColumns extends OpenableColumns {
+ /**
+ * The globally unique ID for a document within a storage backend.
+ * Values <em>must</em> never change once returned.
+ * <p>
+ * Type: STRING
+ *
+ * @see DocumentsContract#ROOT_GUID
+ */
+ public static final String GUID = "guid";
+
+ /**
+ * MIME type of a document, matching the value returned by
+ * {@link ContentResolver#getType(android.net.Uri)}.
+ * <p>
+ * Type: STRING
+ *
+ * @see DocumentsContract#MIME_TYPE_DIRECTORY
+ */
+ public static final String MIME_TYPE = "mime_type";
+
+ /**
+ * Timestamp when a document was last modified, in milliseconds since
+ * January 1, 1970 00:00:00.0 UTC.
+ * <p>
+ * Type: INTEGER (long)
+ *
+ * @see System#currentTimeMillis()
+ */
+ public static final String LAST_MODIFIED = "last_modified";
+
+ /**
+ * Flags that apply to a specific document.
+ * <p>
+ * Type: INTEGER (int)
+ */
+ public static final String FLAGS = "flags";
+ }
+
+ /**
+ * Return thumbnail representing the document at the given URI. Callers are
+ * responsible for their own caching. Given document must have
+ * {@link #FLAG_SUPPORTS_THUMBNAIL} set.
+ *
+ * @return decoded thumbnail, or {@code null} if problem was encountered.
+ */
+ public static Bitmap getThumbnail(ContentResolver resolver, Uri documentUri, Point size) {
+ final Bundle opts = new Bundle();
+ opts.putParcelable(EXTRA_THUMBNAIL_SIZE, size);
+
+ InputStream is = null;
+ try {
+ is = new AssetFileDescriptor.AutoCloseInputStream(
+ resolver.openTypedAssetFileDescriptor(documentUri, "image/*", opts));
+ return BitmapFactory.decodeStream(is);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
+ return null;
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+ }
+
+ /**
+ * Rename the document at the given URI. Given document must have
+ * {@link #FLAG_SUPPORTS_RENAME} set.
+ *
+ * @return if rename was successful.
+ */
+ public static boolean renameDocument(
+ ContentResolver resolver, Uri documentUri, String displayName) {
+ final ContentValues values = new ContentValues();
+ values.put(DocumentColumns.DISPLAY_NAME, displayName);
+ return (resolver.update(documentUri, values, null, null) == 1);
+ }
+}
diff --git a/core/java/android/provider/DrmStore.java b/core/java/android/provider/DrmStore.java
deleted file mode 100644
index 34f2f0d..0000000
--- a/core/java/android/provider/DrmStore.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.
- */
-
-package android.provider;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.drm.mobile1.DrmRawContent;
-import android.drm.mobile1.DrmRights;
-import android.drm.mobile1.DrmRightsManager;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * The DRM provider contains forward locked DRM content.
- *
- * @hide
- */
-public final class DrmStore
-{
- private static final String TAG = "DrmStore";
-
- public static final String AUTHORITY = "drm";
-
- /**
- * This is in the Manifest class of the drm provider, but that isn't visible
- * in the framework.
- */
- private static final String ACCESS_DRM_PERMISSION = "android.permission.ACCESS_DRM";
-
- /**
- * Fields for DRM database
- */
-
- public interface Columns extends BaseColumns {
- /**
- * The data stream for the file
- * <P>Type: DATA STREAM</P>
- */
- public static final String DATA = "_data";
-
- /**
- * The size of the file in bytes
- * <P>Type: INTEGER (long)</P>
- */
- public static final String SIZE = "_size";
-
- /**
- * The title of the file content
- * <P>Type: TEXT</P>
- */
- public static final String TITLE = "title";
-
- /**
- * The MIME type of the file
- * <P>Type: TEXT</P>
- */
- public static final String MIME_TYPE = "mime_type";
-
- }
-
- public interface Images extends Columns {
-
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/images");
- }
-
- public interface Audio extends Columns {
-
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/audio");
- }
-
- /**
- * Utility function for inserting a file into the DRM content provider.
- *
- * @param cr The content resolver to use
- * @param file The file to insert
- * @param title The title for the content (or null)
- * @return uri to the DRM record or null
- */
- public static final Intent addDrmFile(ContentResolver cr, File file, String title) {
- FileInputStream fis = null;
- Intent result = null;
-
- try {
- fis = new FileInputStream(file);
- if (title == null) {
- title = file.getName();
- int lastDot = title.lastIndexOf('.');
- if (lastDot > 0) {
- title = title.substring(0, lastDot);
- }
- }
- result = addDrmFile(cr, fis, title);
- } catch (Exception e) {
- Log.e(TAG, "pushing file failed", e);
- } finally {
- try {
- if (fis != null)
- fis.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in DrmStore.addDrmFile()", e);
- }
- }
-
- return result;
- }
-
- /**
- * Utility function for inserting a file stream into the DRM content provider.
- *
- * @param cr The content resolver to use
- * @param fis The FileInputStream to insert
- * @param title The title for the content (or null)
- * @return uri to the DRM record or null
- */
- public static final Intent addDrmFile(ContentResolver cr, FileInputStream fis, String title) {
- OutputStream os = null;
- Intent result = null;
-
- try {
- DrmRawContent content = new DrmRawContent(fis, (int) fis.available(),
- DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING);
- String mimeType = content.getContentType();
- long size = fis.getChannel().size();
-
- DrmRightsManager manager = manager = DrmRightsManager.getInstance();
- DrmRights rights = manager.queryRights(content);
- InputStream stream = content.getContentInputStream(rights);
-
- Uri contentUri = null;
- if (mimeType.startsWith("audio/")) {
- contentUri = DrmStore.Audio.CONTENT_URI;
- } else if (mimeType.startsWith("image/")) {
- contentUri = DrmStore.Images.CONTENT_URI;
- } else {
- Log.w(TAG, "unsupported mime type " + mimeType);
- }
-
- if (contentUri != null) {
- ContentValues values = new ContentValues(3);
- values.put(DrmStore.Columns.TITLE, title);
- values.put(DrmStore.Columns.SIZE, size);
- values.put(DrmStore.Columns.MIME_TYPE, mimeType);
-
- Uri uri = cr.insert(contentUri, values);
- if (uri != null) {
- os = cr.openOutputStream(uri);
-
- byte[] buffer = new byte[1000];
- int count;
-
- while ((count = stream.read(buffer)) != -1) {
- os.write(buffer, 0, count);
- }
- result = new Intent();
- result.setDataAndType(uri, mimeType);
-
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "pushing file failed", e);
- } finally {
- try {
- if (fis != null)
- fis.close();
- if (os != null)
- os.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in DrmStore.addDrmFile()", e);
- }
- }
-
- return result;
- }
-
- /**
- * Utility function to enforce any permissions required to access DRM
- * content.
- *
- * @param context A context used for checking calling permission.
- */
- public static void enforceAccessDrmPermission(Context context) {
- if (context.checkCallingOrSelfPermission(ACCESS_DRM_PERMISSION) !=
- PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires DRM permission");
- }
- }
-
-}
diff --git a/core/java/android/provider/OpenableColumns.java b/core/java/android/provider/OpenableColumns.java
index f548bae..faf96b7 100644
--- a/core/java/android/provider/OpenableColumns.java
+++ b/core/java/android/provider/OpenableColumns.java
@@ -16,11 +16,17 @@
package android.provider;
+import android.content.ContentResolver;
+import android.content.Intent;
+
/**
- * These are standard columns for openable URIs. (See
- * {@link android.content.Intent#CATEGORY_OPENABLE}.) If possible providers that have openable URIs
- * should support these columns. To find the content type of a URI use
- * {@link android.content.ContentResolver#getType(android.net.Uri)} as normal.
+ * These are standard columns for openable URIs. Providers that serve openable
+ * URIs <em>must</em> support at least these columns when queried.
+ * <p>
+ * To find the content type of a URI, use
+ * {@link ContentResolver#getType(android.net.Uri)}.
+ *
+ * @see Intent#CATEGORY_OPENABLE
*/
public interface OpenableColumns {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3c8187e..9685fb0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1010,6 +1010,14 @@
MOVED_TO_GLOBAL.add(Settings.Global.WAIT_FOR_DEBUGGER);
MOVED_TO_GLOBAL.add(Settings.Global.SHOW_PROCESSES);
MOVED_TO_GLOBAL.add(Settings.Global.ALWAYS_FINISH_ACTIVITIES);
+ MOVED_TO_GLOBAL.add(Settings.Global.TZINFO_UPDATE_CONTENT_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.TZINFO_UPDATE_METADATA_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.SELINUX_UPDATE_CONTENT_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.SELINUX_UPDATE_METADATA_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.SMS_SHORT_CODES_UPDATE_CONTENT_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_CONTENT_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_METADATA_URL);
}
/** @hide */
@@ -3500,6 +3508,13 @@
public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
/**
+ * List of the enabled print providers.
+ * @hide
+ */
+ public static final String ENABLED_PRINT_SERVICES =
+ "enabled_print_services";
+
+ /**
* Setting to always use the default text-to-speech settings regardless
* of the application settings.
* 1 = override application settings,
@@ -3968,6 +3983,14 @@
*/
public static final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service";
+ /**
+ * Stores whether an user has consented to have apps verified through PAM.
+ * The value is boolean (1 or 0).
+ *
+ * @hide
+ */
+ public static final String PACKAGE_VERIFIER_USER_CONSENT =
+ "package_verifier_user_consent";
/**
* The {@link ComponentName} string of the selected spell checker service which is
@@ -4081,6 +4104,9 @@
*/
public static final String DIALPAD_AUTOCOMPLETE = "dialpad_autocomplete";
+ /** @hide */
+ public static final String BAR_SERVICE_COMPONENT = "bar_service_component";
+
/**
* This are the settings to be backed up.
*
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index bfea9ca..2e0e59b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -164,11 +164,19 @@
private class INotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
- NotificationListenerService.this.onNotificationPosted(sbn);
+ try {
+ NotificationListenerService.this.onNotificationPosted(sbn);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationPosted", t);
+ }
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
- NotificationListenerService.this.onNotificationRemoved(sbn);
+ try {
+ NotificationListenerService.this.onNotificationRemoved(sbn);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationRemoved", t);
+ }
}
}
}
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
index 6398d3d..917a109 100644
--- a/core/java/android/speech/tts/SynthesisRequest.java
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -41,6 +41,7 @@
private String mVariant;
private int mSpeechRate;
private int mPitch;
+ private int mCallerUid;
public SynthesisRequest(String text, Bundle params) {
mText = text;
@@ -98,6 +99,13 @@
}
/**
+ * Gets the request caller Uid.
+ */
+ public int getCallerUid() {
+ return mCallerUid;
+ }
+
+ /**
* Sets the locale for the request.
*/
void setLanguage(String language, String country, String variant) {
@@ -119,4 +127,11 @@
void setPitch(int pitch) {
mPitch = pitch;
}
+
+ /**
+ * Sets Caller Uid
+ */
+ void setCallerUid(int uid) {
+ mCallerUid = uid;
+ }
}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 578a86e..b808363 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -561,7 +561,8 @@
* The context this instance is running in.
* @param listener
* The {@link TextToSpeech.OnInitListener} that will be called when the
- * TextToSpeech engine has initialized.
+ * TextToSpeech engine has initialized. In a case of a failure the listener
+ * may be called immediately, before TextToSpeech instance is fully constructed.
*/
public TextToSpeech(Context context, OnInitListener listener) {
this(context, listener, null);
@@ -575,7 +576,8 @@
* The context this instance is running in.
* @param listener
* The {@link TextToSpeech.OnInitListener} that will be called when the
- * TextToSpeech engine has initialized.
+ * TextToSpeech engine has initialized. In a case of a failure the listener
+ * may be called immediately, before TextToSpeech instance is fully constructed.
* @param engine Package name of the TTS engine to use.
*/
public TextToSpeech(Context context, OnInitListener listener, String engine) {
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 703dcff..575855c 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -557,11 +557,13 @@
// guarded by 'this'.
private AbstractSynthesisCallback mSynthesisCallback;
private final EventLogger mEventLogger;
+ private final int mCallerUid;
public SynthesisSpeechItem(Object callerIdentity, int callerUid, int callerPid,
Bundle params, String text) {
super(callerIdentity, callerUid, callerPid, params);
mText = text;
+ mCallerUid = callerUid;
mSynthesisRequest = new SynthesisRequest(mText, mParams);
mDefaultLocale = getSettingsLocale();
setRequestParams(mSynthesisRequest);
@@ -611,7 +613,7 @@
private void setRequestParams(SynthesisRequest request) {
request.setLanguage(getLanguage(), getCountry(), getVariant());
request.setSpeechRate(getSpeechRate());
-
+ request.setCallerUid(mCallerUid);
request.setPitch(getPitch());
}
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 2706bc7..5fbd22e 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -355,7 +355,18 @@
return v1Locale;
}
- private String getDefaultLocale() {
+ /**
+ * Return the default device locale in form of 3 letter codes delimited by
+ * {@link #LOCALE_DELIMITER}:
+ * <ul>
+ * <li> "ISO 639-2/T language code" if locale have no country entry</li>
+ * <li> "ISO 639-2/T language code{@link #LOCALE_DELIMITER}ISO 3166 country code "
+ * if locale have no variant entry</li>
+ * <li> "ISO 639-2/T language code{@link #LOCALE_DELIMITER}ISO 3166 country code
+ * {@link #LOCALE_DELIMITER} variant" if locale have variant entry</li>
+ * </ul>
+ */
+ public String getDefaultLocale() {
final Locale locale = Locale.getDefault();
// Note that the default locale might have an empty variant
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 1291279..e7d6fda 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -474,6 +474,8 @@
mLineCount < mMaximumVisibleLineCount) {
// Log.e("text", "output last " + bufEnd);
+ measured.setPara(source, bufStart, bufEnd, textDir);
+
paint.getFontMetricsInt(fm);
v = out(source,
@@ -482,7 +484,7 @@
v,
spacingmult, spacingadd, null,
null, fm, false,
- needMultiply, null, DEFAULT_DIR, true, bufEnd,
+ needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
includepad, trackpad, null,
null, bufStart, ellipsize,
ellipsizedWidth, 0, paint, false);
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 30bb447..ba6f1d4 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -56,7 +56,7 @@
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0
&& MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0) {
+ MetaKeyKeyListener.META_SELECTING, event) != 0) {
return widget.showContextMenu();
}
}
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 4fede32..63607fa 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -75,7 +75,7 @@
}
// Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible.
- if (event.isAltPressed() || getMetaState(content, META_ALT_ON) == 1) {
+ if (getMetaState(content, META_ALT_ON, event) == 1) {
if (deleteLine(view, content)) {
return true;
}
diff --git a/core/java/android/text/method/BaseMovementMethod.java b/core/java/android/text/method/BaseMovementMethod.java
index 113a4be..155a2c4 100644
--- a/core/java/android/text/method/BaseMovementMethod.java
+++ b/core/java/android/text/method/BaseMovementMethod.java
@@ -135,7 +135,7 @@
*/
protected int getMovementMetaState(Spannable buffer, KeyEvent event) {
// We ignore locked modifiers and SHIFT.
- int metaState = (event.getMetaState() | MetaKeyKeyListener.getMetaState(buffer))
+ int metaState = MetaKeyKeyListener.getMetaState(buffer, event)
& ~(MetaKeyKeyListener.META_ALT_LOCKED | MetaKeyKeyListener.META_SYM_LOCKED);
return KeyEvent.normalizeMetaState(metaState) & ~KeyEvent.META_SHIFT_MASK;
}
diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java
index ce51fae..bb8b0de 100644
--- a/core/java/android/text/method/DialerKeyListener.java
+++ b/core/java/android/text/method/DialerKeyListener.java
@@ -53,7 +53,7 @@
* from the KeyEvent.
*/
protected int lookup(KeyEvent event, Spannable content) {
- int meta = event.getMetaState() | getMetaState(content);
+ int meta = getMetaState(content, event);
int number = event.getNumber();
/*
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index aff233d..3855ff3 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -36,6 +36,11 @@
private static final int DOWN = 3;
@Override
+ public boolean canSelectArbitrarily() {
+ return true;
+ }
+
+ @Override
protected boolean handleMovementKey(TextView widget, Spannable buffer, int keyCode,
int movementMetaState, KeyEvent event) {
switch (keyCode) {
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index 0a097f9..e9db5fd 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -135,6 +135,9 @@
private static final Object SYM = new NoCopySpan.Concrete();
private static final Object SELECTING = new NoCopySpan.Concrete();
+ private static final int PRESSED_RETURN_VALUE = 1;
+ private static final int LOCKED_RETURN_VALUE = 2;
+
/**
* Resets all meta state to inactive.
*/
@@ -161,9 +164,34 @@
}
/**
+ * Gets the state of the meta keys for a specific key event.
+ *
+ * For input devices that use toggled key modifiers, the `toggled' state
+ * is stored into the text buffer. This method retrieves the meta state
+ * for this event, accounting for the stored state. If the event has been
+ * created by a device that does not support toggled key modifiers, like
+ * a virtual device for example, the stored state is ignored.
+ *
+ * @param text the buffer in which the meta key would have been pressed.
+ * @param event the event for which to evaluate the meta state.
+ * @return an integer in which each bit set to one represents a pressed
+ * or locked meta key.
+ */
+ public static final int getMetaState(final CharSequence text, final KeyEvent event) {
+ int metaState = event.getMetaState();
+ if (event.getKeyCharacterMap().getModifierBehavior()
+ == KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED) {
+ metaState |= getMetaState(text);
+ }
+ return metaState;
+ }
+
+ // As META_SELECTING is @hide we should not mention it in public comments, hence the
+ // omission in @param meta
+ /**
* Gets the state of a particular meta key.
*
- * @param meta META_SHIFT_ON, META_ALT_ON, META_SYM_ON, or META_SELECTING
+ * @param meta META_SHIFT_ON, META_ALT_ON, META_SYM_ON
* @param text the buffer in which the meta key would have been pressed.
*
* @return 0 if inactive, 1 if active, 2 if locked.
@@ -171,22 +199,53 @@
public static final int getMetaState(CharSequence text, int meta) {
switch (meta) {
case META_SHIFT_ON:
- return getActive(text, CAP, 1, 2);
+ return getActive(text, CAP, PRESSED_RETURN_VALUE, LOCKED_RETURN_VALUE);
case META_ALT_ON:
- return getActive(text, ALT, 1, 2);
+ return getActive(text, ALT, PRESSED_RETURN_VALUE, LOCKED_RETURN_VALUE);
case META_SYM_ON:
- return getActive(text, SYM, 1, 2);
+ return getActive(text, SYM, PRESSED_RETURN_VALUE, LOCKED_RETURN_VALUE);
case META_SELECTING:
- return getActive(text, SELECTING, 1, 2);
+ return getActive(text, SELECTING, PRESSED_RETURN_VALUE, LOCKED_RETURN_VALUE);
default:
return 0;
}
}
+ /**
+ * Gets the state of a particular meta key to use with a particular key event.
+ *
+ * If the key event has been created by a device that does not support toggled
+ * key modifiers, like a virtual keyboard for example, only the meta state in
+ * the key event is considered.
+ *
+ * @param meta META_SHIFT_ON, META_ALT_ON, META_SYM_ON
+ * @param text the buffer in which the meta key would have been pressed.
+ * @param event the event for which to evaluate the meta state.
+ * @return 0 if inactive, 1 if active, 2 if locked.
+ */
+ public static final int getMetaState(final CharSequence text, final int meta,
+ final KeyEvent event) {
+ int metaState = event.getMetaState();
+ if (event.getKeyCharacterMap().getModifierBehavior()
+ == KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED) {
+ metaState |= getMetaState(text);
+ }
+ if (META_SELECTING == meta) {
+ // #getMetaState(long, int) does not support META_SELECTING, but we want the same
+ // behavior as #getMetaState(CharSequence, int) so we need to do it here
+ if ((metaState & META_SELECTING) != 0) {
+ // META_SELECTING is only ever set to PRESSED and can't be LOCKED, so return 1
+ return 1;
+ }
+ return 0;
+ }
+ return getMetaState(metaState, meta);
+ }
+
private static int getActive(CharSequence text, Object meta,
int on, int lock) {
if (!(text instanceof Spanned)) {
@@ -430,18 +489,18 @@
public static final int getMetaState(long state, int meta) {
switch (meta) {
case META_SHIFT_ON:
- if ((state & META_CAP_LOCKED) != 0) return 2;
- if ((state & META_SHIFT_ON) != 0) return 1;
+ if ((state & META_CAP_LOCKED) != 0) return LOCKED_RETURN_VALUE;
+ if ((state & META_SHIFT_ON) != 0) return PRESSED_RETURN_VALUE;
return 0;
case META_ALT_ON:
- if ((state & META_ALT_LOCKED) != 0) return 2;
- if ((state & META_ALT_ON) != 0) return 1;
+ if ((state & META_ALT_LOCKED) != 0) return LOCKED_RETURN_VALUE;
+ if ((state & META_ALT_ON) != 0) return PRESSED_RETURN_VALUE;
return 0;
case META_SYM_ON:
- if ((state & META_SYM_LOCKED) != 0) return 2;
- if ((state & META_SYM_ON) != 0) return 1;
+ if ((state & META_SYM_LOCKED) != 0) return LOCKED_RETURN_VALUE;
+ if ((state & META_SYM_ON) != 0) return PRESSED_RETURN_VALUE;
return 0;
default:
@@ -599,4 +658,3 @@
private static final int LOCKED =
Spannable.SPAN_MARK_MARK | (4 << Spannable.SPAN_USER_SHIFT);
}
-
diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java
index 5d4c732..988d566 100644
--- a/core/java/android/text/method/NumberKeyListener.java
+++ b/core/java/android/text/method/NumberKeyListener.java
@@ -41,7 +41,7 @@
protected abstract char[] getAcceptedChars();
protected int lookup(KeyEvent event, Spannable content) {
- return event.getMatch(getAcceptedChars(), event.getMetaState() | getMetaState(content));
+ return event.getMatch(getAcceptedChars(), getMetaState(content, event));
}
public CharSequence filter(CharSequence source, int start, int end,
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index 98316ae..0bd46bc 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -108,7 +108,7 @@
// QWERTY keyboard normal case
- int i = event.getUnicodeChar(event.getMetaState() | getMetaState(content));
+ int i = event.getUnicodeChar(getMetaState(content, event));
if (!mFullKeyboard) {
int count = event.getRepeatCount();
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
new file mode 100644
index 0000000..26bcce4
--- /dev/null
+++ b/core/java/android/util/ArrayMap.java
@@ -0,0 +1,729 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ArrayMap is a generic key->value mapping data structure that is
+ * designed to be more memory efficient than a traditional {@link java.util.HashMap}.
+ * It keeps its mappings in an array data structure -- an integer array of hash
+ * codes for each item, and an Object array of the key/value pairs. This allows it to
+ * avoid having to create an extra object for every entry put in to the map, and it
+ * also tries to control the growth of the size of these arrays more aggressively
+ * (since growing them only requires copying the entries in the array, not rebuilding
+ * a hash map).
+ *
+ * <p>Note that this implementation is not intended to be appropriate for data structures
+ * that may contain large numbers of items. It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array. For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%. For larger numbers of items
+ * this data structure should be avoided.</p>
+ *
+ * <p><b>Note:</b> unlike {@link java.util.HashMap}, this container does not support
+ * null keys.</p>
+ *
+ * <p>Because this container is intended to better balance memory use, unlike most other
+ * standard Java containers it will shrink its array as items are removed from it. Currently
+ * you have no control over this shrinking -- if you set a capacity and then remove an
+ * item, it may reduce the capacity to better match the current size. In the future an
+ * explicitly call to set the capacity should turn off this aggressive shrinking behavior.</p>
+ *
+ * @hide
+ */
+public final class ArrayMap<K, V> implements Map<K, V> {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "ArrayMap";
+
+ /**
+ * The minimum amount by which the capacity of a ArrayMap will increase.
+ * This is tuned to be relatively space-efficient.
+ */
+ private static final int BASE_SIZE = 4;
+
+ /**
+ * Maximum number of entries to have in array caches.
+ */
+ private static final int CACHE_SIZE = 10;
+
+ /**
+ * Caches of small array objects to avoid spamming garbage. The cache
+ * Object[] variable is a pointer to a linked list of array objects.
+ * The first entry in the array is a pointer to the next array in the
+ * list; the second entry is a pointer to the int[] hash code array for it.
+ */
+ static Object[] mBaseCache;
+ static int mBaseCacheSize;
+ static Object[] mTwiceBaseCache;
+ static int mTwiceBaseCacheSize;
+
+ int[] mHashes;
+ Object[] mArray;
+ int mSize;
+ MapCollections<K, V> mCollections;
+
+ private int indexOf(Object key, int hash) {
+ final int N = mSize;
+
+ // Important fast case: if nothing is in here, nothing to look for.
+ if (N == 0) {
+ return ~0;
+ }
+
+ int index = SparseArray.binarySearch(mHashes, N, hash);
+
+ // If the hash code wasn't found, then we have no entry for this key.
+ if (index < 0) {
+ return index;
+ }
+
+ // If the key at the returned index matches, that's what we want.
+ if (mArray[index<<1].equals(key)) {
+ return index;
+ }
+
+ // Search for a matching key after the index.
+ int end;
+ for (end = index + 1; end < N && mHashes[end] == hash; end++) {
+ if (mArray[end << 1].equals(key)) return end;
+ }
+
+ // Search for a matching key before the index.
+ for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
+ if (mArray[i << 1].equals(key)) return i;
+ }
+
+ // Key not found -- return negative value indicating where a
+ // new entry for this key should go. We use the end of the
+ // hash chain to reduce the number of array entries that will
+ // need to be copied when inserting.
+ return ~end;
+ }
+
+ private void allocArrays(final int size) {
+ if (size == (BASE_SIZE*2)) {
+ synchronized (ArrayMap.class) {
+ if (mTwiceBaseCache != null) {
+ final Object[] array = mTwiceBaseCache;
+ mArray = array;
+ mTwiceBaseCache = (Object[])array[0];
+ mHashes = (int[])array[1];
+ array[0] = array[1] = null;
+ mTwiceBaseCacheSize--;
+ if (DEBUG) Log.d(TAG, "Retrieving 2x cache " + mHashes
+ + " now have " + mTwiceBaseCacheSize + " entries");
+ return;
+ }
+ }
+ } else if (size == BASE_SIZE) {
+ synchronized (ArrayMap.class) {
+ if (mBaseCache != null) {
+ final Object[] array = mBaseCache;
+ mArray = array;
+ mBaseCache = (Object[])array[0];
+ mHashes = (int[])array[1];
+ array[0] = array[1] = null;
+ mBaseCacheSize--;
+ if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes
+ + " now have " + mBaseCacheSize + " entries");
+ return;
+ }
+ }
+ }
+
+ mHashes = new int[size];
+ mArray = new Object[size<<1];
+ }
+
+ private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
+ if (hashes.length == (BASE_SIZE*2)) {
+ synchronized (ArrayMap.class) {
+ if (mTwiceBaseCacheSize < CACHE_SIZE) {
+ array[0] = mTwiceBaseCache;
+ array[1] = hashes;
+ for (int i=(size<<1)-1; i>=2; i--) {
+ array[i] = null;
+ }
+ mTwiceBaseCache = array;
+ mTwiceBaseCacheSize++;
+ if (DEBUG) Log.d(TAG, "Storing 2x cache " + array
+ + " now have " + mTwiceBaseCacheSize + " entries");
+ }
+ }
+ } else if (hashes.length == BASE_SIZE) {
+ synchronized (ArrayMap.class) {
+ if (mBaseCacheSize < CACHE_SIZE) {
+ array[0] = mBaseCache;
+ array[1] = hashes;
+ for (int i=(size<<1)-1; i>=2; i--) {
+ array[i] = null;
+ }
+ mBaseCache = array;
+ mBaseCacheSize++;
+ if (DEBUG) Log.d(TAG, "Storing 1x cache " + array
+ + " now have " + mBaseCacheSize + " entries");
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a new empty ArrayMap. The default capacity of an array map is 0, and
+ * will grow once items are added to it.
+ */
+ public ArrayMap() {
+ mHashes = SparseArray.EMPTY_INTS;
+ mArray = SparseArray.EMPTY_OBJECTS;
+ mSize = 0;
+ }
+
+ /**
+ * Create a new ArrayMap with a given initial capacity.
+ */
+ public ArrayMap(int capacity) {
+ if (capacity == 0) {
+ mHashes = SparseArray.EMPTY_INTS;
+ mArray = SparseArray.EMPTY_OBJECTS;
+ } else {
+ allocArrays(capacity);
+ }
+ mSize = 0;
+ }
+
+ /**
+ * Create a new ArrayMap with the mappings from the given ArrayMap.
+ */
+ public ArrayMap(ArrayMap map) {
+ this();
+ if (map != null) {
+ putAll(map);
+ }
+ }
+
+ /**
+ * Make the array map empty. All storage is released.
+ */
+ @Override
+ public void clear() {
+ if (mSize != 0) {
+ freeArrays(mHashes, mArray, mSize);
+ mHashes = SparseArray.EMPTY_INTS;
+ mArray = SparseArray.EMPTY_OBJECTS;
+ mSize = 0;
+ }
+ }
+
+ /**
+ * Ensure the array map can hold at least <var>minimumCapacity</var>
+ * items.
+ */
+ public void ensureCapacity(int minimumCapacity) {
+ if (mHashes.length < minimumCapacity) {
+ final int[] ohashes = mHashes;
+ final Object[] oarray = mArray;
+ allocArrays(minimumCapacity);
+ if (mSize > 0) {
+ System.arraycopy(ohashes, 0, mHashes, 0, mSize);
+ System.arraycopy(oarray, 0, mArray, 0, mSize<<1);
+ }
+ freeArrays(ohashes, oarray, mSize);
+ }
+ }
+
+ /**
+ * Check whether a key exists in the array.
+ *
+ * @param key The key to search for.
+ * @return Returns true if the key exists, else false.
+ */
+ @Override
+ public boolean containsKey(Object key) {
+ return indexOf(key, key.hashCode()) >= 0;
+ }
+
+ private int indexOfValue(Object value) {
+ final int N = mSize*2;
+ final Object[] array = mArray;
+ if (value == null) {
+ for (int i=1; i<N; i+=2) {
+ if (array[i] == null) {
+ return i>>1;
+ }
+ }
+ } else {
+ for (int i=1; i<N; i+=2) {
+ if (value.equals(array[i])) {
+ return i>>1;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Check whether a value exists in the array. This requires a linear search
+ * through the entire array.
+ *
+ * @param value The value to search for.
+ * @return Returns true if the value exists, else false.
+ */
+ @Override
+ public boolean containsValue(Object value) {
+ return indexOfValue(value) >= 0;
+ }
+
+ /**
+ * Retrieve a value from the array.
+ * @param key The key of the value to retrieve.
+ * @return Returns the value associated with the given key,
+ * or null if there is no such key.
+ */
+ @Override
+ public V get(Object key) {
+ final int index = indexOf(key, key.hashCode());
+ return index >= 0 ? (V)mArray[(index<<1)+1] : null;
+ }
+
+ /**
+ * Return the key at the given index in the array.
+ * @param index The desired index, must be between 0 and {@link #size()}-1.
+ * @return Returns the key stored at the given index.
+ */
+ public K keyAt(int index) {
+ return (K)mArray[index << 1];
+ }
+
+ /**
+ * Return the value at the given index in the array.
+ * @param index The desired index, must be between 0 and {@link #size()}-1.
+ * @return Returns the value stored at the given index.
+ */
+ public V valueAt(int index) {
+ return (V)mArray[(index << 1) + 1];
+ }
+
+ /**
+ * Set the value at a given index in the array.
+ * @param index The desired index, must be between 0 and {@link #size()}-1.
+ * @param value The new value to store at this index.
+ * @return Returns the previous value at the given index.
+ */
+ public V setValueAt(int index, V value) {
+ index = (index << 1) + 1;
+ V old = (V)mArray[index];
+ mArray[index] = value;
+ return old;
+ }
+
+ /**
+ * Return true if the array map contains no items.
+ */
+ @Override
+ public boolean isEmpty() {
+ return mSize <= 0;
+ }
+
+ /**
+ * Add a new value to the array map.
+ * @param key The key under which to store the value. <b>Must not be null.</b> If
+ * this key already exists in the array, its value will be replaced.
+ * @param value The value to store for the given key.
+ * @return Returns the old value that was stored for the given key, or null if there
+ * was no such key.
+ */
+ @Override
+ public V put(K key, V value) {
+ final int hash = key.hashCode();
+ int index = indexOf(key, hash);
+ if (index >= 0) {
+ index = (index<<1) + 1;
+ final V old = (V)mArray[index];
+ mArray[index] = value;
+ return old;
+ }
+
+ index = ~index;
+ if (mSize >= mHashes.length) {
+ final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))
+ : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
+
+ if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);
+
+ final int[] ohashes = mHashes;
+ final Object[] oarray = mArray;
+ allocArrays(n);
+
+ if (mHashes.length > 0) {
+ if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0");
+ System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
+ System.arraycopy(oarray, 0, mArray, 0, oarray.length);
+ }
+
+ freeArrays(ohashes, oarray, mSize);
+ }
+
+ if (index < mSize) {
+ if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index)
+ + " to " + (index+1));
+ System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
+ System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
+ }
+
+ mHashes[index] = hash;
+ mArray[index<<1] = key;
+ mArray[(index<<1)+1] = value;
+ mSize++;
+ return null;
+ }
+
+ /**
+ * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>array</var>
+ * @param array The array whose contents are to be retrieved.
+ */
+ public void putAll(ArrayMap<? extends K, ? extends V> array) {
+ final int N = array.mSize;
+ ensureCapacity(mSize + N);
+ if (mSize == 0) {
+ if (N > 0) {
+ System.arraycopy(array.mHashes, 0, mHashes, 0, N);
+ System.arraycopy(array.mArray, 0, mArray, 0, N<<1);
+ mSize = N;
+ }
+ } else {
+ for (int i=0; i<N; i++) {
+ put(array.keyAt(i), array.valueAt(i));
+ }
+ }
+ }
+
+ /**
+ * Remove an existing key from the array map.
+ * @param key The key of the mapping to remove.
+ * @return Returns the value that was stored under the key, or null if there
+ * was no such key.
+ */
+ @Override
+ public V remove(Object key) {
+ int index = indexOf(key, key.hashCode());
+ if (index >= 0) {
+ return removeAt(index);
+ }
+
+ return null;
+ }
+
+ /**
+ * Remove the key/value mapping at the given index.
+ * @param index The desired index, must be between 0 and {@link #size()}-1.
+ * @return Returns the value that was stored at this index.
+ */
+ public V removeAt(int index) {
+ final V old = (V)mArray[(index << 1) + 1];
+ if (mSize <= 1) {
+ // Now empty.
+ if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
+ freeArrays(mHashes, mArray, mSize);
+ mHashes = SparseArray.EMPTY_INTS;
+ mArray = SparseArray.EMPTY_OBJECTS;
+ mSize = 0;
+ } else {
+ if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
+ // Shrunk enough to reduce size of arrays. We don't allow it to
+ // shrink smaller than (BASE_SIZE*2) to avoid flapping between
+ // that and BASE_SIZE.
+ final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2);
+
+ if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);
+
+ final int[] ohashes = mHashes;
+ final Object[] oarray = mArray;
+ allocArrays(n);
+
+ mSize--;
+ if (index > 0) {
+ if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
+ System.arraycopy(ohashes, 0, mHashes, 0, index);
+ System.arraycopy(oarray, 0, mArray, 0, index << 1);
+ }
+ if (index < mSize) {
+ if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + mSize
+ + " to " + index);
+ System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
+ System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1,
+ (mSize - index) << 1);
+ }
+ } else {
+ mSize--;
+ if (index < mSize) {
+ if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + mSize
+ + " to " + index);
+ System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
+ System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1,
+ (mSize - index) << 1);
+ }
+ mArray[mSize << 1] = null;
+ mArray[(mSize << 1) + 1] = null;
+ }
+ }
+ return old;
+ }
+
+ /**
+ * Return the number of items in this array map.
+ */
+ @Override
+ public int size() {
+ return mSize;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation returns false if the object is not a map, or
+ * if the maps have different sizes. Otherwise, for each key in this map,
+ * values of both maps are compared. If the values for any key are not
+ * equal, the method returns false, otherwise it returns true.
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object instanceof Map) {
+ Map<?, ?> map = (Map<?, ?>) object;
+ if (size() != map.size()) {
+ return false;
+ }
+
+ try {
+ for (int i=0; i<mSize; i++) {
+ K key = keyAt(i);
+ V mine = valueAt(i);
+ Object theirs = map.get(key);
+ if (mine == null) {
+ if (theirs != null || !map.containsKey(key)) {
+ return false;
+ }
+ } else if (!mine.equals(theirs)) {
+ return false;
+ }
+ }
+ } catch (NullPointerException ignored) {
+ return false;
+ } catch (ClassCastException ignored) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int[] hashes = mHashes;
+ final Object[] array = mArray;
+ int result = 0;
+ for (int i = 0, v = 1, s = mSize; i < s; i++, v+=2) {
+ Object value = array[v];
+ result += hashes[i] ^ (value == null ? 0 : value.hashCode());
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation composes a string by iterating over its mappings. If
+ * this map contains itself as a key or a value, the string "(this Map)"
+ * will appear in its place.
+ */
+ @Override
+ public String toString() {
+ if (isEmpty()) {
+ return "{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(mSize * 28);
+ buffer.append('{');
+ for (int i=0; i<mSize; i++) {
+ if (i > 0) {
+ buffer.append(", ");
+ }
+ Object key = keyAt(i);
+ if (key != this) {
+ buffer.append(key);
+ } else {
+ buffer.append("(this Map)");
+ }
+ buffer.append('=');
+ Object value = valueAt(i);
+ if (value != this) {
+ buffer.append(value);
+ } else {
+ buffer.append("(this Map)");
+ }
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
+
+ // ------------------------------------------------------------------------
+ // Interop with traditional Java containers. Not as efficient as using
+ // specialized collection APIs.
+ // ------------------------------------------------------------------------
+
+ private MapCollections<K, V> getCollection() {
+ if (mCollections == null) {
+ mCollections = new MapCollections<K, V>() {
+ @Override
+ protected int colGetSize() {
+ return mSize;
+ }
+
+ @Override
+ protected Object colGetEntry(int index, int offset) {
+ return mArray[(index<<1) + offset];
+ }
+
+ @Override
+ protected int colIndexOfKey(Object key) {
+ return indexOf(key, key.hashCode());
+ }
+
+ @Override
+ protected int colIndexOfValue(Object value) {
+ return indexOfValue(value);
+ }
+
+ @Override
+ protected Map<K, V> colGetMap() {
+ return ArrayMap.this;
+ }
+
+ @Override
+ protected void colPut(K key, V value) {
+ put(key, value);
+ }
+
+ @Override
+ protected V colSetValue(int index, V value) {
+ return setValueAt(index, value);
+ }
+
+ @Override
+ protected void colRemoveAt(int index) {
+ removeAt(index);
+ }
+
+ @Override
+ protected void colClear() {
+ clear();
+ }
+ };
+ }
+ return mCollections;
+ }
+
+ /**
+ * Determine if the array map contains all of the keys in the given collection.
+ * @param collection The collection whose contents are to be checked against.
+ * @return Returns true if this array map contains a key for every entry
+ * in <var>collection</var>, else returns false.
+ */
+ public boolean containsAll(Collection<?> collection) {
+ return MapCollections.containsAllHelper(this, collection);
+ }
+
+ /**
+ * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>map</var>
+ * @param map The map whose contents are to be retrieved.
+ */
+ @Override
+ public void putAll(Map<? extends K, ? extends V> map) {
+ ensureCapacity(mSize + map.size());
+ for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Remove all keys in the array map that exist in the given collection.
+ * @param collection The collection whose contents are to be used to remove keys.
+ * @return Returns true if any keys were removed from the array map, else false.
+ */
+ public boolean removeAll(Collection<?> collection) {
+ return MapCollections.removeAllHelper(this, collection);
+ }
+
+ /**
+ * Remove all keys in the array map that do <b>not</b> exist in the given collection.
+ * @param collection The collection whose contents are to be used to determine which
+ * keys to keep.
+ * @return Returns true if any keys were removed from the array map, else false.
+ */
+ public boolean retainAll(Collection<?> collection) {
+ return MapCollections.retainAllHelper(this, collection);
+ }
+
+ /**
+ * Return a {@link java.util.Set} for iterating over and interacting with all mappings
+ * in the array map.
+ *
+ * <p><b>Note:</b> this is a very inefficient way to access the array contents, it
+ * requires generating a number of temporary objects.</p>
+ *
+ * <p><b>Note:</b></p> the semantics of this
+ * Set are subtly different than that of a {@link java.util.HashMap}: most important,
+ * the {@link java.util.Map.Entry Map.Entry} object returned by its iterator is a single
+ * object that exists for the entire iterator, so you can <b>not</b> hold on to it
+ * after calling {@link java.util.Iterator#next() Iterator.next}.</p>
+ */
+ @Override
+ public Set<Map.Entry<K, V>> entrySet() {
+ return getCollection().getEntrySet();
+ }
+
+ /**
+ * Return a {@link java.util.Set} for iterating over and interacting with all keys
+ * in the array map.
+ *
+ * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it
+ * requires generating a number of temporary objects.</p>
+ */
+ @Override
+ public Set<K> keySet() {
+ return getCollection().getKeySet();
+ }
+
+ /**
+ * Return a {@link java.util.Collection} for iterating over and interacting with all values
+ * in the array map.
+ *
+ * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it
+ * requires generating a number of temporary objects.</p>
+ */
+ @Override
+ public Collection<V> values() {
+ return getCollection().getValues();
+ }
+}
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
new file mode 100644
index 0000000..2d3380e
--- /dev/null
+++ b/core/java/android/util/ArraySet.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ArraySet is a generic set data structure that is designed to be more memory efficient than a
+ * traditional {@link java.util.HashSet}. The design is very similar to
+ * {@link ArrayMap}, with all of the caveats described there. This implementation is
+ * separate from ArrayMap, however, so the Object array contains only one item for each
+ * entry in the set (instead of a pair for a mapping).
+ *
+ * <p>Note that this implementation is not intended to be appropriate for data structures
+ * that may contain large numbers of items. It is generally slower than a traditional
+ * HashSet, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array. For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%. For larger numbers of items
+ * this data structure should be avoided.</p>
+ *
+ * <p><b>Note:</b> unlike {@link java.util.HashSet}, this container does not support
+ * null values.</p>
+ *
+ * <p>Because this container is intended to better balance memory use, unlike most other
+ * standard Java containers it will shrink its array as items are removed from it. Currently
+ * you have no control over this shrinking -- if you set a capacity and then remove an
+ * item, it may reduce the capacity to better match the current size. In the future an
+ * explicitly call to set the capacity should turn off this aggressive shrinking behavior.</p>
+ *
+ * @hide
+ */
+public final class ArraySet<E> implements Collection<E>, Set<E> {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "ArraySet";
+
+ /**
+ * The minimum amount by which the capacity of a ArraySet will increase.
+ * This is tuned to be relatively space-efficient.
+ */
+ private static final int BASE_SIZE = 4;
+
+ /**
+ * Maximum number of entries to have in array caches.
+ */
+ private static final int CACHE_SIZE = 10;
+
+ /**
+ * Caches of small array objects to avoid spamming garbage. The cache
+ * Object[] variable is a pointer to a linked list of array objects.
+ * The first entry in the array is a pointer to the next array in the
+ * list; the second entry is a pointer to the int[] hash code array for it.
+ */
+ static Object[] mBaseCache;
+ static int mBaseCacheSize;
+ static Object[] mTwiceBaseCache;
+ static int mTwiceBaseCacheSize;
+
+ int[] mHashes;
+ Object[] mArray;
+ int mSize;
+ MapCollections<E, E> mCollections;
+
+ private int indexOf(Object key, int hash) {
+ final int N = mSize;
+
+ // Important fast case: if nothing is in here, nothing to look for.
+ if (N == 0) {
+ return ~0;
+ }
+
+ int index = SparseArray.binarySearch(mHashes, N, hash);
+
+ // If the hash code wasn't found, then we have no entry for this key.
+ if (index < 0) {
+ return index;
+ }
+
+ // If the key at the returned index matches, that's what we want.
+ if (mArray[index].equals(key)) {
+ return index;
+ }
+
+ // Search for a matching key after the index.
+ int end;
+ for (end = index + 1; end < N && mHashes[end] == hash; end++) {
+ if (mArray[end].equals(key)) return end;
+ }
+
+ // Search for a matching key before the index.
+ for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
+ if (mArray[i].equals(key)) return i;
+ }
+
+ // Key not found -- return negative value indicating where a
+ // new entry for this key should go. We use the end of the
+ // hash chain to reduce the number of array entries that will
+ // need to be copied when inserting.
+ return ~end;
+ }
+
+ private void allocArrays(final int size) {
+ if (size == (BASE_SIZE*2)) {
+ synchronized (ArraySet.class) {
+ if (mTwiceBaseCache != null) {
+ final Object[] array = mTwiceBaseCache;
+ mArray = array;
+ mTwiceBaseCache = (Object[])array[0];
+ mHashes = (int[])array[1];
+ array[0] = array[1] = null;
+ mTwiceBaseCacheSize--;
+ if (DEBUG) Log.d(TAG, "Retrieving 2x cache " + mHashes
+ + " now have " + mTwiceBaseCacheSize + " entries");
+ return;
+ }
+ }
+ } else if (size == BASE_SIZE) {
+ synchronized (ArraySet.class) {
+ if (mBaseCache != null) {
+ final Object[] array = mBaseCache;
+ mArray = array;
+ mBaseCache = (Object[])array[0];
+ mHashes = (int[])array[1];
+ array[0] = array[1] = null;
+ mBaseCacheSize--;
+ if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes
+ + " now have " + mBaseCacheSize + " entries");
+ return;
+ }
+ }
+ }
+
+ mHashes = new int[size];
+ mArray = new Object[size];
+ }
+
+ private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
+ if (hashes.length == (BASE_SIZE*2)) {
+ synchronized (ArraySet.class) {
+ if (mTwiceBaseCacheSize < CACHE_SIZE) {
+ array[0] = mTwiceBaseCache;
+ array[1] = hashes;
+ for (int i=size-1; i>=2; i--) {
+ array[i] = null;
+ }
+ mTwiceBaseCache = array;
+ mTwiceBaseCacheSize++;
+ if (DEBUG) Log.d(TAG, "Storing 2x cache " + array
+ + " now have " + mTwiceBaseCacheSize + " entries");
+ }
+ }
+ } else if (hashes.length == BASE_SIZE) {
+ synchronized (ArraySet.class) {
+ if (mBaseCacheSize < CACHE_SIZE) {
+ array[0] = mBaseCache;
+ array[1] = hashes;
+ for (int i=size-1; i>=2; i--) {
+ array[i] = null;
+ }
+ mBaseCache = array;
+ mBaseCacheSize++;
+ if (DEBUG) Log.d(TAG, "Storing 1x cache " + array
+ + " now have " + mBaseCacheSize + " entries");
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a new empty ArraySet. The default capacity of an array map is 0, and
+ * will grow once items are added to it.
+ */
+ public ArraySet() {
+ mHashes = SparseArray.EMPTY_INTS;
+ mArray = SparseArray.EMPTY_OBJECTS;
+ mSize = 0;
+ }
+
+ /**
+ * Create a new ArraySet with a given initial capacity.
+ */
+ public ArraySet(int capacity) {
+ if (capacity == 0) {
+ mHashes = SparseArray.EMPTY_INTS;
+ mArray = SparseArray.EMPTY_OBJECTS;
+ } else {
+ allocArrays(capacity);
+ }
+ mSize = 0;
+ }
+
+ /**
+ * Create a new ArraySet with the mappings from the given ArraySet.
+ */
+ public ArraySet(ArraySet set) {
+ this();
+ if (set != null) {
+ addAll(set);
+ }
+ }
+
+
+ /**
+ * Make the array map empty. All storage is released.
+ */
+ @Override
+ public void clear() {
+ if (mSize != 0) {
+ freeArrays(mHashes, mArray, mSize);
+ mHashes = SparseArray.EMPTY_INTS;
+ mArray = SparseArray.EMPTY_OBJECTS;
+ mSize = 0;
+ }
+ }
+
+ /**
+ * Ensure the array map can hold at least <var>minimumCapacity</var>
+ * items.
+ */
+ public void ensureCapacity(int minimumCapacity) {
+ if (mHashes.length < minimumCapacity) {
+ final int[] ohashes = mHashes;
+ final Object[] oarray = mArray;
+ allocArrays(minimumCapacity);
+ if (mSize > 0) {
+ System.arraycopy(ohashes, 0, mHashes, 0, mSize);
+ System.arraycopy(oarray, 0, mArray, 0, mSize);
+ }
+ freeArrays(ohashes, oarray, mSize);
+ }
+ }
+
+ /**
+ * Check whether a value exists in the set.
+ *
+ * @param key The value to search for.
+ * @return Returns true if the value exists, else false.
+ */
+ @Override
+ public boolean contains(Object key) {
+ return indexOf(key, key.hashCode()) >= 0;
+ }
+
+ /**
+ * Return the value at the given index in the array.
+ * @param index The desired index, must be between 0 and {@link #size()}-1.
+ * @return Returns the value stored at the given index.
+ */
+ public E valueAt(int index) {
+ return (E)mArray[index];
+ }
+
+ /**
+ * Return true if the array map contains no items.
+ */
+ @Override
+ public boolean isEmpty() {
+ return mSize <= 0;
+ }
+
+ /**
+ * Adds the specified object to this set. The set is not modified if it
+ * already contains the object.
+ *
+ * @param value the object to add.
+ * @return {@code true} if this set is modified, {@code false} otherwise.
+ * @throws ClassCastException
+ * when the class of the object is inappropriate for this set.
+ */
+ @Override
+ public boolean add(E value) {
+ final int hash = value.hashCode();
+ int index = indexOf(value, hash);
+ if (index >= 0) {
+ return false;
+ }
+
+ index = ~index;
+ if (mSize >= mHashes.length) {
+ final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))
+ : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
+
+ if (DEBUG) Log.d(TAG, "add: grow from " + mHashes.length + " to " + n);
+
+ final int[] ohashes = mHashes;
+ final Object[] oarray = mArray;
+ allocArrays(n);
+
+ if (mHashes.length > 0) {
+ if (DEBUG) Log.d(TAG, "add: copy 0-" + mSize + " to 0");
+ System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
+ System.arraycopy(oarray, 0, mArray, 0, oarray.length);
+ }
+
+ freeArrays(ohashes, oarray, mSize);
+ }
+
+ if (index < mSize) {
+ if (DEBUG) Log.d(TAG, "add: move " + index + "-" + (mSize-index)
+ + " to " + (index+1));
+ System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
+ System.arraycopy(mArray, index, mArray, index + 1, mSize - index);
+ }
+
+ mHashes[index] = hash;
+ mArray[index] = value;
+ mSize++;
+ return true;
+ }
+
+ /**
+ * Perform a {@link #add(Object)} of all values in <var>array</var>
+ * @param array The array whose contents are to be retrieved.
+ */
+ public void putAll(ArraySet<? extends E> array) {
+ final int N = array.mSize;
+ ensureCapacity(mSize + N);
+ if (mSize == 0) {
+ if (N > 0) {
+ System.arraycopy(array.mHashes, 0, mHashes, 0, N);
+ System.arraycopy(array.mArray, 0, mArray, 0, N);
+ mSize = N;
+ }
+ } else {
+ for (int i=0; i<N; i++) {
+ add(array.valueAt(i));
+ }
+ }
+ }
+
+ /**
+ * Removes the specified object from this set.
+ *
+ * @param object the object to remove.
+ * @return {@code true} if this set was modified, {@code false} otherwise.
+ */
+ @Override
+ public boolean remove(Object object) {
+ int index = indexOf(object, object.hashCode());
+ if (index >= 0) {
+ removeAt(index);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove the key/value mapping at the given index.
+ * @param index The desired index, must be between 0 and {@link #size()}-1.
+ * @return Returns the value that was stored at this index.
+ */
+ public E removeAt(int index) {
+ final E old = (E)mArray[index];
+ if (mSize <= 1) {
+ // Now empty.
+ if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
+ freeArrays(mHashes, mArray, mSize);
+ mHashes = SparseArray.EMPTY_INTS;
+ mArray = SparseArray.EMPTY_OBJECTS;
+ mSize = 0;
+ } else {
+ if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
+ // Shrunk enough to reduce size of arrays. We don't allow it to
+ // shrink smaller than (BASE_SIZE*2) to avoid flapping between
+ // that and BASE_SIZE.
+ final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2);
+
+ if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);
+
+ final int[] ohashes = mHashes;
+ final Object[] oarray = mArray;
+ allocArrays(n);
+
+ mSize--;
+ if (index > 0) {
+ if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
+ System.arraycopy(ohashes, 0, mHashes, 0, index);
+ System.arraycopy(oarray, 0, mArray, 0, index);
+ }
+ if (index < mSize) {
+ if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + mSize
+ + " to " + index);
+ System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
+ System.arraycopy(oarray, index + 1, mArray, index, mSize - index);
+ }
+ } else {
+ mSize--;
+ if (index < mSize) {
+ if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + mSize
+ + " to " + index);
+ System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
+ System.arraycopy(mArray, index + 1, mArray, index, mSize - index);
+ }
+ mArray[mSize] = null;
+ }
+ }
+ return old;
+ }
+
+ /**
+ * Return the number of items in this array map.
+ */
+ @Override
+ public int size() {
+ return mSize;
+ }
+
+ @Override
+ public Object[] toArray() {
+ Object[] result = new Object[mSize];
+ System.arraycopy(mArray, 0, result, 0, mSize);
+ return result;
+ }
+
+ @Override
+ public <T> T[] toArray(T[] array) {
+ if (array.length < mSize) {
+ @SuppressWarnings("unchecked") T[] newArray
+ = (T[]) Array.newInstance(array.getClass().getComponentType(), mSize);
+ array = newArray;
+ }
+ System.arraycopy(mArray, 0, array, 0, mSize);
+ if (array.length > mSize) {
+ array[mSize] = null;
+ }
+ return array;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation returns false if the object is not a set, or
+ * if the sets have different sizes. Otherwise, for each value in this
+ * set, it checks to make sure the value also exists in the other set.
+ * If any value doesn't exist, the method returns false; otherwise, it
+ * returns true.
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object instanceof Set) {
+ Set<?> set = (Set<?>) object;
+ if (size() != set.size()) {
+ return false;
+ }
+
+ try {
+ for (int i=0; i<mSize; i++) {
+ E mine = valueAt(i);
+ if (!set.contains(mine)) {
+ return false;
+ }
+ }
+ } catch (NullPointerException ignored) {
+ return false;
+ } catch (ClassCastException ignored) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int[] hashes = mHashes;
+ int result = 0;
+ for (int i = 0, s = mSize; i < s; i++) {
+ result += hashes[i];
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation composes a string by iterating over its values. If
+ * this set contains itself as a value, the string "(this Set)"
+ * will appear in its place.
+ */
+ @Override
+ public String toString() {
+ if (isEmpty()) {
+ return "{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(mSize * 14);
+ buffer.append('{');
+ for (int i=0; i<mSize; i++) {
+ if (i > 0) {
+ buffer.append(", ");
+ }
+ Object value = valueAt(i);
+ if (value != this) {
+ buffer.append(value);
+ } else {
+ buffer.append("(this Set)");
+ }
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
+
+ // ------------------------------------------------------------------------
+ // Interop with traditional Java containers. Not as efficient as using
+ // specialized collection APIs.
+ // ------------------------------------------------------------------------
+
+ private MapCollections<E, E> getCollection() {
+ if (mCollections == null) {
+ mCollections = new MapCollections<E, E>() {
+ @Override
+ protected int colGetSize() {
+ return mSize;
+ }
+
+ @Override
+ protected Object colGetEntry(int index, int offset) {
+ return mArray[index];
+ }
+
+ @Override
+ protected int colIndexOfKey(Object key) {
+ return indexOf(key, key.hashCode());
+ }
+
+ @Override
+ protected int colIndexOfValue(Object value) {
+ return indexOf(value, value.hashCode());
+ }
+
+ @Override
+ protected Map<E, E> colGetMap() {
+ throw new UnsupportedOperationException("not a map");
+ }
+
+ @Override
+ protected void colPut(E key, E value) {
+ add(key);
+ }
+
+ @Override
+ protected E colSetValue(int index, E value) {
+ throw new UnsupportedOperationException("not a map");
+ }
+
+ @Override
+ protected void colRemoveAt(int index) {
+ removeAt(index);
+ }
+
+ @Override
+ protected void colClear() {
+ clear();
+ }
+ };
+ }
+ return mCollections;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return getCollection().getKeySet().iterator();
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> collection) {
+ Iterator<?> it = collection.iterator();
+ while (it.hasNext()) {
+ if (!contains(it.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> collection) {
+ ensureCapacity(mSize + collection.size());
+ boolean added = false;
+ for (E value : collection) {
+ added |= add(value);
+ }
+ return added;
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> collection) {
+ boolean removed = false;
+ for (Object value : collection) {
+ removed |= remove(value);
+ }
+ return removed;
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> collection) {
+ boolean removed = false;
+ for (int i=mSize-1; i>=0; i--) {
+ if (!collection.contains(mArray[i])) {
+ removeAt(i);
+ removed = true;
+ }
+ }
+ return removed;
+ }
+}
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 1c3709f..6a6f027 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -17,6 +17,7 @@
package android.util;
import com.android.internal.os.RuntimeInit;
+import com.android.internal.util.FastPrintWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -315,8 +316,9 @@
}
StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
+ PrintWriter pw = new FastPrintWriter(sw, false, 256);
tr.printStackTrace(pw);
+ pw.flush();
return sw.toString();
}
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index 630e5f3..660b743 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -41,13 +41,19 @@
/**
* Creates a new LongSparseArray containing no mappings that will not
* require any additional memory allocation to store the specified
- * number of mappings.
+ * number of mappings. If you supply an initial capacity of 0, the
+ * sparse array will be initialized with a light-weight representation
+ * not requiring any additional array allocations.
*/
public LongSparseArray(int initialCapacity) {
- initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
-
- mKeys = new long[initialCapacity];
- mValues = new Object[initialCapacity];
+ if (initialCapacity == 0) {
+ mKeys = SparseLongArray.EMPTY_LONGS;
+ mValues = SparseArray.EMPTY_OBJECTS;
+ } else {
+ initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
+ mKeys = new long[initialCapacity];
+ mValues = new Object[initialCapacity];
+ }
mSize = 0;
}
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index 34b6126..503295c 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -42,13 +42,19 @@
/**
* Creates a new SparseLongArray containing no mappings that will not
* require any additional memory allocation to store the specified
- * number of mappings.
+ * number of mappings. If you supply an initial capacity of 0, the
+ * sparse array will be initialized with a light-weight representation
+ * not requiring any additional array allocations.
*/
public LongSparseLongArray(int initialCapacity) {
- initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
-
- mKeys = new long[initialCapacity];
- mValues = new long[initialCapacity];
+ if (initialCapacity == 0) {
+ mKeys = SparseLongArray.EMPTY_LONGS;
+ mValues = SparseLongArray.EMPTY_LONGS;
+ } else {
+ initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
+ mKeys = new long[initialCapacity];
+ mValues = new long[initialCapacity];
+ }
mSize = 0;
}
diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java
new file mode 100644
index 0000000..f29fb65
--- /dev/null
+++ b/core/java/android/util/MapCollections.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import libcore.util.Objects;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper for writing standard Java collection interfaces to a data
+ * structure like {@link ArrayMap}.
+ * @hide
+ */
+abstract class MapCollections<K, V> {
+ EntrySet mEntrySet;
+ KeySet mKeySet;
+ ValuesCollection mValues;
+
+ final class ArrayIterator<T> implements Iterator<T> {
+ final int mOffset;
+ int mSize;
+ int mIndex;
+ boolean mCanRemove = false;
+
+ ArrayIterator(int offset) {
+ mOffset = offset;
+ mSize = colGetSize();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return mIndex < mSize;
+ }
+
+ @Override
+ public T next() {
+ Object res = colGetEntry(mIndex, mOffset);
+ mIndex++;
+ mCanRemove = true;
+ return (T)res;
+ }
+
+ @Override
+ public void remove() {
+ if (!mCanRemove) {
+ throw new IllegalStateException();
+ }
+ mIndex--;
+ mSize--;
+ mCanRemove = false;
+ colRemoveAt(mIndex);
+ }
+ }
+
+ final class MapIterator implements Iterator<Map.Entry<K, V>>, Map.Entry<K, V> {
+ int mEnd;
+ int mIndex;
+ boolean mEntryValid = false;
+
+ MapIterator() {
+ mEnd = colGetSize() - 1;
+ mIndex = -1;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return mIndex < mEnd;
+ }
+
+ @Override
+ public Map.Entry<K, V> next() {
+ mIndex++;
+ mEntryValid = true;
+ return this;
+ }
+
+ @Override
+ public void remove() {
+ if (!mEntryValid) {
+ throw new IllegalStateException();
+ }
+ mIndex--;
+ mEnd--;
+ mEntryValid = false;
+ colRemoveAt(mIndex);
+ }
+
+ @Override
+ public K getKey() {
+ if (!mEntryValid) {
+ throw new IllegalStateException(
+ "This container does not support retaining Map.Entry objects");
+ }
+ return (K)colGetEntry(mIndex, 0);
+ }
+
+ @Override
+ public V getValue() {
+ if (!mEntryValid) {
+ throw new IllegalStateException(
+ "This container does not support retaining Map.Entry objects");
+ }
+ return (V)colGetEntry(mIndex, 1);
+ }
+
+ @Override
+ public V setValue(V object) {
+ if (!mEntryValid) {
+ throw new IllegalStateException(
+ "This container does not support retaining Map.Entry objects");
+ }
+ return colSetValue(mIndex, object);
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!mEntryValid) {
+ throw new IllegalStateException(
+ "This container does not support retaining Map.Entry objects");
+ }
+ if (!(o instanceof Map.Entry)) {
+ return false;
+ }
+ Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
+ return Objects.equal(e.getKey(), colGetEntry(mIndex, 0))
+ && Objects.equal(e.getValue(), colGetEntry(mIndex, 1));
+ }
+
+ @Override
+ public final int hashCode() {
+ if (!mEntryValid) {
+ throw new IllegalStateException(
+ "This container does not support retaining Map.Entry objects");
+ }
+ final Object key = colGetEntry(mIndex, 0);
+ final Object value = colGetEntry(mIndex, 1);
+ return (key == null ? 0 : key.hashCode()) ^
+ (value == null ? 0 : value.hashCode());
+ }
+
+ @Override
+ public final String toString() {
+ return getKey() + "=" + getValue();
+ }
+ }
+
+ final class EntrySet implements Set<Map.Entry<K, V>> {
+ @Override
+ public boolean add(Map.Entry<K, V> object) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Map.Entry<K, V>> collection) {
+ int oldSize = colGetSize();
+ for (Map.Entry<K, V> entry : collection) {
+ colPut(entry.getKey(), entry.getValue());
+ }
+ return oldSize != colGetSize();
+ }
+
+ @Override
+ public void clear() {
+ colClear();
+ }
+
+ @Override
+ public boolean contains(Object object) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> collection) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return colGetSize() == 0;
+ }
+
+ @Override
+ public Iterator<Map.Entry<K, V>> iterator() {
+ return new MapIterator();
+ }
+
+ @Override
+ public boolean remove(Object object) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> collection) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> collection) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int size() {
+ return colGetSize();
+ }
+
+ @Override
+ public Object[] toArray() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T[] toArray(T[] array) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ final class KeySet implements Set<K> {
+
+ @Override
+ public boolean add(K object) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends K> collection) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ colClear();
+ }
+
+ @Override
+ public boolean contains(Object object) {
+ return colIndexOfKey(object) >= 0;
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> collection) {
+ return removeAllHelper(colGetMap(), collection);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return colGetSize() == 0;
+ }
+
+ @Override
+ public Iterator<K> iterator() {
+ return new ArrayIterator<K>(0);
+ }
+
+ @Override
+ public boolean remove(Object object) {
+ int index = colIndexOfKey(object);
+ if (index >= 0) {
+ colRemoveAt(index);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> collection) {
+ return removeAllHelper(colGetMap(), collection);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> collection) {
+ return retainAllHelper(colGetMap(), collection);
+ }
+
+ @Override
+ public int size() {
+ return colGetSize();
+ }
+
+ @Override
+ public Object[] toArray() {
+ return toArrayHelper(1);
+ }
+
+ @Override
+ public <T> T[] toArray(T[] array) {
+ return toArrayHelper(array, 1);
+ }
+ };
+
+ final class ValuesCollection implements Collection<V> {
+
+ @Override
+ public boolean add(V object) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends V> collection) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ colClear();
+ }
+
+ @Override
+ public boolean contains(Object object) {
+ return colIndexOfValue(object) >= 0;
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> collection) {
+ Iterator<?> it = collection.iterator();
+ while (it.hasNext()) {
+ if (!contains(it.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return colGetSize() == 0;
+ }
+
+ @Override
+ public Iterator<V> iterator() {
+ return new ArrayIterator<V>(1);
+ }
+
+ @Override
+ public boolean remove(Object object) {
+ int index = colIndexOfValue(object);
+ if (index >= 0) {
+ colRemoveAt(index);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> collection) {
+ int N = colGetSize();
+ boolean changed = false;
+ for (int i=0; i<N; i++) {
+ Object cur = colGetEntry(i, 1);
+ if (collection.contains(cur)) {
+ colRemoveAt(i);
+ i--;
+ N--;
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> collection) {
+ int N = colGetSize();
+ boolean changed = false;
+ for (int i=0; i<N; i++) {
+ Object cur = colGetEntry(i, 1);
+ if (!collection.contains(cur)) {
+ colRemoveAt(i);
+ i--;
+ N--;
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ public int size() {
+ return colGetSize();
+ }
+
+ @Override
+ public Object[] toArray() {
+ return toArrayHelper(1);
+ }
+
+ @Override
+ public <T> T[] toArray(T[] array) {
+ return toArrayHelper(array, 1);
+ }
+ };
+
+ public static <K, V> boolean containsAllHelper(Map<K, V> map, Collection<?> collection) {
+ Iterator<?> it = collection.iterator();
+ while (it.hasNext()) {
+ if (!map.containsKey(it.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static <K, V> boolean removeAllHelper(Map<K, V> map, Collection<?> collection) {
+ int oldSize = map.size();
+ Iterator<?> it = collection.iterator();
+ while (it.hasNext()) {
+ map.remove(it.next());
+ }
+ return oldSize != map.size();
+ }
+
+ public static <K, V> boolean retainAllHelper(Map<K, V> map, Collection<?> collection) {
+ int oldSize = map.size();
+ Iterator<K> it = map.keySet().iterator();
+ while (it.hasNext()) {
+ if (!collection.contains(it.next())) {
+ it.remove();
+ }
+ }
+ return oldSize != map.size();
+ }
+
+
+ public Object[] toArrayHelper(int offset) {
+ final int N = colGetSize();
+ Object[] result = new Object[N];
+ for (int i=0; i<N; i++) {
+ result[i] = colGetEntry(i, offset);
+ }
+ return result;
+ }
+
+ public <T> T[] toArrayHelper(T[] array, int offset) {
+ final int N = colGetSize();
+ if (array.length < N) {
+ @SuppressWarnings("unchecked") T[] newArray
+ = (T[]) Array.newInstance(array.getClass().getComponentType(), N);
+ array = newArray;
+ }
+ for (int i=0; i<N; i++) {
+ array[i] = (T)colGetEntry(i, offset);
+ }
+ if (array.length > N) {
+ array[N] = null;
+ }
+ return array;
+ }
+
+ public Set<Map.Entry<K, V>> getEntrySet() {
+ if (mEntrySet == null) {
+ mEntrySet = new EntrySet();
+ }
+ return mEntrySet;
+ }
+
+ public Set<K> getKeySet() {
+ if (mKeySet == null) {
+ mKeySet = new KeySet();
+ }
+ return mKeySet;
+ }
+
+ public Collection<V> getValues() {
+ if (mValues == null) {
+ mValues = new ValuesCollection();
+ }
+ return mValues;
+ }
+
+ protected abstract int colGetSize();
+ protected abstract Object colGetEntry(int index, int offset);
+ protected abstract int colIndexOfKey(Object key);
+ protected abstract int colIndexOfValue(Object key);
+ protected abstract Map<K, V> colGetMap();
+ protected abstract void colPut(K key, V value);
+ protected abstract V colSetValue(int index, V value);
+ protected abstract void colRemoveAt(int index);
+ protected abstract void colClear();
+}
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 7e8fee5..001fc5b 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -25,6 +25,8 @@
*/
public class SparseArray<E> implements Cloneable {
private static final Object DELETED = new Object();
+ static final int[] EMPTY_INTS = new int[0];
+ static final Object[] EMPTY_OBJECTS = new Object[0];
private boolean mGarbage = false;
private int[] mKeys;
@@ -41,13 +43,19 @@
/**
* Creates a new SparseArray containing no mappings that will not
* require any additional memory allocation to store the specified
- * number of mappings.
+ * number of mappings. If you supply an initial capacity of 0, the
+ * sparse array will be initialized with a light-weight representation
+ * not requiring any additional array allocations.
*/
public SparseArray(int initialCapacity) {
- initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
-
- mKeys = new int[initialCapacity];
- mValues = new Object[initialCapacity];
+ if (initialCapacity == 0) {
+ mKeys = EMPTY_INTS;
+ mValues = EMPTY_OBJECTS;
+ } else {
+ initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
+ mKeys = new int[initialCapacity];
+ mValues = new Object[initialCapacity];
+ }
mSize = 0;
}
@@ -79,7 +87,7 @@
*/
@SuppressWarnings("unchecked")
public E get(int key, E valueIfKeyNotFound) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = binarySearch(mKeys, mSize, key);
if (i < 0 || mValues[i] == DELETED) {
return valueIfKeyNotFound;
@@ -92,7 +100,7 @@
* Removes the mapping from the specified key, if there was any.
*/
public void delete(int key) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = binarySearch(mKeys, mSize, key);
if (i >= 0) {
if (mValues[i] != DELETED) {
@@ -153,7 +161,7 @@
* was one.
*/
public void put(int key, E value) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = binarySearch(mKeys, mSize, key);
if (i >= 0) {
mValues[i] = value;
@@ -170,7 +178,7 @@
gc();
// Search again because indices may have changed.
- i = ~binarySearch(mKeys, 0, mSize, key);
+ i = ~binarySearch(mKeys, mSize, key);
}
if (mSize >= mKeys.length) {
@@ -261,7 +269,7 @@
gc();
}
- return binarySearch(mKeys, 0, mSize, key);
+ return binarySearch(mKeys, mSize, key);
}
/**
@@ -335,23 +343,23 @@
mSize = pos + 1;
}
- private static int binarySearch(int[] a, int start, int len, int key) {
- int high = start + len, low = start - 1, guess;
+ // This is Arrays.binarySearch(), but doesn't do any argument validation.
+ static int binarySearch(int[] array, int size, int value) {
+ int lo = 0;
+ int hi = size - 1;
- while (high - low > 1) {
- guess = (high + low) / 2;
+ while (lo <= hi) {
+ int mid = (lo + hi) >>> 1;
+ int midVal = array[mid];
- if (a[guess] < key)
- low = guess;
- else
- high = guess;
+ if (midVal < value) {
+ lo = mid + 1;
+ } else if (midVal > value) {
+ hi = mid - 1;
+ } else {
+ return mid; // value found
+ }
}
-
- if (high == start + len)
- return ~(start + len);
- else if (a[high] == key)
- return high;
- else
- return ~high;
+ return ~lo; // value not present
}
}
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index 76c47c6..73e3629 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -25,6 +25,8 @@
* than using a HashMap to map Integers to Booleans.
*/
public class SparseBooleanArray implements Cloneable {
+ static final boolean[] EMPTY_BOOLEANS = new boolean[0];
+
/**
* Creates a new SparseBooleanArray containing no mappings.
*/
@@ -35,13 +37,19 @@
/**
* Creates a new SparseBooleanArray containing no mappings that will not
* require any additional memory allocation to store the specified
- * number of mappings.
+ * number of mappings. If you supply an initial capacity of 0, the
+ * sparse array will be initialized with a light-weight representation
+ * not requiring any additional array allocations.
*/
public SparseBooleanArray(int initialCapacity) {
- initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
-
- mKeys = new int[initialCapacity];
- mValues = new boolean[initialCapacity];
+ if (initialCapacity == 0) {
+ mKeys = SparseArray.EMPTY_INTS;
+ mValues = EMPTY_BOOLEANS;
+ } else {
+ initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
+ mKeys = new int[initialCapacity];
+ mValues = new boolean[initialCapacity];
+ }
mSize = 0;
}
@@ -71,7 +79,7 @@
* if no such mapping has been made.
*/
public boolean get(int key, boolean valueIfKeyNotFound) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = SparseArray.binarySearch(mKeys, mSize, key);
if (i < 0) {
return valueIfKeyNotFound;
@@ -84,7 +92,7 @@
* Removes the mapping from the specified key, if there was any.
*/
public void delete(int key) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = SparseArray.binarySearch(mKeys, mSize, key);
if (i >= 0) {
System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1));
@@ -99,7 +107,7 @@
* was one.
*/
public void put(int key, boolean value) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = SparseArray.binarySearch(mKeys, mSize, key);
if (i >= 0) {
mValues[i] = value;
@@ -164,7 +172,7 @@
* key is not mapped.
*/
public int indexOfKey(int key) {
- return binarySearch(mKeys, 0, mSize, key);
+ return SparseArray.binarySearch(mKeys, mSize, key);
}
/**
@@ -220,26 +228,6 @@
mSize = pos + 1;
}
- private static int binarySearch(int[] a, int start, int len, int key) {
- int high = start + len, low = start - 1, guess;
-
- while (high - low > 1) {
- guess = (high + low) / 2;
-
- if (a[guess] < key)
- low = guess;
- else
- high = guess;
- }
-
- if (high == start + len)
- return ~(start + len);
- else if (a[high] == key)
- return high;
- else
- return ~high;
- }
-
private int[] mKeys;
private boolean[] mValues;
private int mSize;
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index 8d11177..122f7f5 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -24,7 +24,6 @@
* than using a HashMap to map Integers to Integers.
*/
public class SparseIntArray implements Cloneable {
-
private int[] mKeys;
private int[] mValues;
private int mSize;
@@ -39,13 +38,19 @@
/**
* Creates a new SparseIntArray containing no mappings that will not
* require any additional memory allocation to store the specified
- * number of mappings.
+ * number of mappings. If you supply an initial capacity of 0, the
+ * sparse array will be initialized with a light-weight representation
+ * not requiring any additional array allocations.
*/
public SparseIntArray(int initialCapacity) {
- initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
-
- mKeys = new int[initialCapacity];
- mValues = new int[initialCapacity];
+ if (initialCapacity == 0) {
+ mKeys = SparseArray.EMPTY_INTS;
+ mValues = SparseArray.EMPTY_INTS;
+ } else {
+ initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
+ mKeys = new int[initialCapacity];
+ mValues = new int[initialCapacity];
+ }
mSize = 0;
}
@@ -75,7 +80,7 @@
* if no such mapping has been made.
*/
public int get(int key, int valueIfKeyNotFound) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = SparseArray.binarySearch(mKeys, mSize, key);
if (i < 0) {
return valueIfKeyNotFound;
@@ -88,7 +93,7 @@
* Removes the mapping from the specified key, if there was any.
*/
public void delete(int key) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = SparseArray.binarySearch(mKeys, mSize, key);
if (i >= 0) {
removeAt(i);
@@ -110,7 +115,7 @@
* was one.
*/
public void put(int key, int value) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = SparseArray.binarySearch(mKeys, mSize, key);
if (i >= 0) {
mValues[i] = value;
@@ -175,7 +180,7 @@
* key is not mapped.
*/
public int indexOfKey(int key) {
- return binarySearch(mKeys, 0, mSize, key);
+ return SparseArray.binarySearch(mKeys, mSize, key);
}
/**
@@ -230,24 +235,4 @@
mValues[pos] = value;
mSize = pos + 1;
}
-
- private static int binarySearch(int[] a, int start, int len, int key) {
- int high = start + len, low = start - 1, guess;
-
- while (high - low > 1) {
- guess = (high + low) / 2;
-
- if (a[guess] < key)
- low = guess;
- else
- high = guess;
- }
-
- if (high == start + len)
- return ~(start + len);
- else if (a[high] == key)
- return high;
- else
- return ~high;
- }
}
diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java
index 2f7a6fe..c608996 100644
--- a/core/java/android/util/SparseLongArray.java
+++ b/core/java/android/util/SparseLongArray.java
@@ -24,6 +24,7 @@
* than using a HashMap to map Integers to Longs.
*/
public class SparseLongArray implements Cloneable {
+ static final long[] EMPTY_LONGS = new long[0];
private int[] mKeys;
private long[] mValues;
@@ -39,13 +40,19 @@
/**
* Creates a new SparseLongArray containing no mappings that will not
* require any additional memory allocation to store the specified
- * number of mappings.
+ * number of mappings. If you supply an initial capacity of 0, the
+ * sparse array will be initialized with a light-weight representation
+ * not requiring any additional array allocations.
*/
public SparseLongArray(int initialCapacity) {
- initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
-
- mKeys = new int[initialCapacity];
- mValues = new long[initialCapacity];
+ if (initialCapacity == 0) {
+ mKeys = SparseArray.EMPTY_INTS;
+ mValues = EMPTY_LONGS;
+ } else {
+ initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
+ mKeys = new int[initialCapacity];
+ mValues = new long[initialCapacity];
+ }
mSize = 0;
}
@@ -75,7 +82,7 @@
* if no such mapping has been made.
*/
public long get(int key, long valueIfKeyNotFound) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = SparseArray.binarySearch(mKeys, mSize, key);
if (i < 0) {
return valueIfKeyNotFound;
@@ -88,7 +95,7 @@
* Removes the mapping from the specified key, if there was any.
*/
public void delete(int key) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = SparseArray.binarySearch(mKeys, mSize, key);
if (i >= 0) {
removeAt(i);
@@ -110,7 +117,7 @@
* was one.
*/
public void put(int key, long value) {
- int i = binarySearch(mKeys, 0, mSize, key);
+ int i = SparseArray.binarySearch(mKeys, mSize, key);
if (i >= 0) {
mValues[i] = value;
@@ -164,7 +171,7 @@
* key is not mapped.
*/
public int indexOfKey(int key) {
- return binarySearch(mKeys, 0, mSize, key);
+ return SparseArray.binarySearch(mKeys, mSize, key);
}
/**
@@ -222,24 +229,4 @@
mKeys = nkeys;
mValues = nvalues;
}
-
- private static int binarySearch(int[] a, int start, int len, long key) {
- int high = start + len, low = start - 1, guess;
-
- while (high - low > 1) {
- guess = (high + low) / 2;
-
- if (a[guess] < key)
- low = guess;
- else
- high = guess;
- }
-
- if (high == start + len)
- return ~(start + len);
- else if (a[high] == key)
- return high;
- else
- return ~high;
- }
}
diff --git a/core/java/android/util/TimedRemoteCaller.java b/core/java/android/util/TimedRemoteCaller.java
new file mode 100644
index 0000000..abb2b64
--- /dev/null
+++ b/core/java/android/util/TimedRemoteCaller.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.os.SystemClock;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This is a helper class for making an async one way call and
+ * its async one way response response in a sync fashion within
+ * a timeout. The key idea is to call the remote method with a
+ * sequence number and a callback and then starting to wait for
+ * the response. The remote method calls back with the result and
+ * the sequence number. If the response comes within the timeout
+ * and its sequence number is the one sent in the method invocation,
+ * then the call succeeded. If the response does not come within
+ * the timeout then the call failed. Older result received when
+ * waiting for the result are ignored.
+ * <p>
+ * Typical usage is:
+ * </p>
+ * <p><pre><code>
+ * public class MyMethodCaller extends TimeoutRemoteCallHelper<Object> {
+ * // The one way remote method to call.
+ * private final IRemoteInterface mTarget;
+ *
+ * // One way callback invoked when the remote method is done.
+ * private final IRemoteCallback mCallback = new IRemoteCallback.Stub() {
+ * public void onCompleted(Object result, int sequence) {
+ * onRemoteMethodResult(result, sequence);
+ * }
+ * };
+ *
+ * public MyMethodCaller(IRemoteInterface target) {
+ * mTarget = target;
+ * }
+ *
+ * public Object onCallMyMethod(Object arg) throws RemoteException {
+ * final int sequence = onBeforeRemoteCall();
+ * mTarget.myMethod(arg, sequence);
+ * return getResultTimed(sequence);
+ * }
+ * }
+ * </code></pre></p>
+ *
+ * @param <T> The type of the expected result.
+ *
+ * @hide
+ */
+public abstract class TimedRemoteCaller<T> {
+
+ public static final long DEFAULT_CALL_TIMEOUT_MILLIS = 5000;
+
+ private static final int UNDEFINED_SEQUENCE = -1;
+
+ private final Object mLock = new Object();
+
+ private final long mCallTimeoutMillis;
+
+ private int mSequenceCounter;
+
+ private int mReceivedSequence = UNDEFINED_SEQUENCE;
+
+ private int mAwaitedSequence = UNDEFINED_SEQUENCE;
+
+ private T mResult;
+
+ public TimedRemoteCaller(long callTimeoutMillis) {
+ mCallTimeoutMillis = callTimeoutMillis;
+ }
+
+ public final int onBeforeRemoteCall() {
+ synchronized (mLock) {
+ mAwaitedSequence = mSequenceCounter++;
+ return mAwaitedSequence;
+ }
+ }
+
+ public final T getResultTimed(int sequence) throws TimeoutException {
+ synchronized (mLock) {
+ final boolean success = waitForResultTimedLocked(sequence);
+ if (!success) {
+ throw new TimeoutException("No reponse for sequence: " + sequence);
+ }
+ T result = mResult;
+ mResult = null;
+ return result;
+ }
+ }
+
+ public final void onRemoteMethodResult(T result, int sequence) {
+ synchronized (mLock) {
+ if (sequence == mAwaitedSequence) {
+ mReceivedSequence = sequence;
+ mResult = result;
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ private boolean waitForResultTimedLocked(int sequence) {
+ final long startMillis = SystemClock.uptimeMillis();
+ while (true) {
+ try {
+ if (mReceivedSequence == sequence) {
+ return true;
+ }
+ final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+ final long waitMillis = mCallTimeoutMillis - elapsedMillis;
+ if (waitMillis <= 0) {
+ return false;
+ }
+ mLock.wait(waitMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 2d6453e..e835a97 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -406,6 +406,10 @@
if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
break;
}
+ // The focused view not shown, we failed.
+ if (!isShown(host)) {
+ break;
+ }
// If the host has a provider ask this provider to search for the
// focus instead fetching all provider nodes to do the search here.
AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
diff --git a/core/java/android/view/CompatibilityInfoHolder.java b/core/java/android/view/CompatibilityInfoHolder.java
deleted file mode 100644
index fc8d684..0000000
--- a/core/java/android/view/CompatibilityInfoHolder.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.content.res.CompatibilityInfo;
-
-/** @hide */
-public class CompatibilityInfoHolder {
- private volatile CompatibilityInfo mCompatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
-
- public void set(CompatibilityInfo compatInfo) {
- if (compatInfo != null && (compatInfo.isScalingRequired()
- || !compatInfo.supportsScreen())) {
- mCompatInfo = compatInfo;
- } else {
- mCompatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
- }
- }
-
- public CompatibilityInfo get() {
- return mCompatInfo;
- }
-
- public CompatibilityInfo getIfNeeded() {
- CompatibilityInfo ci = mCompatInfo;
- if (ci == null || ci == CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO) {
- return null;
- }
- return ci;
- }
-}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index e6a7950..5f614b1 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -16,10 +16,12 @@
package android.view;
+import android.content.res.CompatibilityInfo;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.Process;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -57,7 +59,9 @@
private final int mFlags;
private final int mType;
private final String mAddress;
- private final CompatibilityInfoHolder mCompatibilityInfo;
+ private final int mOwnerUid;
+ private final String mOwnerPackageName;
+ private final DisplayAdjustments mDisplayAdjustments;
private DisplayInfo mDisplayInfo; // never null
private boolean mIsValid;
@@ -143,6 +147,18 @@
public static final int FLAG_SECURE = 1 << 1;
/**
+ * Display flag: Indicates that the display is private. Only the application that
+ * owns the display can create windows on it.
+ * <p>
+ * This flag is associated with displays that were created using
+ * {@link android.hardware.display.DisplayManager#createPrivateVirtualDisplay}.
+ * </p>
+ *
+ * @see #getFlags
+ */
+ public static final int FLAG_PRIVATE = 1 << 2;
+
+ /**
* Display type: Unknown display type.
* @hide
*/
@@ -173,6 +189,12 @@
public static final int TYPE_OVERLAY = 4;
/**
+ * Display type: Virtual display.
+ * @hide
+ */
+ public static final int TYPE_VIRTUAL = 5;
+
+ /**
* Internal method to create a display.
* Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
* or {@link android.hardware.display.DisplayManager#getDisplay}
@@ -182,11 +204,11 @@
*/
public Display(DisplayManagerGlobal global,
int displayId, DisplayInfo displayInfo /*not null*/,
- CompatibilityInfoHolder compatibilityInfo) {
+ DisplayAdjustments daj) {
mGlobal = global;
mDisplayId = displayId;
mDisplayInfo = displayInfo;
- mCompatibilityInfo = compatibilityInfo;
+ mDisplayAdjustments = new DisplayAdjustments(daj);
mIsValid = true;
// Cache properties that cannot change as long as the display is valid.
@@ -194,6 +216,8 @@
mFlags = displayInfo.flags;
mType = displayInfo.type;
mAddress = displayInfo.address;
+ mOwnerUid = displayInfo.ownerUid;
+ mOwnerPackageName = displayInfo.ownerPackageName;
}
/**
@@ -262,6 +286,7 @@
*
* @see #FLAG_SUPPORTS_PROTECTED_BUFFERS
* @see #FLAG_SECURE
+ * @see #FLAG_PRIVATE
*/
public int getFlags() {
return mFlags;
@@ -277,6 +302,7 @@
* @see #TYPE_HDMI
* @see #TYPE_WIFI
* @see #TYPE_OVERLAY
+ * @see #TYPE_VIRTUAL
* @hide
*/
public int getType() {
@@ -295,13 +321,39 @@
}
/**
- * Gets the compatibility info used by this display instance.
+ * Gets the UID of the application that owns this display, or zero if it is
+ * owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
*
- * @return The compatibility info holder, or null if none is required.
* @hide
*/
- public CompatibilityInfoHolder getCompatibilityInfo() {
- return mCompatibilityInfo;
+ public int getOwnerUid() {
+ return mOwnerUid;
+ }
+
+ /**
+ * Gets the package name of the application that owns this display, or null if it is
+ * owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ *
+ * @hide
+ */
+ public String getOwnerPackageName() {
+ return mOwnerPackageName;
+ }
+
+ /**
+ * Gets the compatibility info used by this display instance.
+ *
+ * @return The display adjustments holder, or null if none is required.
+ * @hide
+ */
+ public DisplayAdjustments getDisplayAdjustments() {
+ return mDisplayAdjustments;
}
/**
@@ -342,7 +394,7 @@
public void getSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(mTempMetrics, mCompatibilityInfo);
+ mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);
outSize.x = mTempMetrics.widthPixels;
outSize.y = mTempMetrics.heightPixels;
}
@@ -357,7 +409,7 @@
public void getRectSize(Rect outSize) {
synchronized (this) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(mTempMetrics, mCompatibilityInfo);
+ mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);
outSize.set(0, 0, mTempMetrics.widthPixels, mTempMetrics.heightPixels);
}
}
@@ -522,7 +574,7 @@
public void getMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(outMetrics, mCompatibilityInfo);
+ mDisplayInfo.getAppMetrics(outMetrics, mDisplayAdjustments);
}
}
@@ -560,10 +612,28 @@
public void getRealMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
- mDisplayInfo.getLogicalMetrics(outMetrics, null);
+ mDisplayInfo.getLogicalMetrics(outMetrics,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
+ mDisplayAdjustments.getActivityToken());
}
}
+ /**
+ * Returns true if the specified UID has access to this display.
+ * @hide
+ */
+ public boolean hasAccess(int uid) {
+ return Display.hasAccess(uid, mFlags, mOwnerUid);
+ }
+
+ /** @hide */
+ public static boolean hasAccess(int uid, int flags, int ownerUid) {
+ return (flags & Display.FLAG_PRIVATE) == 0
+ || uid == ownerUid
+ || uid == Process.SYSTEM_UID
+ || uid == 0;
+ }
+
private void updateDisplayInfoLocked() {
// Note: The display manager caches display info objects on our behalf.
DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId);
@@ -591,7 +661,7 @@
long now = SystemClock.uptimeMillis();
if (now > mLastCachedAppSizeUpdate + CACHED_APP_SIZE_DURATION_MILLIS) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(mTempMetrics, mCompatibilityInfo);
+ mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);
mCachedAppWidthCompat = mTempMetrics.widthPixels;
mCachedAppHeightCompat = mTempMetrics.heightPixels;
mLastCachedAppSizeUpdate = now;
@@ -603,7 +673,7 @@
public String toString() {
synchronized (this) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(mTempMetrics, mCompatibilityInfo);
+ mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);
return "Display id " + mDisplayId + ": " + mDisplayInfo
+ ", " + mTempMetrics + ", isValid=" + mIsValid;
}
@@ -624,6 +694,8 @@
return "WIFI";
case TYPE_OVERLAY:
return "OVERLAY";
+ case TYPE_VIRTUAL:
+ return "VIRTUAL";
default:
return Integer.toString(type);
}
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
new file mode 100644
index 0000000..041d9e0
--- /dev/null
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.res.CompatibilityInfo;
+import android.os.IBinder;
+
+import com.android.internal.util.Objects;
+
+/** @hide */
+public class DisplayAdjustments {
+ public static final boolean DEVELOPMENT_RESOURCES_DEPEND_ON_ACTIVITY_TOKEN = false;
+
+ public static final DisplayAdjustments DEFAULT_DISPLAY_ADJUSTMENTS = new DisplayAdjustments();
+
+ private volatile CompatibilityInfo mCompatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ private volatile IBinder mActivityToken;
+
+ public DisplayAdjustments() {
+ }
+
+ public DisplayAdjustments(IBinder token) {
+ mActivityToken = token;
+ }
+
+ public DisplayAdjustments(DisplayAdjustments daj) {
+ this (daj.getCompatibilityInfo(), daj.getActivityToken());
+ }
+
+ public DisplayAdjustments(CompatibilityInfo compatInfo, IBinder token) {
+ setCompatibilityInfo(compatInfo);
+ mActivityToken = token;
+ }
+
+ public void setCompatibilityInfo(CompatibilityInfo compatInfo) {
+ if (this == DEFAULT_DISPLAY_ADJUSTMENTS) {
+ throw new IllegalArgumentException(
+ "setCompatbilityInfo: Cannot modify DEFAULT_DISPLAY_ADJUSTMENTS");
+ }
+ if (compatInfo != null && (compatInfo.isScalingRequired()
+ || !compatInfo.supportsScreen())) {
+ mCompatInfo = compatInfo;
+ } else {
+ mCompatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ }
+ }
+
+ public CompatibilityInfo getCompatibilityInfo() {
+ return mCompatInfo;
+ }
+
+ public void setActivityToken(IBinder token) {
+ if (this == DEFAULT_DISPLAY_ADJUSTMENTS) {
+ throw new IllegalArgumentException(
+ "setActivityToken: Cannot modify DEFAULT_DISPLAY_ADJUSTMENTS");
+ }
+ mActivityToken = token;
+ }
+
+ public IBinder getActivityToken() {
+ return mActivityToken;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 17;
+ hash = hash * 31 + mCompatInfo.hashCode();
+ if (DEVELOPMENT_RESOURCES_DEPEND_ON_ACTIVITY_TOKEN) {
+ hash = hash * 31 + (mActivityToken == null ? 0 : mActivityToken.hashCode());
+ }
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof DisplayAdjustments)) {
+ return false;
+ }
+ DisplayAdjustments daj = (DisplayAdjustments)o;
+ return Objects.equal(daj.mCompatInfo, mCompatInfo) &&
+ Objects.equal(daj.mActivityToken, mActivityToken);
+ }
+}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 9fcd9b1..9a9c4cd 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -17,8 +17,10 @@
package android.view;
import android.content.res.CompatibilityInfo;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
import android.util.DisplayMetrics;
import libcore.util.Objects;
@@ -177,6 +179,23 @@
*/
public float physicalYDpi;
+ /**
+ * The UID of the application that owns this display, or zero if it is owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ */
+ public int ownerUid;
+
+ /**
+ * The package name of the application that owns this display, or null if it is
+ * owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ */
+ public String ownerPackageName;
+
public static final Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
@@ -228,7 +247,9 @@
&& refreshRate == other.refreshRate
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
- && physicalYDpi == other.physicalYDpi;
+ && physicalYDpi == other.physicalYDpi
+ && ownerUid == other.ownerUid
+ && Objects.equal(ownerPackageName, other.ownerPackageName);
}
@Override
@@ -259,6 +280,8 @@
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
physicalYDpi = other.physicalYDpi;
+ ownerUid = other.ownerUid;
+ ownerPackageName = other.ownerPackageName;
}
public void readFromParcel(Parcel source) {
@@ -284,6 +307,8 @@
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
physicalYDpi = source.readFloat();
+ ownerUid = source.readInt();
+ ownerPackageName = source.readString();
}
@Override
@@ -310,6 +335,8 @@
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
dest.writeFloat(physicalYDpi);
+ dest.writeInt(ownerUid);
+ dest.writeString(ownerPackageName);
}
@Override
@@ -317,12 +344,22 @@
return 0;
}
- public void getAppMetrics(DisplayMetrics outMetrics, CompatibilityInfoHolder cih) {
- getMetricsWithSize(outMetrics, cih, appWidth, appHeight);
+ public void getAppMetrics(DisplayMetrics outMetrics) {
+ getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
- public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfoHolder cih) {
- getMetricsWithSize(outMetrics, cih, logicalWidth, logicalHeight);
+ public void getAppMetrics(DisplayMetrics outMetrics, DisplayAdjustments displayAdjustments) {
+ getMetricsWithSize(outMetrics, displayAdjustments.getCompatibilityInfo(),
+ displayAdjustments.getActivityToken(), appWidth, appHeight);
+ }
+
+ public void getAppMetrics(DisplayMetrics outMetrics, CompatibilityInfo ci, IBinder token) {
+ getMetricsWithSize(outMetrics, ci, token, appWidth, appHeight);
+ }
+
+ public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
+ IBinder token) {
+ getMetricsWithSize(outMetrics, compatInfo, token, logicalWidth, logicalHeight);
}
public int getNaturalWidth() {
@@ -335,8 +372,15 @@
logicalHeight : logicalWidth;
}
- private void getMetricsWithSize(DisplayMetrics outMetrics, CompatibilityInfoHolder cih,
- int width, int height) {
+ /**
+ * Returns true if the specified UID has access to this display.
+ */
+ public boolean hasAccess(int uid) {
+ return Display.hasAccess(uid, flags, ownerUid);
+ }
+
+ private void getMetricsWithSize(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
+ IBinder token, int width, int height) {
outMetrics.densityDpi = outMetrics.noncompatDensityDpi = logicalDensityDpi;
outMetrics.noncompatWidthPixels = outMetrics.widthPixels = width;
outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height;
@@ -347,11 +391,8 @@
outMetrics.xdpi = outMetrics.noncompatXdpi = physicalXDpi;
outMetrics.ydpi = outMetrics.noncompatYdpi = physicalYDpi;
- if (cih != null) {
- CompatibilityInfo ci = cih.getIfNeeded();
- if (ci != null) {
- ci.applyToDisplayMetrics(outMetrics);
- }
+ if (!compatInfo.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
+ compatInfo.applyToDisplayMetrics(outMetrics);
}
}
@@ -402,8 +443,13 @@
sb.append(layerStack);
sb.append(", type ");
sb.append(Display.typeToString(type));
- sb.append(", address ");
- sb.append(address);
+ if (address != null) {
+ sb.append(", address ").append(address);
+ }
+ if (ownerUid != 0 || ownerPackageName != null) {
+ sb.append(", owner ").append(ownerPackageName);
+ sb.append(" (uid ").append(ownerUid).append(")");
+ }
sb.append(flagsToString(flags));
sb.append("}");
return sb.toString();
@@ -417,6 +463,9 @@
if ((flags & Display.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
result.append(", FLAG_SUPPORTS_PROTECTED_BUFFERS");
}
+ if ((flags & Display.FLAG_PRIVATE) != 0) {
+ result.append(", FLAG_PRIVATE");
+ }
return result.toString();
}
}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 2ec9a7d..4adee14 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -21,6 +21,7 @@
import android.graphics.ColorFilter;
import android.graphics.DrawFilter;
import android.graphics.Matrix;
+import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
@@ -58,11 +59,11 @@
private int mWidth;
private int mHeight;
- private final float[] mPoint = new float[2];
- private final float[] mLine = new float[4];
+ private float[] mPoint;
+ private float[] mLine;
- private final Rect mClipBounds = new Rect();
- private final RectF mPathBounds = new RectF();
+ private Rect mClipBounds;
+ private RectF mPathBounds;
private DrawFilter mFilter;
@@ -162,6 +163,16 @@
}
@Override
+ void cancelLayerUpdate(HardwareLayer layer) {
+ nCancelLayerUpdate(mRenderer, ((GLES20RenderLayer) layer).mLayer);
+ }
+
+ @Override
+ void flushLayerUpdates() {
+ nFlushLayerUpdates(mRenderer);
+ }
+
+ @Override
void clearLayerUpdates() {
nClearLayerUpdates(mRenderer);
}
@@ -183,7 +194,9 @@
static native boolean nCopyLayer(int layerId, int bitmap);
private static native void nClearLayerUpdates(int renderer);
+ private static native void nFlushLayerUpdates(int renderer);
private static native void nPushLayerUpdate(int renderer, int layer);
+ private static native void nCancelLayerUpdate(int renderer, int layer);
///////////////////////////////////////////////////////////////////////////
// Canvas management
@@ -273,6 +286,18 @@
private static native int nGetStencilSize();
+ void setCountOverdrawEnabled(boolean enabled) {
+ nSetCountOverdrawEnabled(mRenderer, enabled);
+ }
+
+ static native void nSetCountOverdrawEnabled(int renderer, boolean enabled);
+
+ float getOverdraw() {
+ return nGetOverdraw(mRenderer);
+ }
+
+ static native float nGetOverdraw(int renderer);
+
///////////////////////////////////////////////////////////////////////////
// Functor
///////////////////////////////////////////////////////////////////////////
@@ -314,21 +339,21 @@
*
* @see #flushCaches(int)
*/
- public static final int FLUSH_CACHES_LAYERS = 0;
+ static final int FLUSH_CACHES_LAYERS = 0;
/**
* Must match Caches::FlushMode values
*
* @see #flushCaches(int)
*/
- public static final int FLUSH_CACHES_MODERATE = 1;
+ static final int FLUSH_CACHES_MODERATE = 1;
/**
* Must match Caches::FlushMode values
*
* @see #flushCaches(int)
*/
- public static final int FLUSH_CACHES_FULL = 2;
+ static final int FLUSH_CACHES_FULL = 2;
/**
* Flush caches to reclaim as much memory as possible. The amount of memory
@@ -338,10 +363,8 @@
* {@link #FLUSH_CACHES_FULL}.
*
* @param level Hint about the amount of memory to reclaim
- *
- * @hide
*/
- public static void flushCaches(int level) {
+ static void flushCaches(int level) {
nFlushCaches(level);
}
@@ -353,21 +376,28 @@
*
* @hide
*/
- public static void terminateCaches() {
+ static void terminateCaches() {
nTerminateCaches();
}
private static native void nTerminateCaches();
- /**
- * @hide
- */
- public static void initCaches() {
- nInitCaches();
+ static boolean initCaches() {
+ return nInitCaches();
}
- private static native void nInitCaches();
-
+ private static native boolean nInitCaches();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Atlas
+ ///////////////////////////////////////////////////////////////////////////
+
+ static void initAtlas(GraphicBuffer buffer, int[] map) {
+ nInitAtlas(buffer, map, map.length);
+ }
+
+ private static native void nInitAtlas(GraphicBuffer buffer, int[] map, int count);
+
///////////////////////////////////////////////////////////////////////////
// Display list
///////////////////////////////////////////////////////////////////////////
@@ -419,6 +449,31 @@
private static native void nResume(int renderer);
///////////////////////////////////////////////////////////////////////////
+ // Support
+ ///////////////////////////////////////////////////////////////////////////
+
+ private Rect getInternalClipBounds() {
+ if (mClipBounds == null) mClipBounds = new Rect();
+ return mClipBounds;
+ }
+
+
+ private RectF getPathBounds() {
+ if (mPathBounds == null) mPathBounds = new RectF();
+ return mPathBounds;
+ }
+
+ private float[] getPointStorage() {
+ if (mPoint == null) mPoint = new float[2];
+ return mPoint;
+ }
+
+ private float[] getLineStorage() {
+ if (mLine == null) mLine = new float[4];
+ return mLine;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
// Clipping
///////////////////////////////////////////////////////////////////////////
@@ -506,9 +561,10 @@
@Override
public boolean quickReject(Path path, EdgeType type) {
- path.computeBounds(mPathBounds, true);
- return nQuickReject(mRenderer, mPathBounds.left, mPathBounds.top,
- mPathBounds.right, mPathBounds.bottom);
+ RectF pathBounds = getPathBounds();
+ path.computeBounds(pathBounds, true);
+ return nQuickReject(mRenderer, pathBounds.left, pathBounds.top,
+ pathBounds.right, pathBounds.bottom);
}
@Override
@@ -718,20 +774,36 @@
}
@Override
- public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+ public void drawPatch(NinePatch patch, Rect dst, Paint paint) {
+ Bitmap bitmap = patch.getBitmap();
if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
// Shaders are ignored when drawing patches
int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
try {
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
+ nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
} finally {
if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
}
}
- private static native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks,
+ @Override
+ public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
+ Bitmap bitmap = patch.getBitmap();
+ if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
+ // Shaders are ignored when drawing patches
+ int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
+ try {
+ final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+ nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint);
+ } finally {
+ if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+ }
+ }
+
+ private static native void nDrawPatch(int renderer, int bitmap, byte[] buffer, int chunk,
float left, float top, float right, float bottom, int paint);
@Override
@@ -747,8 +819,8 @@
}
}
- private static native void nDrawBitmap(
- int renderer, int bitmap, byte[] buffer, float left, float top, int paint);
+ private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
+ float left, float top, int paint);
@Override
public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
@@ -764,7 +836,7 @@
}
}
- private static native void nDrawBitmap(int renderer, int bitmap, byte[] buff,
+ private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
int matrix, int paint);
@Override
@@ -890,7 +962,7 @@
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
try {
- final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+ final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
verts, vertOffset, colors, colorOffset, nativePaint);
} finally {
@@ -929,11 +1001,12 @@
@Override
public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
- mLine[0] = startX;
- mLine[1] = startY;
- mLine[2] = stopX;
- mLine[3] = stopY;
- drawLines(mLine, 0, 4, paint);
+ float[] line = getLineStorage();
+ line[0] = startX;
+ line[1] = startY;
+ line[2] = stopX;
+ line[3] = stopY;
+ drawLines(line, 0, 4, paint);
}
@Override
@@ -974,7 +1047,7 @@
@Override
public void drawPaint(Paint paint) {
- final Rect r = mClipBounds;
+ final Rect r = getInternalClipBounds();
nGetClipBounds(mRenderer, r);
drawRect(r.left, r.top, r.right, r.bottom, paint);
}
@@ -1051,9 +1124,10 @@
@Override
public void drawPoint(float x, float y, Paint paint) {
- mPoint[0] = x;
- mPoint[1] = y;
- drawPoints(mPoint, 0, 2, paint);
+ float[] point = getPointStorage();
+ point[0] = x;
+ point[1] = y;
+ drawPoints(point, 0, 2, paint);
}
@Override
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 3272504..8b2a2ef 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -16,7 +16,6 @@
package android.view;
-import android.graphics.Bitmap;
import android.graphics.Matrix;
import java.util.ArrayList;
@@ -25,12 +24,7 @@
* An implementation of display list for OpenGL ES 2.0.
*/
class GLES20DisplayList extends DisplayList {
- // These lists ensure that any Bitmaps and DisplayLists recorded by a DisplayList are kept
- // alive as long as the DisplayList is alive. The Bitmap and DisplayList lists
- // are populated by the GLES20RecordingCanvas during appropriate drawing calls and are
- // cleared at the start of a new drawing frame or when the view is detached from the window.
- final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5);
- final ArrayList<DisplayList> mChildDisplayLists = new ArrayList<DisplayList>();
+ private ArrayList<DisplayList> mChildDisplayLists;
private GLES20RecordingCanvas mCanvas;
private boolean mValid;
@@ -83,8 +77,16 @@
}
mValid = false;
- mBitmaps.clear();
- mChildDisplayLists.clear();
+ clearReferences();
+ }
+
+ void clearReferences() {
+ if (mChildDisplayLists != null) mChildDisplayLists.clear();
+ }
+
+ ArrayList<DisplayList> getChildDisplayLists() {
+ if (mChildDisplayLists == null) mChildDisplayLists = new ArrayList<DisplayList>();
+ return mChildDisplayLists;
}
@Override
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index 7da2451..b6fc38d 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -16,14 +16,7 @@
package android.view;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Shader;
import android.util.Pools.SynchronizedPool;
/**
@@ -62,229 +55,17 @@
}
void start() {
- mDisplayList.mBitmaps.clear();
- mDisplayList.mChildDisplayLists.clear();
+ mDisplayList.clearReferences();
}
int end(int nativeDisplayList) {
return getDisplayList(nativeDisplayList);
}
- private void recordShaderBitmap(Paint paint) {
- if (paint != null) {
- final Shader shader = paint.getShader();
- if (shader instanceof BitmapShader) {
- mDisplayList.mBitmaps.add(((BitmapShader) shader).mBitmap);
- }
- }
- }
-
- @Override
- public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
- super.drawPatch(bitmap, chunks, dst, paint);
- mDisplayList.mBitmaps.add(bitmap);
- // Shaders in the Paint are ignored when drawing a Bitmap
- }
-
- @Override
- public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
- super.drawBitmap(bitmap, left, top, paint);
- mDisplayList.mBitmaps.add(bitmap);
- // Shaders in the Paint are ignored when drawing a Bitmap
- }
-
- @Override
- public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
- super.drawBitmap(bitmap, matrix, paint);
- mDisplayList.mBitmaps.add(bitmap);
- // Shaders in the Paint are ignored when drawing a Bitmap
- }
-
- @Override
- public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
- super.drawBitmap(bitmap, src, dst, paint);
- mDisplayList.mBitmaps.add(bitmap);
- // Shaders in the Paint are ignored when drawing a Bitmap
- }
-
- @Override
- public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
- super.drawBitmap(bitmap, src, dst, paint);
- mDisplayList.mBitmaps.add(bitmap);
- // Shaders in the Paint are ignored when drawing a Bitmap
- }
-
- @Override
- public void drawBitmap(int[] colors, int offset, int stride, float x, float y, int width,
- int height, boolean hasAlpha, Paint paint) {
- super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint);
- // Shaders in the Paint are ignored when drawing a Bitmap
- }
-
- @Override
- public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width,
- int height, boolean hasAlpha, Paint paint) {
- super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint);
- // Shaders in the Paint are ignored when drawing a Bitmap
- }
-
- @Override
- public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
- int vertOffset, int[] colors, int colorOffset, Paint paint) {
- super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset,
- colors, colorOffset, paint);
- mDisplayList.mBitmaps.add(bitmap);
- // Shaders in the Paint are ignored when drawing a Bitmap
- }
-
- @Override
- public void drawCircle(float cx, float cy, float radius, Paint paint) {
- super.drawCircle(cx, cy, radius, paint);
- recordShaderBitmap(paint);
- }
-
@Override
public int drawDisplayList(DisplayList displayList, Rect dirty, int flags) {
int status = super.drawDisplayList(displayList, dirty, flags);
- mDisplayList.mChildDisplayLists.add(displayList);
+ mDisplayList.getChildDisplayLists().add(displayList);
return status;
}
-
- @Override
- public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
- super.drawLine(startX, startY, stopX, stopY, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawLines(float[] pts, int offset, int count, Paint paint) {
- super.drawLines(pts, offset, count, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawLines(float[] pts, Paint paint) {
- super.drawLines(pts, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawOval(RectF oval, Paint paint) {
- super.drawOval(oval, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawPaint(Paint paint) {
- super.drawPaint(paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawPath(Path path, Paint paint) {
- super.drawPath(path, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawPoint(float x, float y, Paint paint) {
- super.drawPoint(x, y, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawPoints(float[] pts, int offset, int count, Paint paint) {
- super.drawPoints(pts, offset, count, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawPoints(float[] pts, Paint paint) {
- super.drawPoints(pts, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
- super.drawPosText(text, index, count, pos, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawPosText(String text, float[] pos, Paint paint) {
- super.drawPosText(text, pos, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawRect(float left, float top, float right, float bottom, Paint paint) {
- super.drawRect(left, top, right, bottom, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
- super.drawRoundRect(rect, rx, ry, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
- super.drawText(text, index, count, x, y, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
- super.drawText(text, start, end, x, y, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawText(String text, int start, int end, float x, float y, Paint paint) {
- super.drawText(text, start, end, x, y, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawText(String text, float x, float y, Paint paint) {
- super.drawText(text, x, y, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset,
- float vOffset, Paint paint) {
- super.drawTextOnPath(text, index, count, path, hOffset, vOffset, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
- super.drawTextOnPath(text, path, hOffset, vOffset, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
- float x, float y, int dir, Paint paint) {
- super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, dir, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawTextRun(CharSequence text, int start, int end, int contextStart,
- int contextEnd, float x, float y, int dir, Paint paint) {
- super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, dir, paint);
- recordShaderBitmap(paint);
- }
-
- @Override
- public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
- float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
- int indexOffset, int indexCount, Paint paint) {
- super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors,
- colorOffset, indices, indexOffset, indexCount, paint);
- recordShaderBitmap(paint);
- }
}
diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java
index 685dc70..68ba77c 100644
--- a/core/java/android/view/GLES20RenderLayer.java
+++ b/core/java/android/view/GLES20RenderLayer.java
@@ -100,12 +100,17 @@
@Override
HardwareCanvas start(Canvas currentCanvas) {
+ return start(currentCanvas, null);
+ }
+
+ @Override
+ HardwareCanvas start(Canvas currentCanvas, Rect dirty) {
if (currentCanvas instanceof GLES20Canvas) {
((GLES20Canvas) currentCanvas).interrupt();
}
HardwareCanvas canvas = getCanvas();
canvas.setViewport(mWidth, mHeight);
- canvas.onPreDraw(null);
+ canvas.onPreDraw(dirty);
return canvas;
}
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index e863e49..a4cc630 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -63,6 +63,11 @@
}
@Override
+ HardwareCanvas start(Canvas currentCanvas, Rect dirty) {
+ return null;
+ }
+
+ @Override
void end(Canvas currentCanvas) {
}
diff --git a/core/java/android/view/GraphicBuffer.aidl b/core/java/android/view/GraphicBuffer.aidl
new file mode 100644
index 0000000..6dc6bed
--- /dev/null
+++ b/core/java/android/view/GraphicBuffer.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+parcelable GraphicBuffer;
diff --git a/core/java/android/view/GraphicBuffer.java b/core/java/android/view/GraphicBuffer.java
new file mode 100644
index 0000000..30c077c
--- /dev/null
+++ b/core/java/android/view/GraphicBuffer.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Simple wrapper for the native GraphicBuffer class.
+ *
+ * @hide
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class GraphicBuffer implements Parcelable {
+ // Note: keep usage flags in sync with GraphicBuffer.h and gralloc.h
+ public static final int USAGE_SW_READ_NEVER = 0x0;
+ public static final int USAGE_SW_READ_RARELY = 0x2;
+ public static final int USAGE_SW_READ_OFTEN = 0x3;
+ public static final int USAGE_SW_READ_MASK = 0xF;
+
+ public static final int USAGE_SW_WRITE_NEVER = 0x0;
+ public static final int USAGE_SW_WRITE_RARELY = 0x20;
+ public static final int USAGE_SW_WRITE_OFTEN = 0x30;
+ public static final int USAGE_SW_WRITE_MASK = 0xF0;
+
+ public static final int USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK | USAGE_SW_WRITE_MASK;
+
+ public static final int USAGE_PROTECTED = 0x4000;
+
+ public static final int USAGE_HW_TEXTURE = 0x100;
+ public static final int USAGE_HW_RENDER = 0x200;
+ public static final int USAGE_HW_2D = 0x400;
+ public static final int USAGE_HW_COMPOSER = 0x800;
+ public static final int USAGE_HW_VIDEO_ENCODER = 0x10000;
+ public static final int USAGE_HW_MASK = 0x71F00;
+
+ private final int mWidth;
+ private final int mHeight;
+ private final int mFormat;
+ private final int mUsage;
+ // Note: do not rename, this field is used by native code
+ private final int mNativeObject;
+
+ // These two fields are only used by lock/unlockCanvas()
+ private Canvas mCanvas;
+ private int mSaveCount;
+
+ // If set to true, this GraphicBuffer instance cannot be used anymore
+ private boolean mDestroyed;
+
+ /**
+ * Creates new <code>GraphicBuffer</code> instance. This method will return null
+ * if the buffer cannot be created.
+ *
+ * @param width The width in pixels of the buffer
+ * @param height The height in pixels of the buffer
+ * @param format The format of each pixel as specified in {@link PixelFormat}
+ * @param usage Hint indicating how the buffer will be used
+ *
+ * @return A <code>GraphicBuffer</code> instance or null
+ */
+ public static GraphicBuffer create(int width, int height, int format, int usage) {
+ int nativeObject = nCreateGraphicBuffer(width, height, format, usage);
+ if (nativeObject != 0) {
+ return new GraphicBuffer(width, height, format, usage, nativeObject);
+ }
+ return null;
+ }
+
+ /**
+ * Private use only. See {@link #create(int, int, int, int)}.
+ */
+ private GraphicBuffer(int width, int height, int format, int usage, int nativeObject) {
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mUsage = usage;
+ mNativeObject = nativeObject;
+ }
+
+ /**
+ * Returns the width of this buffer in pixels.
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Returns the height of this buffer in pixels.
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Returns the pixel format of this buffer. The pixel format must be one of
+ * the formats defined in {@link PixelFormat}.
+ */
+ public int getFormat() {
+ return mFormat;
+ }
+
+ /**
+ * Returns the usage hint set on this buffer.
+ */
+ public int getUsage() {
+ return mUsage;
+ }
+
+ /**
+ * <p>Start editing the pixels in the buffer. A null is returned if the buffer
+ * cannot be locked for editing.</p>
+ *
+ * <p>The content of the buffer is preserved between unlockCanvas()
+ * and lockCanvas().</p>
+ *
+ * <p>If this method is called after {@link #destroy()}, the return value will
+ * always be null.</p>
+ *
+ * @return A Canvas used to draw into the buffer, or null.
+ *
+ * @see #lockCanvas(android.graphics.Rect)
+ * @see #unlockCanvasAndPost(android.graphics.Canvas)
+ * @see #isDestroyed()
+ */
+ public Canvas lockCanvas() {
+ return lockCanvas(null);
+ }
+
+ /**
+ * Just like {@link #lockCanvas()} but allows specification of a dirty
+ * rectangle.
+ *
+ * <p>If this method is called after {@link #destroy()}, the return value will
+ * always be null.</p>
+ *
+ * @param dirty Area of the buffer that may be modified.
+
+ * @return A Canvas used to draw into the surface, or null.
+ *
+ * @see #lockCanvas()
+ * @see #unlockCanvasAndPost(android.graphics.Canvas)
+ * @see #isDestroyed()
+ */
+ public Canvas lockCanvas(Rect dirty) {
+ if (mDestroyed) {
+ return null;
+ }
+
+ if (mCanvas == null) {
+ mCanvas = new Canvas();
+ }
+
+ if (nLockCanvas(mNativeObject, mCanvas, dirty)) {
+ mSaveCount = mCanvas.save();
+ return mCanvas;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finish editing pixels in the buffer.
+ *
+ * <p>This method doesn't do anything if {@link #destroy()} was
+ * previously called.</p>
+ *
+ * @param canvas The Canvas previously returned by lockCanvas()
+ *
+ * @see #lockCanvas()
+ * @see #lockCanvas(android.graphics.Rect)
+ * @see #isDestroyed()
+ */
+ public void unlockCanvasAndPost(Canvas canvas) {
+ if (!mDestroyed && mCanvas != null && canvas == mCanvas) {
+ canvas.restoreToCount(mSaveCount);
+ mSaveCount = 0;
+
+ nUnlockCanvasAndPost(mNativeObject, mCanvas);
+ }
+ }
+
+ /**
+ * Destroyes this buffer immediately. Calling this method frees up any
+ * underlying native resources. After calling this method, this buffer
+ * must not be used in any way ({@link #lockCanvas()} must not be called,
+ * etc.)
+ *
+ * @see #isDestroyed()
+ */
+ public void destroy() {
+ if (!mDestroyed) {
+ mDestroyed = true;
+ nDestroyGraphicBuffer(mNativeObject);
+ }
+ }
+
+ /**
+ * Indicates whether this buffer has been destroyed. A destroyed buffer
+ * cannot be used in any way: locking a Canvas will return null, the buffer
+ * cannot be written to a parcel, etc.
+ *
+ * @return True if this <code>GraphicBuffer</code> is in a destroyed state,
+ * false otherwise.
+ *
+ * @see #destroy()
+ */
+ public boolean isDestroyed() {
+ return mDestroyed;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (!mDestroyed) nDestroyGraphicBuffer(mNativeObject);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * <p>Calling this method will throw an <code>IllegalStateException</code> if
+ * {@link #destroy()} has been previously called.</p>
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mDestroyed) {
+ throw new IllegalStateException("This GraphicBuffer has been destroyed and cannot be "
+ + "written to a parcel.");
+ }
+
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ dest.writeInt(mFormat);
+ dest.writeInt(mUsage);
+ nWriteGraphicBufferToParcel(mNativeObject, dest);
+ }
+
+ public static final Parcelable.Creator<GraphicBuffer> CREATOR =
+ new Parcelable.Creator<GraphicBuffer>() {
+ public GraphicBuffer createFromParcel(Parcel in) {
+ int width = in.readInt();
+ int height = in.readInt();
+ int format = in.readInt();
+ int usage = in.readInt();
+ int nativeObject = nReadGraphicBufferFromParcel(in);
+ if (nativeObject != 0) {
+ return new GraphicBuffer(width, height, format, usage, nativeObject);
+ }
+ return null;
+ }
+
+ public GraphicBuffer[] newArray(int size) {
+ return new GraphicBuffer[size];
+ }
+ };
+
+ private static native int nCreateGraphicBuffer(int width, int height, int format, int usage);
+ private static native void nDestroyGraphicBuffer(int nativeObject);
+ private static native void nWriteGraphicBufferToParcel(int nativeObject, Parcel dest);
+ private static native int nReadGraphicBufferFromParcel(Parcel in);
+ private static native boolean nLockCanvas(int nativeObject, Canvas canvas, Rect dirty);
+ private static native boolean nUnlockCanvasAndPost(int nativeObject, Canvas canvas);
+}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 0dfed69..259e1cd 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -202,6 +202,28 @@
abstract void pushLayerUpdate(HardwareLayer layer);
/**
+ * Cancels a queued layer update. If the specified layer was not
+ * queued for update, this method has no effect.
+ *
+ * @param layer The layer whose update to cancel
+ *
+ * @see #pushLayerUpdate(HardwareLayer)
+ * @see #clearLayerUpdates()
+ *
+ * @hide
+ */
+ abstract void cancelLayerUpdate(HardwareLayer layer);
+
+ /**
+ * Immediately executes all enqueued layer updates.
+ *
+ * @see #pushLayerUpdate(HardwareLayer)
+ *
+ * @hide
+ */
+ abstract void flushLayerUpdates();
+
+ /**
* Removes all enqueued layer updates.
*
* @see #pushLayerUpdate(HardwareLayer)
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 18b838b..23383d9 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -158,14 +158,22 @@
/**
* This must be invoked before drawing onto this layer.
*
- * @param currentCanvas
+ * @param currentCanvas The canvas whose rendering needs to be interrupted
*/
abstract HardwareCanvas start(Canvas currentCanvas);
/**
+ * This must be invoked before drawing onto this layer.
+ *
+ * @param dirty The dirty area to repaint
+ * @param currentCanvas The canvas whose rendering needs to be interrupted
+ */
+ abstract HardwareCanvas start(Canvas currentCanvas, Rect dirty);
+
+ /**
* This must be invoked after drawing onto this layer.
*
- * @param currentCanvas
+ * @param currentCanvas The canvas whose rendering needs to be resumed
*/
abstract void end(Canvas currentCanvas);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 8308459..d9e4600 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,6 +17,7 @@
package android.view;
import android.content.ComponentCallbacks2;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
@@ -24,7 +25,10 @@
import android.opengl.GLUtils;
import android.opengl.ManagedEGLContext;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
@@ -42,7 +46,6 @@
import java.io.File;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
import static javax.microedition.khronos.egl.EGL10.*;
@@ -162,15 +165,32 @@
"debug.hwui.show_layers_updates";
/**
- * Turn on to show overdraw level.
+ * Controls overdraw debugging.
*
* Possible values:
- * "true", to enable overdraw debugging
* "false", to disable overdraw debugging
+ * "show", to show overdraw areas on screen
+ * "count", to display an overdraw counter
*
* @hide
*/
- public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";
+ public static final String DEBUG_OVERDRAW_PROPERTY = "debug.hwui.overdraw";
+
+ /**
+ * Value for {@link #DEBUG_OVERDRAW_PROPERTY}. When the property is set to this
+ * value, overdraw will be shown on screen by coloring pixels.
+ *
+ * @hide
+ */
+ public static final String OVERDRAW_PROPERTY_SHOW = "show";
+
+ /**
+ * Value for {@link #DEBUG_OVERDRAW_PROPERTY}. When the property is set to this
+ * value, an overdraw counter will be shown on screen.
+ *
+ * @hide
+ */
+ public static final String OVERDRAW_PROPERTY_COUNT = "count";
/**
* Turn on to debug non-rectangular clip operations.
@@ -386,6 +406,17 @@
private static native void nBeginFrame(int[] size);
/**
+ * Returns the current system time according to the renderer.
+ * This method is used for debugging only and should not be used
+ * as a clock.
+ */
+ static long getSystemTime() {
+ return nGetSystemTime();
+ }
+
+ private static native long nGetSystemTime();
+
+ /**
* Preserves the back buffer of the current surface after a buffer swap.
* Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current
* surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL
@@ -418,10 +449,30 @@
* as soon as possible.
*
* @param layer The hardware layer that needs an update
+ *
+ * @see #flushLayerUpdates()
+ * @see #cancelLayerUpdate(HardwareLayer)
*/
abstract void pushLayerUpdate(HardwareLayer layer);
/**
+ * Cancels a queued layer update. If the specified layer was not
+ * queued for update, this method has no effect.
+ *
+ * @param layer The layer whose update to cancel
+ *
+ * @see #pushLayerUpdate(HardwareLayer)
+ */
+ abstract void cancelLayerUpdate(HardwareLayer layer);
+
+ /**
+ * Forces all enqueued layer updates to be executed immediately.
+ *
+ * @see #pushLayerUpdate(HardwareLayer)
+ */
+ abstract void flushLayerUpdates();
+
+ /**
* Interface used to receive callbacks whenever a view is drawn by
* a hardware renderer instance.
*/
@@ -762,6 +813,17 @@
private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
private static final int PROFILE_DRAW_DP_PER_MS = 7;
+ private static final String[] VISUALIZERS = {
+ PROFILE_PROPERTY_VISUALIZE_BARS,
+ PROFILE_PROPERTY_VISUALIZE_LINES
+ };
+
+ private static final String[] OVERDRAW = {
+ OVERDRAW_PROPERTY_SHOW,
+ OVERDRAW_PROPERTY_COUNT
+ };
+ private static final int OVERDRAW_TYPE_COUNT = 1;
+
static EGL10 sEgl;
static EGLDisplay sEglDisplay;
static EGLConfig sEglConfig;
@@ -807,7 +869,9 @@
Paint mProfilePaint;
boolean mDebugDirtyRegions;
- boolean mShowOverdraw;
+ int mDebugOverdraw = -1;
+ HardwareLayer mDebugOverdrawLayer;
+ Paint mDebugOverdrawPaint;
final int mGlVersion;
final boolean mTranslucent;
@@ -819,6 +883,8 @@
private final int[] mSurfaceSize = new int[2];
private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
+ private long mDrawDelta = Long.MAX_VALUE;
+
GlRenderer(int glVersion, boolean translucent) {
mGlVersion = glVersion;
mTranslucent = translucent;
@@ -826,18 +892,13 @@
loadSystemProperties(null);
}
- private static final String[] VISUALIZERS = {
- PROFILE_PROPERTY_VISUALIZE_BARS,
- PROFILE_PROPERTY_VISUALIZE_LINES
- };
-
@Override
boolean loadSystemProperties(Surface surface) {
boolean value;
boolean changed = false;
String profiling = SystemProperties.get(PROFILE_PROPERTY);
- int graphType = Arrays.binarySearch(VISUALIZERS, profiling);
+ int graphType = search(VISUALIZERS, profiling);
value = graphType >= 0;
if (graphType != mProfileVisualizerType) {
@@ -894,11 +955,19 @@
}
}
- value = SystemProperties.getBoolean(
- HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY, false);
- if (value != mShowOverdraw) {
+ String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY);
+ int debugOverdraw = search(OVERDRAW, overdraw);
+ if (debugOverdraw != mDebugOverdraw) {
changed = true;
- mShowOverdraw = value;
+ mDebugOverdraw = debugOverdraw;
+
+ if (mDebugOverdraw != OVERDRAW_TYPE_COUNT) {
+ if (mDebugOverdrawLayer != null) {
+ mDebugOverdrawLayer.destroy();
+ mDebugOverdrawLayer = null;
+ mDebugOverdrawPaint = null;
+ }
+ }
}
if (nLoadProperties()) {
@@ -908,6 +977,13 @@
return changed;
}
+ private static int search(String[] values, String value) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].equals(value)) return i;
+ }
+ return -1;
+ }
+
@Override
void dumpGfxInfo(PrintWriter pw) {
if (mProfileEnabled) {
@@ -968,7 +1044,7 @@
if (fallback) {
// we'll try again if it was context lost
setRequested(false);
- Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
+ Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
+ "Switching back to software rendering.");
}
}
@@ -976,7 +1052,7 @@
@Override
boolean initialize(Surface surface) throws Surface.OutOfResourcesException {
if (isRequested() && !isEnabled()) {
- initializeEgl();
+ boolean contextCreated = initializeEgl();
mGl = createEglSurface(surface);
mDestroyed = false;
@@ -991,6 +1067,10 @@
mCanvas.setName(mName);
}
setEnabled(true);
+
+ if (contextCreated) {
+ initAtlas();
+ }
}
return mCanvas != null;
@@ -1010,7 +1090,7 @@
abstract int[] getConfig(boolean dirtyRegions);
- void initializeEgl() {
+ boolean initializeEgl() {
synchronized (sEglLock) {
if (sEgl == null && sEglConfig == null) {
sEgl = (EGL10) EGLContext.getEGL();
@@ -1043,7 +1123,10 @@
if (mEglContext == null) {
mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
sEglContextStorage.set(createManagedContext(mEglContext));
+ return true;
}
+
+ return false;
}
private EGLConfig loadEglConfig() {
@@ -1181,6 +1264,7 @@
}
abstract void initCaches();
+ abstract void initAtlas();
EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
@@ -1193,6 +1277,7 @@
"Could not create an EGL context. eglCreateContext failed with error: " +
GLUtils.getEGLErrorString(sEgl.eglGetError()));
}
+
return context;
}
@@ -1216,7 +1301,10 @@
void destroySurface() {
if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ if (mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
+ sEgl.eglMakeCurrent(sEglDisplay,
+ EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ }
sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
mEglSurface = null;
}
@@ -1272,7 +1360,7 @@
@Override
boolean validate() {
- return checkCurrent() != SURFACE_STATE_ERROR;
+ return checkRenderContext() != SURFACE_STATE_ERROR;
}
@Override
@@ -1325,8 +1413,7 @@
return;
}
- final int surfaceState = checkCurrent();
- if (surfaceState != SURFACE_STATE_ERROR) {
+ if (checkRenderContext() != SURFACE_STATE_ERROR) {
int status = mCanvas.invokeFunctors(mRedrawClip);
handleFunctorStatus(attachInfo, status);
}
@@ -1345,7 +1432,8 @@
view.mPrivateFlags |= View.PFLAG_DRAWN;
- final int surfaceState = checkCurrent();
+ // We are already on the correct thread
+ final int surfaceState = checkRenderContextUnsafe();
if (surfaceState != SURFACE_STATE_ERROR) {
HardwareCanvas canvas = mCanvas;
attachInfo.mHardwareCanvas = canvas;
@@ -1358,9 +1446,17 @@
DisplayList displayList = buildDisplayList(view, canvas);
+ // buildDisplayList() calls into user code which can cause
+ // an eglMakeCurrent to happen with a different surface/context.
+ // We must therefore check again here.
+ if (checkRenderContextUnsafe() == SURFACE_STATE_ERROR) {
+ return;
+ }
+
int saveCount = 0;
int status = DisplayList.STATUS_DONE;
+ long start = getSystemTime();
try {
status = prepareFrame(dirty);
@@ -1380,10 +1476,15 @@
canvas.restoreToCount(saveCount);
view.mRecreateDisplayList = false;
- mFrameCount++;
+ mDrawDelta = getSystemTime() - start;
- debugDirtyRegions(dirty, canvas);
- drawProfileData(attachInfo);
+ if (mDrawDelta > 0) {
+ mFrameCount++;
+
+ debugOverdraw(attachInfo, dirty, canvas, displayList);
+ debugDirtyRegions(dirty, canvas);
+ drawProfileData(attachInfo);
+ }
}
onPostDraw();
@@ -1399,7 +1500,63 @@
}
}
+ abstract void countOverdraw(HardwareCanvas canvas);
+ abstract float getOverdraw(HardwareCanvas canvas);
+
+ private void debugOverdraw(View.AttachInfo attachInfo, Rect dirty,
+ HardwareCanvas canvas, DisplayList displayList) {
+
+ if (mDebugOverdraw == OVERDRAW_TYPE_COUNT) {
+ if (mDebugOverdrawLayer == null) {
+ mDebugOverdrawLayer = createHardwareLayer(mWidth, mHeight, true);
+ } else if (mDebugOverdrawLayer.getWidth() != mWidth ||
+ mDebugOverdrawLayer.getHeight() != mHeight) {
+ mDebugOverdrawLayer.resize(mWidth, mHeight);
+ }
+
+ if (!mDebugOverdrawLayer.isValid()) {
+ mDebugOverdraw = -1;
+ return;
+ }
+
+ HardwareCanvas layerCanvas = mDebugOverdrawLayer.start(canvas, dirty);
+ countOverdraw(layerCanvas);
+ final int restoreCount = layerCanvas.save();
+ layerCanvas.drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN);
+ layerCanvas.restoreToCount(restoreCount);
+ mDebugOverdrawLayer.end(canvas);
+
+ float overdraw = getOverdraw(layerCanvas);
+ DisplayMetrics metrics = attachInfo.mRootView.getResources().getDisplayMetrics();
+
+ drawOverdrawCounter(canvas, overdraw, metrics.density);
+ }
+ }
+
+ private void drawOverdrawCounter(HardwareCanvas canvas, float overdraw, float density) {
+ final String text = String.format("%.2fx", overdraw);
+ final Paint paint = setupPaint(density);
+ // HSBtoColor will clamp the values in the 0..1 range
+ paint.setColor(Color.HSBtoColor(0.28f - 0.28f * overdraw / 3.5f, 0.8f, 1.0f));
+
+ canvas.drawText(text, density * 4.0f, mHeight - paint.getFontMetrics().bottom, paint);
+ }
+
+ private Paint setupPaint(float density) {
+ if (mDebugOverdrawPaint == null) {
+ mDebugOverdrawPaint = new Paint();
+ mDebugOverdrawPaint.setAntiAlias(true);
+ mDebugOverdrawPaint.setShadowLayer(density * 3.0f, 0.0f, 0.0f, 0xff000000);
+ mDebugOverdrawPaint.setTextSize(density * 20.0f);
+ }
+ return mDebugOverdrawPaint;
+ }
+
private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
+ if (mDrawDelta <= 0) {
+ return view.mDisplayList;
+ }
+
view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
== View.PFLAG_INVALIDATED;
view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
@@ -1572,21 +1729,39 @@
}
/**
- * Ensures the current EGL context is the one we expect.
+ * Ensures the current EGL context and surface are the ones we expect.
+ * This method throws an IllegalStateException if invoked from a thread
+ * that did not initialize EGL.
*
* @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
* {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
* {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
+ *
+ * @see #checkRenderContextUnsafe()
*/
- int checkCurrent() {
+ int checkRenderContext() {
if (mEglThread != Thread.currentThread()) {
throw new IllegalStateException("Hardware acceleration can only be used with a " +
"single UI thread.\nOriginal thread: " + mEglThread + "\n" +
"Current thread: " + Thread.currentThread());
}
- if (!mEglContext.equals(sEgl.eglGetCurrentContext()) ||
- !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
+ return checkRenderContextUnsafe();
+ }
+
+ /**
+ * Ensures the current EGL context and surface are the ones we expect.
+ * This method does not check the current thread.
+ *
+ * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
+ * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
+ * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
+ *
+ * @see #checkRenderContext()
+ */
+ private int checkRenderContextUnsafe() {
+ if (!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW)) ||
+ !mEglContext.equals(sEgl.eglGetCurrentContext())) {
if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
Log.e(LOG_TAG, "eglMakeCurrent failed " +
GLUtils.getEGLErrorString(sEgl.eglGetError()));
@@ -1788,7 +1963,32 @@
@Override
void initCaches() {
- GLES20Canvas.initCaches();
+ if (GLES20Canvas.initCaches()) {
+ // Caches were (re)initialized, rebind atlas
+ initAtlas();
+ }
+ }
+
+ @Override
+ void initAtlas() {
+ IBinder binder = ServiceManager.getService("assetatlas");
+ if (binder == null) return;
+
+ IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
+ try {
+ if (atlas.isCompatible(android.os.Process.myPpid())) {
+ GraphicBuffer buffer = atlas.getBuffer();
+ if (buffer != null) {
+ int[] map = atlas.getMap();
+ if (map != null) {
+ GLES20Canvas.initAtlas(buffer, map);
+ }
+ buffer.destroy();
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Could not acquire atlas", e);
+ }
}
@Override
@@ -1970,6 +2170,16 @@
}
@Override
+ void cancelLayerUpdate(HardwareLayer layer) {
+ mGlCanvas.cancelLayerUpdate(layer);
+ }
+
+ @Override
+ void flushLayerUpdates() {
+ mGlCanvas.flushLayerUpdates();
+ }
+
+ @Override
public DisplayList createDisplayList(String name) {
return new GLES20DisplayList(name);
}
@@ -1985,6 +2195,16 @@
}
@Override
+ void countOverdraw(HardwareCanvas canvas) {
+ ((GLES20Canvas) canvas).setCountOverdrawEnabled(true);
+ }
+
+ @Override
+ float getOverdraw(HardwareCanvas canvas) {
+ return ((GLES20Canvas) canvas).getOverdraw();
+ }
+
+ @Override
public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
return ((GLES20TextureLayer) layer).getSurfaceTexture();
}
@@ -1996,8 +2216,7 @@
@Override
boolean safelyRun(Runnable action) {
- boolean needsContext = true;
- if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false;
+ boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
if (needsContext) {
Gl20RendererEglContext managedContext =
diff --git a/core/java/android/view/IAssetAtlas.aidl b/core/java/android/view/IAssetAtlas.aidl
new file mode 100644
index 0000000..5f1e238
--- /dev/null
+++ b/core/java/android/view/IAssetAtlas.aidl
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.GraphicBuffer;
+
+/**
+ * Programming interface to the system assets atlas. This atlas, when
+ * present, holds preloaded drawable in a single, shareable graphics
+ * buffer. This allows multiple processes to share the same data to
+ * save up on memory.
+ *
+ * @hide
+ */
+interface IAssetAtlas {
+ /**
+ * Indicates whether the atlas is compatible with the specified
+ * parent process id. If the atlas' ppid does not match, this
+ * method will return false.
+ */
+ boolean isCompatible(int ppid);
+
+ /**
+ * Returns the atlas buffer (texture) or null if the atlas is
+ * not available yet.
+ */
+ GraphicBuffer getBuffer();
+
+ /**
+ * Returns the map of the bitmaps stored in the atlas or null
+ * if the atlas is not available yet.
+ *
+ * Each bitmap is represented by several entries in the array:
+ * int0: SkBitmap*, the native bitmap object
+ * int1: x position
+ * int2: y position
+ * int3: rotated, 1 if the bitmap must be rotated, 0 otherwise
+ */
+ int[] getMap();
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8ed4a86..0289407 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -71,17 +71,14 @@
void setOverscan(int displayId, int left, int top, int right, int bottom);
- // Is the device configured to have a full system bar for larger screens?
- boolean hasSystemNavBar();
-
// These can only be called when holding the MANAGE_APP_TOKENS permission.
void pauseKeyDispatching(IBinder token);
void resumeKeyDispatching(IBinder token);
void setEventDispatching(boolean enabled);
void addWindowToken(IBinder token, int type);
void removeWindowToken(IBinder token);
- void addAppToken(int addPos, IApplicationToken token,
- int groupId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked);
+ void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
+ int requestedOrientation, boolean fullscreen, boolean showWhenLocked);
void setAppGroupId(IBinder token, int groupId);
void setAppOrientation(IApplicationToken token, int requestedOrientation);
int getAppOrientation(IApplicationToken token);
@@ -97,15 +94,12 @@
void executeAppTransition();
void setAppStartingWindow(IBinder token, String pkg, int theme,
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
- int icon, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
+ int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
void setAppWillBeHidden(IBinder token);
void setAppVisibility(IBinder token, boolean visible);
void startAppFreezingScreen(IBinder token, int configChanges);
void stopAppFreezingScreen(IBinder token, boolean force);
void removeAppToken(IBinder token);
- void moveAppToken(int index, IBinder token);
- void moveAppTokensToTop(in List<IBinder> tokens);
- void moveAppTokensToBottom(in List<IBinder> tokens);
// Re-evaluate the current orientation from the caller's state.
// If there is a change, the new Configuration is returned and the
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 07a937c..1ecdf30 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -70,6 +70,7 @@
* Gets the source of the event.
*
* @return The event source or {@link InputDevice#SOURCE_UNKNOWN} if unknown.
+ * @see InputDevice#getSources
*/
public abstract int getSource();
diff --git a/core/java/android/view/InputFilter.java b/core/java/android/view/InputFilter.java
index c25b87b..4aba30c 100644
--- a/core/java/android/view/InputFilter.java
+++ b/core/java/android/view/InputFilter.java
@@ -40,7 +40,7 @@
* <li>Input events are then asynchronously delivered to the input filter's
* {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to
* applications as usual. The input filter only receives input events that were
- * generated by input device; the input filter will not receive input events that were
+ * generated by an input device; the input filter will not receive input events that were
* injected into the system by other means, such as by instrumentation.</li>
* <li>The input filter processes and optionally transforms the stream of events. For example,
* it may transform a sequence of motion events representing an accessibility gesture into
@@ -68,7 +68,7 @@
* The input filter must take into account the fact that the input events coming from different
* devices or even different sources all consist of distinct streams of input.
* Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify
- * the source of the event and its semantics. There are be multiple sources of keys,
+ * the source of the event and its semantics. There may be multiple sources of keys,
* touches and other input: they must be kept separate.
* </p>
* <h3>Policy flags</h3>
@@ -88,7 +88,7 @@
* The input filter should clear its internal state about the gesture and then send key or
* motion events to the dispatcher to cancel any keys or pointers that are down.
* </p><p>
- * Corollary: Events that set sent to the dispatcher should usually include the
+ * Corollary: Events that get sent to the dispatcher should usually include the
* {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag. Otherwise, they will be dropped!
* </p><p>
* It may be prudent to disable automatic key repeating for synthetic key events
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index ee36097..db577f3 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -3003,13 +3003,13 @@
}
/**
- * Returns a string that represents the symbolic name of the specified action
+ * Returns a string that represents the symbolic name of the specified unmasked action
* such as "ACTION_DOWN", "ACTION_POINTER_DOWN(3)" or an equivalent numeric constant
* such as "35" if unknown.
*
- * @param action The action.
+ * @param action The unmasked action.
* @return The symbolic name of the specified action.
- * @hide
+ * @see #getAction()
*/
public static String actionToString(int action) {
switch (action) {
@@ -3047,7 +3047,7 @@
* Returns a string that represents the symbolic name of the specified axis
* such as "AXIS_X" or an equivalent numeric constant such as "42" if unknown.
*
- * @param axis The axis
+ * @param axis The axis.
* @return The symbolic name of the specified axis.
*/
public static String axisToString(int axis) {
@@ -3061,7 +3061,7 @@
*
* @param symbolicName The symbolic name of the axis.
* @return The axis or -1 if not found.
- * @see KeyEvent#keycodeToString(int)
+ * @see KeyEvent#keyCodeToString(int)
*/
public static int axisFromString(String symbolicName) {
if (symbolicName == null) {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ae4005b..e0786f7 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -71,8 +71,8 @@
// Guarded state.
final Object mLock = new Object(); // protects the native state
private String mName;
- int mNativeSurface; // package scope only for SurfaceControl access
- private int mGenerationId; // incremented each time mNativeSurface changes
+ int mNativeObject; // package scope only for SurfaceControl access
+ private int mGenerationId; // incremented each time mNativeObject changes
private final Canvas mCanvas = new CompatibleCanvas();
// A matrix to scale the matrix set by application. This is set to null for
@@ -158,8 +158,8 @@
*/
public void release() {
synchronized (mLock) {
- if (mNativeSurface != 0) {
- nativeRelease(mNativeSurface);
+ if (mNativeObject != 0) {
+ nativeRelease(mNativeObject);
setNativeObjectLocked(0);
}
}
@@ -183,8 +183,8 @@
*/
public boolean isValid() {
synchronized (mLock) {
- if (mNativeSurface == 0) return false;
- return nativeIsValid(mNativeSurface);
+ if (mNativeObject == 0) return false;
+ return nativeIsValid(mNativeObject);
}
}
@@ -210,7 +210,7 @@
public boolean isConsumerRunningBehind() {
synchronized (mLock) {
checkNotReleasedLocked();
- return nativeIsConsumerRunningBehind(mNativeSurface);
+ return nativeIsConsumerRunningBehind(mNativeObject);
}
}
@@ -233,7 +233,7 @@
throws OutOfResourcesException, IllegalArgumentException {
synchronized (mLock) {
checkNotReleasedLocked();
- nativeLockCanvas(mNativeSurface, mCanvas, inOutDirty);
+ nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
return mCanvas;
}
}
@@ -252,7 +252,7 @@
synchronized (mLock) {
checkNotReleasedLocked();
- nativeUnlockCanvasAndPost(mNativeSurface, canvas);
+ nativeUnlockCanvasAndPost(mNativeObject, canvas);
}
}
@@ -298,8 +298,8 @@
int newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
synchronized (mLock) {
- if (mNativeSurface != 0) {
- nativeRelease(mNativeSurface);
+ if (mNativeObject != 0) {
+ nativeRelease(mNativeObject);
}
setNativeObjectLocked(newNativeObject);
}
@@ -319,13 +319,13 @@
if (other != this) {
final int newPtr;
synchronized (other.mLock) {
- newPtr = other.mNativeSurface;
+ newPtr = other.mNativeObject;
other.setNativeObjectLocked(0);
}
synchronized (mLock) {
- if (mNativeSurface != 0) {
- nativeRelease(mNativeSurface);
+ if (mNativeObject != 0) {
+ nativeRelease(mNativeObject);
}
setNativeObjectLocked(newPtr);
}
@@ -344,7 +344,7 @@
synchronized (mLock) {
mName = source.readString();
- setNativeObjectLocked(nativeReadFromParcel(mNativeSurface, source));
+ setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
}
}
@@ -355,7 +355,7 @@
}
synchronized (mLock) {
dest.writeString(mName);
- nativeWriteToParcel(mNativeSurface, dest);
+ nativeWriteToParcel(mNativeObject, dest);
}
if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
release();
@@ -370,19 +370,19 @@
}
private void setNativeObjectLocked(int ptr) {
- if (mNativeSurface != ptr) {
- if (mNativeSurface == 0 && ptr != 0) {
+ if (mNativeObject != ptr) {
+ if (mNativeObject == 0 && ptr != 0) {
mCloseGuard.open("release");
- } else if (mNativeSurface != 0 && ptr == 0) {
+ } else if (mNativeObject != 0 && ptr == 0) {
mCloseGuard.close();
}
- mNativeSurface = ptr;
+ mNativeObject = ptr;
mGenerationId += 1;
}
}
private void checkNotReleasedLocked() {
- if (mNativeSurface == 0) {
+ if (mNativeObject == 0) {
throw new IllegalStateException("Surface has already been released.");
}
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index c6da84f..6b530ef 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -499,7 +499,7 @@
if (surface != null) {
synchronized (surface.mLock) {
- nativeSetDisplaySurface(displayToken, surface.mNativeSurface);
+ nativeSetDisplaySurface(displayToken, surface.mNativeObject);
}
} else {
nativeSetDisplaySurface(displayToken, 0);
diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java
index 015a78e..99fa2a4 100644
--- a/core/java/android/view/SurfaceHolder.java
+++ b/core/java/android/view/SurfaceHolder.java
@@ -277,9 +277,6 @@
*
* <p>This method is intended to be used by frameworks which often need
* direct access to the Surface object (usually to pass it to native code).
- * When designing APIs always use SurfaceHolder to pass surfaces around
- * as opposed to the Surface object itself. A rule of thumb is that
- * application code should never have to call this method.
*
* @return Surface The surface.
*/
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 793fb5e..8b2b556 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -160,7 +160,6 @@
int mHeight = -1;
int mFormat = -1;
final Rect mSurfaceFrame = new Rect();
- Rect mTmpDirty;
int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
boolean mUpdateWindowNeeded;
boolean mReportDrawNeeded;
@@ -795,14 +794,6 @@
Canvas c = null;
if (!mDrawingStopped && mWindow != null) {
- if (dirty == null) {
- if (mTmpDirty == null) {
- mTmpDirty = new Rect();
- }
- mTmpDirty.set(mSurfaceFrame);
- dirty = mTmpDirty;
- }
-
try {
c = mSurface.lockCanvas(dirty);
} catch (Exception e) {
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 244dc33..52ad76d 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -656,13 +656,19 @@
* rectangle. Every pixel within that rectangle must be written; however
* pixels outside the dirty rectangle will be preserved by the next call
* to lockCanvas().
+ *
+ * This method can return null if the underlying surface texture is not
+ * available (see {@link #isAvailable()} or if the surface texture is
+ * already connected to an image producer (for instance: the camera,
+ * OpenGL, a media player, etc.)
*
* @param dirty Area of the surface that will be modified.
* @return A Canvas used to draw into the surface.
*
* @see #lockCanvas()
- * @see #unlockCanvasAndPost(android.graphics.Canvas)
+ * @see #unlockCanvasAndPost(android.graphics.Canvas)
+ * @see #isAvailable()
*/
public Canvas lockCanvas(Rect dirty) {
if (!isAvailable()) return null;
@@ -672,7 +678,9 @@
}
synchronized (mNativeWindowLock) {
- nLockCanvas(mNativeWindow, mCanvas, dirty);
+ if (!nLockCanvas(mNativeWindow, mCanvas, dirty)) {
+ return null;
+ }
}
mSaveCount = mCanvas.save();
@@ -811,6 +819,6 @@
private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
int width, int height);
- private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty);
+ private static native boolean nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty);
private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 50638aa..1192390 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,7 +40,6 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -73,6 +72,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.view.transition.Scene;
import android.widget.ScrollBarDrawable;
import static android.os.Build.VERSION_CODES.*;
@@ -1563,6 +1563,8 @@
private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
+ SendViewStateChangedAccessibilityEvent mSendViewStateChangedAccessibilityEvent;
+
/**
* The view's tag.
* {@hide}
@@ -1572,6 +1574,8 @@
*/
protected Object mTag;
+ private Scene mCurrentScene = null;
+
// for mPrivateFlags:
/** {@hide} */
static final int PFLAG_WANTS_FOCUS = 0x00000001;
@@ -2133,13 +2137,12 @@
/**
* Flag indicating whether a view has accessibility focus.
*/
- static final int PFLAG2_ACCESSIBILITY_FOCUSED = 0x00000040 << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+ static final int PFLAG2_ACCESSIBILITY_FOCUSED = 0x04000000;
/**
- * Flag indicating whether a view state for accessibility has changed.
+ * Flag whether the accessibility state of the subtree rooted at this view changed.
*/
- static final int PFLAG2_ACCESSIBILITY_STATE_CHANGED = 0x00000080
- << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+ static final int PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED = 0x08000000;
/**
* Flag indicating whether a view failed the quickReject() check in draw(). This condition
@@ -2191,6 +2194,12 @@
*/
static final int PFLAG3_VIEW_IS_ANIMATING_ALPHA = 0x2;
+ /**
+ * Flag indicating that the view has been through at least one layout since it
+ * was last attached to a window.
+ */
+ static final int PFLAG3_HAS_LAYOUT = 0x4;
+
/* End of masks for mPrivateFlags3 */
@@ -2341,7 +2350,7 @@
* allows it to avoid artifacts when switching in and out of that mode, at
* the expense that some of its user interface may be covered by screen
* decorations when they are shown. You can perform layout of your inner
- * UI elements to account for the navagation system UI through the
+ * UI elements to account for the navigation system UI through the
* {@link #fitSystemWindows(Rect)} method.
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
@@ -2359,6 +2368,15 @@
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
/**
+ * Flag for {@link #setSystemUiVisibility(int)}: View would like to receive touch events
+ * when hiding the status bar with {@link #SYSTEM_UI_FLAG_FULLSCREEN} and/or hiding the
+ * navigation bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} instead of having the system
+ * clear these flags upon interaction. The system may compensate by temporarily overlaying
+ * transparent system ui while also delivering the event.
+ */
+ public static final int SYSTEM_UI_FLAG_ALLOW_OVERLAY = 0x00000800;
+
+ /**
* @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead.
*/
public static final int STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE;
@@ -2479,6 +2497,30 @@
/**
* @hide
+ *
+ * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
+ * out of the public fields to keep the undefined bits out of the developer's way.
+ *
+ * Flag to specify that the status bar should temporarily overlay underlying content
+ * that is otherwise assuming the status bar is hidden. The status bar may
+ * have some degree of transparency while in this temporary overlay mode.
+ */
+ public static final int STATUS_BAR_OVERLAY = 0x04000000;
+
+ /**
+ * @hide
+ *
+ * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
+ * out of the public fields to keep the undefined bits out of the developer's way.
+ *
+ * Flag to specify that the navigation bar should temporarily overlay underlying content
+ * that is otherwise assuming the navigation bar is hidden. The navigation bar mayu
+ * have some degree of transparency while in this temporary overlay mode.
+ */
+ public static final int NAVIGATION_BAR_OVERLAY = 0x08000000;
+
+ /**
+ * @hide
*/
public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x0000FFFF;
@@ -3012,18 +3054,6 @@
private int[] mDrawableState = null;
/**
- * Set to true when drawing cache is enabled and cannot be created.
- *
- * @hide
- */
- public boolean mCachingFailed;
-
- private Bitmap mDrawingCache;
- private Bitmap mUnscaledDrawingCache;
- private HardwareLayer mHardwareLayer;
- DisplayList mDisplayList;
-
- /**
* When this view has focus and the next focus is {@link #FOCUS_LEFT},
* the user may specify which view to go to next.
*/
@@ -3215,6 +3245,18 @@
int mLayerType = LAYER_TYPE_NONE;
Paint mLayerPaint;
Rect mLocalDirtyRect;
+ private HardwareLayer mHardwareLayer;
+
+ /**
+ * Set to true when drawing cache is enabled and cannot be created.
+ *
+ * @hide
+ */
+ public boolean mCachingFailed;
+ private Bitmap mDrawingCache;
+ private Bitmap mUnscaledDrawingCache;
+
+ DisplayList mDisplayList;
/**
* Set to true when the view is sending hover accessibility events because it
@@ -3266,8 +3308,8 @@
mUserPaddingStart = UNDEFINED_PADDING;
mUserPaddingEnd = UNDEFINED_PADDING;
- if (!sUseBrokenMakeMeasureSpec && context.getApplicationInfo().targetSdkVersion <=
- Build.VERSION_CODES.JELLY_BEAN_MR1 ) {
+ if (!sUseBrokenMakeMeasureSpec && context != null &&
+ context.getApplicationInfo().targetSdkVersion <= JELLY_BEAN_MR1) {
// Older apps may need this compatibility hack for measurement.
sUseBrokenMakeMeasureSpec = true;
}
@@ -4418,10 +4460,6 @@
onFocusChanged(true, direction, previouslyFocusedRect);
refreshDrawableState();
-
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- notifyAccessibilityStateChanged();
- }
}
}
@@ -4525,10 +4563,6 @@
if (!rootViewRequestFocus()) {
notifyGlobalFocusCleared(this);
}
-
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- notifyAccessibilityStateChanged();
- }
}
}
@@ -4539,11 +4573,8 @@
}
boolean rootViewRequestFocus() {
- View root = getRootView();
- if (root != null) {
- return root.requestFocus();
- }
- return false;
+ final View root = getRootView();
+ return root != null && root.requestFocus();
}
/**
@@ -4560,10 +4591,6 @@
onFocusChanged(false, 0, null);
refreshDrawableState();
-
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- notifyAccessibilityStateChanged();
- }
}
}
@@ -4614,9 +4641,9 @@
*/
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
if (gainFocus) {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
- }
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ } else {
+ notifyViewAccessibilityStateChangedIfNeeded();
}
InputMethodManager imm = InputMethodManager.peekInstance();
@@ -4673,6 +4700,10 @@
* @see AccessibilityDelegate
*/
public void sendAccessibilityEvent(int eventType) {
+ // Excluded views do not send accessibility events.
+ if (!includeForAccessibility()) {
+ return;
+ }
if (mAccessibilityDelegate != null) {
mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
} else {
@@ -4840,7 +4871,6 @@
* Note: Called from the default {@link AccessibilityDelegate}.
*/
void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
-
}
/**
@@ -5331,9 +5361,11 @@
mContentDescription = contentDescription;
final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ } else {
+ notifyViewAccessibilityStateChangedIfNeeded();
}
- notifyAccessibilityStateChanged();
}
/**
@@ -6110,6 +6142,21 @@
}
/**
+ * Returns true if this view is currently attached to a window.
+ */
+ public boolean isAttachedToWindow() {
+ return mAttachInfo != null;
+ }
+
+ /**
+ * Returns true if this view has been through at least one layout since it
+ * was last attached to or detached from a window.
+ */
+ public boolean hasLayout() {
+ return (mPrivateFlags3 & PFLAG3_HAS_LAYOUT) == PFLAG3_HAS_LAYOUT;
+ }
+
+ /**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
@@ -6613,7 +6660,6 @@
}
invalidate();
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
- notifyAccessibilityStateChanged();
return true;
}
return false;
@@ -6676,7 +6722,6 @@
mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
invalidate();
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
- notifyAccessibilityStateChanged();
}
}
@@ -6850,11 +6895,16 @@
* @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
*/
public void setImportantForAccessibility(int mode) {
+ final boolean oldIncludeForAccessibility = includeForAccessibility();
if (mode != getImportantForAccessibility()) {
mPrivateFlags2 &= ~PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
mPrivateFlags2 |= (mode << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT)
& PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
- notifyAccessibilityStateChanged();
+ if (oldIncludeForAccessibility != includeForAccessibility()) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ } else {
+ notifyViewAccessibilityStateChangedIfNeeded();
+ }
}
}
@@ -6926,6 +6976,7 @@
* @hide
*/
public boolean includeForAccessibility() {
+ //noinspection SimplifiableIfStatement
if (mAttachInfo != null) {
return (mAttachInfo.mAccessibilityFetchFlags
& AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
@@ -6961,25 +7012,44 @@
}
/**
- * Notifies accessibility services that some view's important for
- * accessibility state has changed. Note that such notifications
- * are made at most once every
+ * Notifies that the accessibility state of this view changed. The change
+ * is local to this view and does not represent structural changes such
+ * as children and parent. For example, the view became focusable. The
+ * notification is at at most once every
* {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
- * to avoid unnecessary load to the system. Also once a view has
- * made a notifucation this method is a NOP until the notification has
- * been sent to clients.
+ * to avoid unnecessary load to the system. Also once a view has a pending
+ * notifucation this method is a NOP until the notification has been sent.
*
* @hide
- *
- * TODO: Makse sure this method is called for any view state change
- * that is interesting for accessilility purposes.
*/
- public void notifyAccessibilityStateChanged() {
+ public void notifyViewAccessibilityStateChangedIfNeeded() {
if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
return;
}
- if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_STATE_CHANGED) == 0) {
- mPrivateFlags2 |= PFLAG2_ACCESSIBILITY_STATE_CHANGED;
+ if (mSendViewStateChangedAccessibilityEvent == null) {
+ mSendViewStateChangedAccessibilityEvent =
+ new SendViewStateChangedAccessibilityEvent();
+ }
+ mSendViewStateChangedAccessibilityEvent.runOrPost();
+ }
+
+ /**
+ * Notifies that the accessibility state of this view changed. The change
+ * is *not* local to this view and does represent structural changes such
+ * as children and parent. For example, the view size changed. The
+ * notification is at at most once every
+ * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
+ * to avoid unnecessary load to the system. Also once a view has a pending
+ * notifucation this method is a NOP until the notification has been sent.
+ *
+ * @hide
+ */
+ public void notifySubtreeAccessibilityStateChangedIfNeeded() {
+ if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+ return;
+ }
+ if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
+ mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
if (mParent != null) {
mParent.childAccessibilityStateChanged(this);
}
@@ -6987,13 +7057,11 @@
}
/**
- * Reset the state indicating the this view has requested clients
- * interested in its accessibility state to be notified.
- *
- * @hide
+ * Reset the flag indicating the accessibility state of the subtree rooted
+ * at this view changed.
*/
- public void resetAccessibilityStateChanged() {
- mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_STATE_CHANGED;
+ void resetSubtreeAccessibilityStateChanged() {
+ mPrivateFlags2 &= ~PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
}
/**
@@ -7106,7 +7174,7 @@
|| getAccessibilitySelectionEnd() != end)
&& (start == end)) {
setAccessibilitySelection(start, end);
- notifyAccessibilityStateChanged();
+ notifyViewAccessibilityStateChangedIfNeeded();
return true;
}
} break;
@@ -7497,8 +7565,8 @@
* @return True if the event was handled by the view, false otherwise.
*/
protected boolean dispatchHoverEvent(MotionEvent event) {
- //noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
+ //noinspection SimplifiableIfStatement
if (li != null && li.mOnHoverListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnHoverListener.onHover(this, event)) {
@@ -8527,6 +8595,10 @@
* @param mask Constant indicating the bit range that should be changed
*/
void setFlags(int flags, int mask) {
+ final boolean accessibilityEnabled =
+ AccessibilityManager.getInstance(mContext).isEnabled();
+ final boolean oldIncludeForAccessibility = accessibilityEnabled && includeForAccessibility();
+
int old = mViewFlags;
mViewFlags = (mViewFlags & ~mask) | (flags & mask);
@@ -8551,12 +8623,10 @@
*/
if (mParent != null) mParent.focusableViewAvailable(this);
}
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- notifyAccessibilityStateChanged();
- }
}
- if ((flags & VISIBILITY_MASK) == VISIBLE) {
+ final int newVisibility = flags & VISIBILITY_MASK;
+ if (newVisibility == VISIBLE) {
if ((changed & VISIBILITY_MASK) != 0) {
/*
* If this view is becoming visible, invalidate it in case it changed while
@@ -8622,14 +8692,19 @@
}
if ((changed & VISIBILITY_MASK) != 0) {
+ // If the view is invisible, cleanup its display list to free up resources
+ if (newVisibility != VISIBLE) {
+ cleanupDraw();
+ }
+
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onChildVisibilityChanged(this,
- (changed & VISIBILITY_MASK), (flags & VISIBILITY_MASK));
+ (changed & VISIBILITY_MASK), newVisibility);
((View) mParent).invalidate(true);
} else if (mParent != null) {
mParent.invalidateChild(this, null);
}
- dispatchVisibilityChanged(this, (flags & VISIBILITY_MASK));
+ dispatchVisibilityChanged(this, newVisibility);
}
if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {
@@ -8668,10 +8743,18 @@
}
}
- if (AccessibilityManager.getInstance(mContext).isEnabled()
- && ((changed & FOCUSABLE) != 0 || (changed & CLICKABLE) != 0
- || (changed & LONG_CLICKABLE) != 0 || (changed & ENABLED) != 0)) {
- notifyAccessibilityStateChanged();
+ if (accessibilityEnabled) {
+ if ((changed & FOCUSABLE_MASK) != 0 || (changed & VISIBILITY_MASK) != 0
+ || (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0) {
+ if (oldIncludeForAccessibility != includeForAccessibility()) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ } else {
+ notifyViewAccessibilityStateChangedIfNeeded();
+ }
+ }
+ if ((changed & ENABLED_MASK) != 0) {
+ notifyViewAccessibilityStateChangedIfNeeded();
+ }
}
}
@@ -9981,8 +10064,10 @@
* is inside the view, where the area of the view is expanded by the slop factor.
* This method is called while processing touch-move events to determine if the event
* is still within the view.
+ *
+ * @hide
*/
- private boolean pointInView(float localX, float localY, float slop) {
+ public boolean pointInView(float localX, float localY, float slop) {
return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
@@ -11715,9 +11800,13 @@
mPrivateFlags &= ~PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
}
+ mPrivateFlags3 &= ~PFLAG3_HAS_LAYOUT;
+
jumpDrawablesToCurrentState();
clearAccessibilityFocus();
+ resetSubtreeAccessibilityStateChanged();
+
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
@@ -11999,6 +12088,7 @@
*/
protected void onDetachedFromWindow() {
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
+ mPrivateFlags3 &= ~PFLAG3_HAS_LAYOUT;
removeUnsetPressCallback();
removeLongPressCallback();
@@ -12009,6 +12099,13 @@
destroyLayer(false);
+ cleanupDraw();
+
+ mCurrentAnimation = null;
+ mCurrentScene = null;
+ }
+
+ private void cleanupDraw() {
if (mAttachInfo != null) {
if (mDisplayList != null) {
mDisplayList.markDirty();
@@ -12019,10 +12116,9 @@
// Should never happen
clearDisplayList();
}
+ }
- mCurrentAnimation = null;
-
- resetAccessibilityStateChanged();
+ void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
}
/**
@@ -12519,16 +12615,26 @@
public void buildLayer() {
if (mLayerType == LAYER_TYPE_NONE) return;
- if (mAttachInfo == null) {
+ final AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo == null) {
throw new IllegalStateException("This view must be attached to a window first");
}
switch (mLayerType) {
case LAYER_TYPE_HARDWARE:
- if (mAttachInfo.mHardwareRenderer != null &&
- mAttachInfo.mHardwareRenderer.isEnabled() &&
- mAttachInfo.mHardwareRenderer.validate()) {
+ if (attachInfo.mHardwareRenderer != null &&
+ attachInfo.mHardwareRenderer.isEnabled() &&
+ attachInfo.mHardwareRenderer.validate()) {
getHardwareLayer();
+ // TODO: We need a better way to handle this case
+ // If views have registered pre-draw listeners they need
+ // to be notified before we build the layer. Those listeners
+ // may however rely on other events to happen first so we
+ // cannot just invoke them here until they don't cancel the
+ // current frame
+ if (!attachInfo.mTreeObserver.hasOnPreDrawListeners()) {
+ attachInfo.mViewRootImpl.dispatchFlushHardwareLayerUpdates();
+ }
}
break;
case LAYER_TYPE_SOFTWARE:
@@ -12614,6 +12720,8 @@
if (info != null && info.mHardwareRenderer != null &&
info.mHardwareRenderer.isEnabled() &&
(valid || info.mHardwareRenderer.validate())) {
+
+ info.mHardwareRenderer.cancelLayerUpdate(mHardwareLayer);
mHardwareLayer.destroy();
mHardwareLayer = null;
@@ -12640,6 +12748,7 @@
* @hide
*/
protected void destroyHardwareResources() {
+ clearDisplayList();
destroyLayer(true);
}
@@ -12784,8 +12893,7 @@
mRecreateDisplayList = true;
}
if (displayList == null) {
- final String name = getClass().getSimpleName();
- displayList = mAttachInfo.mHardwareRenderer.createDisplayList(name);
+ displayList = mAttachInfo.mHardwareRenderer.createDisplayList(getClass().getName());
// If we're creating a new display list, make sure our parent gets invalidated
// since they will need to recreate their display list to account for this
// new child display list.
@@ -13432,7 +13540,8 @@
onAnimationStart();
}
- boolean more = a.getTransformation(drawingTime, parent.mChildTransformation, 1f);
+ final Transformation t = parent.getChildTransformation();
+ boolean more = a.getTransformation(drawingTime, t, 1f);
if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
if (parent.mInvalidationTransformation == null) {
parent.mInvalidationTransformation = new Transformation();
@@ -13440,7 +13549,7 @@
invalidationTransform = parent.mInvalidationTransformation;
a.getTransformation(drawingTime, invalidationTransform, 1f);
} else {
- invalidationTransform = parent.mChildTransformation;
+ invalidationTransform = t;
}
if (more) {
@@ -13493,17 +13602,15 @@
if (mParent instanceof ViewGroup && (((ViewGroup) mParent).mGroupFlags &
ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
ViewGroup parentVG = (ViewGroup) mParent;
- final boolean hasTransform =
- parentVG.getChildStaticTransformation(this, parentVG.mChildTransformation);
- if (hasTransform) {
- Transformation transform = parentVG.mChildTransformation;
- final int transformType = parentVG.mChildTransformation.getTransformationType();
+ final Transformation t = parentVG.getChildTransformation();
+ if (parentVG.getChildStaticTransformation(this, t)) {
+ final int transformType = t.getTransformationType();
if (transformType != Transformation.TYPE_IDENTITY) {
if ((transformType & Transformation.TYPE_ALPHA) != 0) {
- alpha = transform.getAlpha();
+ alpha = t.getAlpha();
}
if ((transformType & Transformation.TYPE_MATRIX) != 0) {
- displayList.setMatrix(transform.getMatrix());
+ displayList.setMatrix(t.getMatrix());
}
}
}
@@ -13548,7 +13655,7 @@
final int flags = parent.mGroupFlags;
if ((flags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) == ViewGroup.FLAG_CLEAR_TRANSFORMATION) {
- parent.mChildTransformation.clear();
+ parent.getChildTransformation().clear();
parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
@@ -13576,7 +13683,7 @@
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
- transformToApply = parent.mChildTransformation;
+ transformToApply = parent.getChildTransformation();
} else {
if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) ==
PFLAG3_VIEW_IS_ANIMATING_TRANSFORM && mDisplayList != null) {
@@ -13586,12 +13693,11 @@
}
if (!useDisplayListProperties &&
(flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
- final boolean hasTransform =
- parent.getChildStaticTransformation(this, parent.mChildTransformation);
+ final Transformation t = parent.getChildTransformation();
+ final boolean hasTransform = parent.getChildStaticTransformation(this, t);
if (hasTransform) {
- final int transformType = parent.mChildTransformation.getTransformationType();
- transformToApply = transformType != Transformation.TYPE_IDENTITY ?
- parent.mChildTransformation : null;
+ final int transformType = t.getTransformationType();
+ transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
}
}
@@ -14301,6 +14407,7 @@
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
+ mPrivateFlags3 |= PFLAG3_HAS_LAYOUT;
}
/**
@@ -14393,6 +14500,8 @@
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
+
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
return changed;
}
@@ -15151,15 +15260,14 @@
* @param selected true if the view must be selected, false otherwise
*/
public void setSelected(boolean selected) {
+ //noinspection DoubleNegation
if (((mPrivateFlags & PFLAG_SELECTED) != 0) != selected) {
mPrivateFlags = (mPrivateFlags & ~PFLAG_SELECTED) | (selected ? PFLAG_SELECTED : 0);
if (!selected) resetPressedState();
invalidate(true);
refreshDrawableState();
dispatchSetSelected(selected);
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- notifyAccessibilityStateChanged();
- }
+ notifyViewAccessibilityStateChangedIfNeeded();
}
}
@@ -15197,6 +15305,7 @@
* @param activated true if the view must be activated, false otherwise
*/
public void setActivated(boolean activated) {
+ //noinspection DoubleNegation
if (((mPrivateFlags & PFLAG_ACTIVATED) != 0) != activated) {
mPrivateFlags = (mPrivateFlags & ~PFLAG_ACTIVATED) | (activated ? PFLAG_ACTIVATED : 0);
invalidate(true);
@@ -15637,7 +15746,7 @@
private void setKeyedTag(int key, Object tag) {
if (mKeyedTags == null) {
- mKeyedTags = new SparseArray<Object>();
+ mKeyedTags = new SparseArray<Object>(2);
}
mKeyedTags.put(key, tag);
@@ -16706,8 +16815,8 @@
* </p>
*/
public boolean dispatchDragEvent(DragEvent event) {
- //noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
+ //noinspection SimplifiableIfStatement
if (li != null && li.mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnDragListener.onDrag(this, event)) {
return true;
@@ -17742,6 +17851,31 @@
}
/**
+ * Set the current Scene that this view is in. The current scene is set only
+ * on the root view of a scene, not for every view in that hierarchy. This
+ * information is used by Scene to determine whether there is a previous
+ * scene which should be exited before the new scene is entered.
+ *
+ * @param scene The new scene being set on the view
+ *
+ * @hide
+ */
+ public void setCurrentScene(Scene scene) {
+ mCurrentScene = scene;
+ }
+
+ /**
+ * Gets the current {@link Scene} set on this view. A scene is set on a view
+ * only if that view is the scene root.
+ *
+ * @return The current Scene set on this view. A value of null indicates that
+ * no Scene is current set.
+ */
+ public Scene getCurrentScene() {
+ return mCurrentScene;
+ }
+
+ /**
* Interface definition for a callback to be invoked when a hardware key event is
* dispatched to this view. The callback will be invoked before the key event is
* given to the view. This is only useful for hardware keyboards; a software input
@@ -18749,6 +18883,38 @@
}
}
+ private class SendViewStateChangedAccessibilityEvent implements Runnable {
+ private boolean mPosted;
+ private long mLastEventTimeMillis;
+
+ public void run() {
+ mPosted = false;
+ mLastEventTimeMillis = SystemClock.uptimeMillis();
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeType(AccessibilityEvent.CONTENT_CHANGE_TYPE_NODE);
+ sendAccessibilityEventUnchecked(event);
+ }
+ }
+
+ public void runOrPost() {
+ if (mPosted) {
+ return;
+ }
+ final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis;
+ final long minEventIntevalMillis =
+ ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
+ if (timeSinceLastMillis >= minEventIntevalMillis) {
+ removeCallbacks(this);
+ run();
+ } else {
+ postDelayed(this, minEventIntevalMillis - timeSinceLastMillis);
+ mPosted = true;
+ }
+ }
+ }
+
/**
* Dump all private flags in readable format, useful for documentation and
* sanity checking.
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 499075e..f8cb9c0 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -291,7 +291,7 @@
if (!sHasPermanentMenuKeySet) {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
- sHasPermanentMenuKey = !wm.hasSystemNavBar() && !wm.hasNavigationBar();
+ sHasPermanentMenuKey = !wm.hasNavigationBar();
sHasPermanentMenuKeySet = true;
} catch (RemoteException ex) {
sHasPermanentMenuKey = false;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3aa4cfb..a40582b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -130,7 +130,7 @@
* A Transformation used when drawing children, to
* apply on the child being drawn.
*/
- final Transformation mChildTransformation = new Transformation();
+ private Transformation mChildTransformation;
/**
* Used to track the current invalidation region.
@@ -154,7 +154,7 @@
private boolean mChildAcceptsDrag;
// Used during drag dispatch
- private final PointF mLocalPoint = new PointF();
+ private PointF mLocalPoint;
// Layout animation
private LayoutAnimationController mLayoutAnimationController;
@@ -206,7 +206,7 @@
/**
* Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
*/
- private int mLayoutMode = DEFAULT_LAYOUT_MODE;
+ private int mLayoutMode = LAYOUT_MODE_UNDEFINED;
/**
* NOTE: If you change the flags below make sure to reflect the changes
@@ -347,6 +347,14 @@
private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
/**
+ * When true, indicates that a layoutMode has been explicitly set, either with
+ * an explicit call to {@link #setLayoutMode(int)} in code or from an XML resource.
+ * This distinguishes the situation in which a layout mode was inherited from
+ * one of the ViewGroup's ancestors and cached locally.
+ */
+ private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
+
+ /**
* Indicates which types of drawing caches are to be kept in memory.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -375,6 +383,8 @@
// Layout Modes
+ private static final int LAYOUT_MODE_UNDEFINED = -1;
+
/**
* This constant is a {@link #setLayoutMode(int) layoutMode}.
* Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
@@ -391,7 +401,7 @@
public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
/** @hide */
- public static int DEFAULT_LAYOUT_MODE = LAYOUT_MODE_CLIP_BOUNDS;
+ public static int LAYOUT_MODE_DEFAULT = LAYOUT_MODE_CLIP_BOUNDS;
/**
* We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
@@ -533,7 +543,7 @@
}
break;
case R.styleable.ViewGroup_layoutMode:
- setLayoutMode(a.getInt(attr, DEFAULT_LAYOUT_MODE));
+ setLayoutMode(a.getInt(attr, LAYOUT_MODE_UNDEFINED));
break;
}
}
@@ -1121,6 +1131,11 @@
}
}
+ private PointF getLocalPoint() {
+ if (mLocalPoint == null) mLocalPoint = new PointF();
+ return mLocalPoint;
+ }
+
/**
* {@inheritDoc}
*/
@@ -1134,6 +1149,8 @@
ViewRootImpl root = getViewRootImpl();
// Dispatch down the view hierarchy
+ final PointF localPoint = getLocalPoint();
+
switch (event.mAction) {
case DragEvent.ACTION_DRAG_STARTED: {
// clear state to recalculate which views we drag over
@@ -1194,7 +1211,7 @@
case DragEvent.ACTION_DRAG_LOCATION: {
// Find the [possibly new] drag target
- final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+ final View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
// If we've changed apparent drag target, tell the view root which view
// we're over now [for purposes of the eventual drag-recipient-changed
@@ -1228,8 +1245,8 @@
// Dispatch the actual drag location notice, localized into its coordinates
if (target != null) {
- event.mX = mLocalPoint.x;
- event.mY = mLocalPoint.y;
+ event.mX = localPoint.x;
+ event.mY = localPoint.y;
retval = target.dispatchDragEvent(event);
@@ -1263,11 +1280,11 @@
case DragEvent.ACTION_DROP: {
if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
- View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+ View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
if (target != null) {
if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
- event.mX = mLocalPoint.x;
- event.mY = mLocalPoint.y;
+ event.mX = localPoint.x;
+ event.mY = localPoint.y;
retval = target.dispatchDragEvent(event);
event.mX = tx;
event.mY = ty;
@@ -1690,16 +1707,6 @@
}
/**
- * @hide
- */
- @Override
- public void childAccessibilityStateChanged(View child) {
- if (mParent != null) {
- mParent.childAccessibilityStateChanged(child);
- }
- }
-
- /**
* Implement this method to intercept hover events before they are handled
* by child views.
* <p>
@@ -2527,13 +2534,19 @@
* @hide
*/
@Override
- public void resetAccessibilityStateChanged() {
- super.resetAccessibilityStateChanged();
+ public void childAccessibilityStateChanged(View root) {
+ if (mParent != null) {
+ mParent.childAccessibilityStateChanged(root);
+ }
+ }
+
+ @Override
+ void resetSubtreeAccessibilityStateChanged() {
+ super.resetSubtreeAccessibilityStateChanged();
View[] children = mChildren;
final int childCount = mChildrenCount;
for (int i = 0; i < childCount; i++) {
- View child = children[i];
- child.resetAccessibilityStateChanged();
+ children[i].resetSubtreeAccessibilityStateChanged();
}
}
@@ -2773,8 +2786,8 @@
return (int) (dips * scale + 0.5f);
}
- private void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
- int lineLength, int lineWidth) {
+ private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
+ int lineLength, int lineWidth) {
drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth);
drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth);
@@ -3204,6 +3217,13 @@
return false;
}
+ Transformation getChildTransformation() {
+ if (mChildTransformation == null) {
+ mChildTransformation = new Transformation();
+ }
+ return mChildTransformation;
+ }
+
/**
* {@hide}
*/
@@ -3451,6 +3471,24 @@
}
}
+ private void clearCachedLayoutMode() {
+ if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
+ mLayoutMode = LAYOUT_MODE_UNDEFINED;
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ clearCachedLayoutMode();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ clearCachedLayoutMode();
+ }
+
/**
* Adds a view during layout. This is useful if in your onLayout() method,
* you need to add more views (as does the list view for example).
@@ -3565,6 +3603,10 @@
if (child.hasTransientState()) {
childHasTransientStateChanged(child, true);
}
+
+ if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
}
private void addInArray(View child, int index) {
@@ -3804,6 +3846,10 @@
}
onViewRemoved(view);
+
+ if (view.isImportantForAccessibility() && view.getVisibility() != View.GONE) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
}
/**
@@ -4380,8 +4426,10 @@
/**
* Quick invalidation method that simply transforms the dirty rect into the parent's
* coordinate system, pruning the invalidation if the parent has already been invalidated.
+ *
+ * @hide
*/
- private ViewParent invalidateChildInParentFast(int left, int top, final Rect dirty) {
+ protected ViewParent invalidateChildInParentFast(int left, int top, final Rect dirty) {
if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
(mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
dirty.offset(left - mScrollX, top - mScrollY);
@@ -4754,6 +4802,10 @@
setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
}
+ private boolean hasBooleanFlag(int flag) {
+ return (mGroupFlags & flag) == flag;
+ }
+
private void setBooleanFlag(int flag, boolean value) {
if (value) {
mGroupFlags |= flag;
@@ -4797,24 +4849,63 @@
mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
}
+ private void setLayoutMode(int layoutMode, boolean explicitly) {
+ mLayoutMode = layoutMode;
+ setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly);
+ }
+
/**
- * Returns the basis of alignment during layout operations on this view group:
+ * Recursively traverse the view hierarchy, resetting the layoutMode of any
+ * descendants that had inherited a different layoutMode from a previous parent.
+ * Recursion terminates when a descendant's mode is:
+ * <ul>
+ * <li>Undefined</li>
+ * <li>The same as the root node's</li>
+ * <li>A mode that had been explicitly set</li>
+ * <ul/>
+ * The first two clauses are optimizations.
+ * @param layoutModeOfRoot
+ */
+ @Override
+ void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
+ if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
+ mLayoutMode == layoutModeOfRoot ||
+ hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
+ return;
+ }
+ setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
+
+ // apply recursively
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot);
+ }
+ }
+
+ /**
+ * Returns the basis of alignment during layout operations on this ViewGroup:
* either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
+ * <p>
+ * If no layoutMode was explicitly set, either programmatically or in an XML resource,
+ * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
+ * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
*
* @return the layout mode to use during layout operations
*
* @see #setLayoutMode(int)
*/
public int getLayoutMode() {
+ if (mLayoutMode == LAYOUT_MODE_UNDEFINED) {
+ int inheritedLayoutMode = (mParent instanceof ViewGroup) ?
+ ((ViewGroup) mParent).getLayoutMode() : LAYOUT_MODE_DEFAULT;
+ setLayoutMode(inheritedLayoutMode, false);
+ }
return mLayoutMode;
}
/**
- * Sets the basis of alignment during the layout of this view group.
+ * Sets the basis of alignment during the layout of this ViewGroup.
* Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
* {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
- * <p>
- * The default is {@link #LAYOUT_MODE_CLIP_BOUNDS}.
*
* @param layoutMode the layout mode to use during layout operations
*
@@ -4823,7 +4914,8 @@
*/
public void setLayoutMode(int layoutMode) {
if (mLayoutMode != layoutMode) {
- mLayoutMode = layoutMode;
+ invalidateInheritedLayoutMode(layoutMode);
+ setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
requestLayout();
}
}
@@ -6343,7 +6435,7 @@
*/
private static final class TouchTarget {
private static final int MAX_RECYCLED = 32;
- private static final Object sRecycleLock = new Object();
+ private static final Object sRecycleLock = new Object[0];
private static TouchTarget sRecycleBin;
private static int sRecycledCount;
@@ -6395,7 +6487,7 @@
/* Describes a hovered view. */
private static final class HoverTarget {
private static final int MAX_RECYCLED = 32;
- private static final Object sRecycleLock = new Object();
+ private static final Object sRecycleLock = new Object[0];
private static HoverTarget sRecycleBin;
private static int sRecycledCount;
@@ -6614,8 +6706,9 @@
return sDebugPaint;
}
- private void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
+ private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
if (sDebugLines== null) {
+ // TODO: This won't work with multiple UI threads in a single process
sDebugLines = new float[16];
}
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 5510939..975931a 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -15,6 +15,7 @@
*/
package android.view;
+import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -169,6 +170,14 @@
child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]);
}
parent.removeView(child);
+ if (parent.getLayoutTransition() != null) {
+ // LayoutTransition will cause the child to delay removal - cancel it
+ parent.getLayoutTransition().cancel(LayoutTransition.DISAPPEARING);
+ }
+ // fail-safe if view is still attached for any reason
+ if (child.getParent() != null) {
+ child.mParent = null;
+ }
}
super.addView(child);
}
@@ -291,6 +300,17 @@
}
}
+ /**
+ * @hide
+ */
+ @Override
+ protected ViewParent invalidateChildInParentFast(int left, int top, Rect dirty) {
+ if (mHostView instanceof ViewGroup) {
+ return ((ViewGroup) mHostView).invalidateChildInParentFast(left, top, dirty);
+ }
+ return null;
+ }
+
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
if (mHostView != null) {
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index d79aa7e..2ebc1a1 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -292,13 +292,14 @@
public ViewParent getParentForAccessibility();
/**
- * A child notifies its parent that its state for accessibility has changed.
- * That is some of the child properties reported to accessibility services has
- * changed, hence the interested services have to be notified for the new state.
+ * A child notifies its parent that the accessibility state of a subtree rooted
+ * at a given node changed. That is the structure of the subtree is different.
+ *
+ * @param The root of the changed subtree.
*
* @hide
*/
- public void childAccessibilityStateChanged(View child);
+ public void childAccessibilityStateChanged(View root);
/**
* Tells if this view parent can resolve the layout direction.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6b2ed91..93c6d6e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -23,7 +23,6 @@
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -108,13 +107,12 @@
private static final boolean DEBUG_FPS = false;
private static final boolean DEBUG_INPUT_PROCESSING = false || LOCAL_LOGV;
- private static final boolean USE_RENDER_THREAD = false;
-
/**
* Set this system property to true to force the view hierarchy to render
* at 60 Hz. This can be used to measure the potential framerate.
*/
- private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering";
+ private static final String PROPERTY_PROFILE_RENDERING = "viewroot.profile_rendering";
+ private static final String PROPERTY_MEDIA_DISABLED = "config.disable_media";
/**
* Maximum time we allow the user to roll the trackball enough to generate
@@ -126,14 +124,10 @@
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
static boolean sFirstDrawComplete = false;
-
+
static final ArrayList<ComponentCallbacks> sConfigCallbacks
= new ArrayList<ComponentCallbacks>();
- private static boolean sUseRenderThread = false;
- private static boolean sRenderThreadQueried = false;
- private static final Object[] sRenderThreadQueryLock = new Object[0];
-
final Context mContext;
final IWindowSession mWindowSession;
final Display mDisplay;
@@ -167,14 +161,14 @@
// Set to true if the owner of this window is in the stopped state,
// so the window should no longer be active.
boolean mStopped = false;
-
+
boolean mLastInCompatMode = false;
SurfaceHolder.Callback2 mSurfaceHolderCallback;
BaseSurfaceHolder mSurfaceHolder;
boolean mIsCreating;
boolean mDrawingAllowed;
-
+
final Region mTransparentRegion;
final Region mPreviousTransparentRegion;
@@ -245,7 +239,7 @@
boolean mAdded;
boolean mAddedTouchMode;
- final CompatibilityInfoHolder mCompatibilityInfo;
+ final DisplayAdjustments mDisplayAdjustments;
// These are accessed by multiple threads.
final Rect mWinFrame; // frame given by window manager.
@@ -286,6 +280,8 @@
private Choreographer.FrameCallback mRenderProfiler;
private boolean mRenderProfilingEnabled;
+ private boolean mMediaDisabled;
+
// Variables to track frames per second, enabled via DEBUG_FPS flag
private long mFpsStartTime = -1;
private long mFpsPrevTime = -1;
@@ -317,6 +313,9 @@
private int mViewLayoutDirectionInitial;
+ /** Set to true once doDie() has been called. */
+ private boolean mRemoved;
+
/**
* Consistency verifier for debugging purposes.
*/
@@ -337,8 +336,7 @@
mDisplay = display;
mBasePackageName = context.getBasePackageName();
- CompatibilityInfoHolder cih = display.getCompatibilityInfo();
- mCompatibilityInfo = cih != null ? cih : new CompatibilityInfoHolder();
+ mDisplayAdjustments = display.getDisplayAdjustments();
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
@@ -373,35 +371,6 @@
loadSystemProperties();
}
- /**
- * @return True if the application requests the use of a separate render thread,
- * false otherwise
- */
- private static boolean isRenderThreadRequested(Context context) {
- if (USE_RENDER_THREAD) {
- synchronized (sRenderThreadQueryLock) {
- if (!sRenderThreadQueried) {
- final PackageManager packageManager = context.getPackageManager();
- final String packageName = context.getApplicationInfo().packageName;
- try {
- ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName,
- PackageManager.GET_META_DATA);
- if (applicationInfo.metaData != null) {
- sUseRenderThread = applicationInfo.metaData.getBoolean(
- "android.graphics.renderThread", false);
- }
- } catch (PackageManager.NameNotFoundException e) {
- } finally {
- sRenderThreadQueried = true;
- }
- }
- return sUseRenderThread;
- }
- } else {
- return false;
- }
- }
-
public static void addFirstDrawHandler(Runnable callback) {
synchronized (sFirstDrawHandlers) {
if (!sFirstDrawComplete) {
@@ -474,12 +443,13 @@
}
}
- CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
+ CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
+ mDisplayAdjustments.setActivityToken(attrs.token);
// If the application owns the surface, don't enable hardware acceleration
if (mSurfaceHolder == null) {
- enableHardwareAcceleration(mView.getContext(), attrs);
+ enableHardwareAcceleration(attrs);
}
boolean restore = false;
@@ -673,6 +643,18 @@
}
}
+ void flushHardwareLayerUpdates() {
+ if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled() &&
+ mAttachInfo.mHardwareRenderer.validate()) {
+ mAttachInfo.mHardwareRenderer.flushLayerUpdates();
+ }
+ }
+
+ void dispatchFlushHardwareLayerUpdates() {
+ mHandler.removeMessages(MSG_FLUSH_LAYER_UPDATES);
+ mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES));
+ }
+
public boolean attachFunctor(int functor) {
//noinspection SimplifiableIfStatement
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
@@ -687,7 +669,7 @@
}
}
- private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) {
+ private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
@@ -727,11 +709,6 @@
return;
}
- final boolean renderThread = isRenderThreadRequested(context);
- if (renderThread) {
- Log.i(HardwareRenderer.LOG_TAG, "Render threat initiated");
- }
-
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.destroy(true);
}
@@ -865,6 +842,7 @@
invalidateChildInParent(null, dirty);
}
+ @Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
@@ -922,10 +900,12 @@
}
}
+ @Override
public ViewParent getParent() {
return null;
}
+ @Override
public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
if (child != mView) {
throw new RuntimeException("child is not mine, honest!");
@@ -943,9 +923,14 @@
}
void disposeResizeBuffer() {
- if (mResizeBuffer != null) {
- mResizeBuffer.destroy();
- mResizeBuffer = null;
+ if (mResizeBuffer != null && mAttachInfo.mHardwareRenderer != null) {
+ mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() {
+ @Override
+ public void run() {
+ mResizeBuffer.destroy();
+ mResizeBuffer = null;
+ }
+ });
}
}
@@ -1151,7 +1136,7 @@
surfaceChanged = true;
params = lp;
}
- CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
+ CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
params = lp;
mFullRedrawNeeded = true;
@@ -1486,8 +1471,7 @@
if (mResizeBuffer != null) {
mResizeBuffer.end(hwRendererCanvas);
if (!completed) {
- mResizeBuffer.destroy();
- mResizeBuffer = null;
+ disposeResizeBuffer();
}
}
}
@@ -1766,10 +1750,6 @@
if (triggerGlobalLayoutListener) {
attachInfo.mRecomputeGlobalAttributes = false;
attachInfo.mTreeObserver.dispatchOnGlobalLayout();
-
- if (AccessibilityManager.getInstance(host.mContext).isEnabled()) {
- postSendWindowContentChangedCallback(mView);
- }
}
if (computesInternalInsets) {
@@ -2867,8 +2847,8 @@
+ mWindowAttributes.getTitle()
+ ": " + config);
- CompatibilityInfo ci = mCompatibilityInfo.getIfNeeded();
- if (ci != null) {
+ CompatibilityInfo ci = mDisplayAdjustments.getCompatibilityInfo();
+ if (!ci.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
config = new Configuration(config);
ci.applyToConfiguration(mNoncompatDensity, config);
}
@@ -2942,6 +2922,7 @@
private final static int MSG_DISPATCH_DONE_ANIMATING = 22;
private final static int MSG_INVALIDATE_WORLD = 23;
private final static int MSG_WINDOW_MOVED = 24;
+ private final static int MSG_FLUSH_LAYER_UPDATES = 25;
final class ViewRootHandler extends Handler {
@Override
@@ -2991,6 +2972,8 @@
return "MSG_DISPATCH_DONE_ANIMATING";
case MSG_WINDOW_MOVED:
return "MSG_WINDOW_MOVED";
+ case MSG_FLUSH_LAYER_UPDATES:
+ return "MSG_FLUSH_LAYER_UPDATES";
}
return super.getMessageName(message);
}
@@ -3213,6 +3196,9 @@
invalidateWorld(mView);
}
} break;
+ case MSG_FLUSH_LAYER_UPDATES: {
+ flushHardwareLayerUpdates();
+ } break;
}
}
}
@@ -5058,6 +5044,10 @@
public void playSoundEffect(int effectId) {
checkThread();
+ if (mMediaDisabled) {
+ return;
+ }
+
try {
final AudioManager audioManager = getAudioManager();
@@ -5138,26 +5128,36 @@
}
}
- public void die(boolean immediate) {
+ /**
+ * @param immediate True, do now if not in traversal. False, put on queue and do later.
+ * @return True, request has been queued. False, request has been completed.
+ */
+ boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
if (immediate && !mIsInTraversal) {
doDie();
- } else {
- if (!mIsDrawing) {
- destroyHardwareRenderer();
- } else {
- Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
- " window=" + this + ", title=" + mWindowAttributes.getTitle());
- }
- mHandler.sendEmptyMessage(MSG_DIE);
+ return false;
}
+
+ if (!mIsDrawing) {
+ destroyHardwareRenderer();
+ } else {
+ Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
+ " window=" + this + ", title=" + mWindowAttributes.getTitle());
+ }
+ mHandler.sendEmptyMessage(MSG_DIE);
+ return true;
}
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
synchronized (this) {
+ if (mRemoved) {
+ return;
+ }
+ mRemoved = true;
if (mAdded) {
dispatchDetachedFromWindow();
}
@@ -5181,13 +5181,14 @@
} catch (RemoteException e) {
}
}
-
+
mSurface.release();
}
}
mAdded = false;
}
+ WindowManagerGlobal.getInstance().doRemoveView(this);
}
public void requestUpdateConfiguration(Configuration config) {
@@ -5203,6 +5204,9 @@
mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false);
profileRendering(mAttachInfo.mHasWindowFocus);
+ // Media (used by sound effects)
+ mMediaDisabled = SystemProperties.getBoolean(PROPERTY_MEDIA_DISABLED, false);
+
// Hardware rendering
if (mAttachInfo.mHardwareRenderer != null) {
if (mAttachInfo.mHardwareRenderer.loadSystemProperties(mHolder.getSurface())) {
@@ -5744,15 +5748,7 @@
mSendWindowContentChangedAccessibilityEvent =
new SendWindowContentChangedAccessibilityEvent();
}
- View oldSource = mSendWindowContentChangedAccessibilityEvent.mSource;
- if (oldSource == null) {
- mSendWindowContentChangedAccessibilityEvent.mSource = source;
- mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent,
- ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
- } else {
- mSendWindowContentChangedAccessibilityEvent.mSource =
- getCommonPredecessor(oldSource, source);
- }
+ mSendWindowContentChangedAccessibilityEvent.runOrPost(source);
}
/**
@@ -6436,12 +6432,35 @@
private class SendWindowContentChangedAccessibilityEvent implements Runnable {
public View mSource;
+ public long mLastEventTimeMillis;
public void run() {
+ mLastEventTimeMillis = SystemClock.uptimeMillis();
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeType(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ mSource.sendAccessibilityEventUnchecked(event);
+ mSource.resetSubtreeAccessibilityStateChanged();
+ mSource = null;
+ }
+
+ public void runOrPost(View source) {
if (mSource != null) {
- mSource.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- mSource.resetAccessibilityStateChanged();
- mSource = null;
+ // If there is no common predecessor, then mSource points to
+ // a removed view, hence in this case always prefer the source.
+ View predecessor = getCommonPredecessor(mSource, source);
+ mSource = (predecessor != null) ? predecessor : source;
+ return;
+ }
+ mSource = source;
+ final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis;
+ final long minEventIntevalMillis =
+ ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
+ if (timeSinceLastMillis >= minEventIntevalMillis) {
+ mSource.removeCallbacks(this);
+ run();
+ } else {
+ mSource.postDelayed(this, minEventIntevalMillis - timeSinceLastMillis);
}
}
}
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 072c95f..730c4eb 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -243,9 +243,9 @@
@Override
public int hashCode() {
- int result = contentInsets != null ? contentInsets.hashCode() : 0;
- result = 31 * result + (visibleInsets != null ? visibleInsets.hashCode() : 0);
- result = 31 * result + (touchableRegion != null ? touchableRegion.hashCode() : 0);
+ int result = contentInsets.hashCode();
+ result = 31 * result + visibleInsets.hashCode();
+ result = 31 * result + touchableRegion.hashCode();
result = 31 * result + mTouchableInsets;
return result;
}
@@ -814,6 +814,13 @@
}
/**
+ * Returns whether there are listeners for on pre-draw events.
+ */
+ final boolean hasOnPreDrawListeners() {
+ return mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0;
+ }
+
+ /**
* Notifies registered listeners that the drawing pass is about to start. If a
* listener returns true, then the drawing pass is canceled and rescheduled. This can
* be called manually if you are forcing the drawing on a View or a hierarchy of Views
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 06974d3..39d48a7 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1256,4 +1256,38 @@
* @param mask Flags specifying which options should be modified. Others will remain unchanged.
*/
public void setUiOptions(int uiOptions, int mask) { }
+
+ /**
+ * Set the primary icon for this window.
+ *
+ * @param resId resource ID of a drawable to set
+ */
+ public void setIcon(int resId) { }
+
+ /**
+ * Set the default icon for this window.
+ * This will be overridden by any other icon set operation which could come from the
+ * theme or another explicit set.
+ *
+ * @hide
+ */
+ public void setDefaultIcon(int resId) { }
+
+ /**
+ * Set the logo for this window. A logo is often shown in place of an
+ * {@link #setIcon(int) icon} but is generally wider and communicates window title information
+ * as well.
+ *
+ * @param resId resource ID of a drawable to set
+ */
+ public void setLogo(int resId) { }
+
+ /**
+ * Set the default logo for this window.
+ * This will be overridden by any other logo set operation which could come from the
+ * theme or another explicit set.
+ *
+ * @hide
+ */
+ public void setDefaultLogo(int resId) { }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 541c503..5144889 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -527,6 +527,14 @@
*/
public static final int TYPE_RECENTS_OVERLAY = FIRST_SYSTEM_WINDOW+28;
+
+ /**
+ * Window type: keyguard scrim window. Shows if keyguard needs to be restarted.
+ * In multiuser systems shows on all users' windows.
+ * @hide
+ */
+ public static final int TYPE_KEYGUARD_SCRIM = FIRST_SYSTEM_WINDOW+29;
+
/**
* End of types of system windows.
*/
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 0ff46e9..b183bb6 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -21,18 +21,22 @@
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.opengl.ManagedEGLContext;
+import android.os.Debug;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
+import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
+import java.util.ArrayList;
/**
* Provides low-level communication with the system window manager for
@@ -107,9 +111,11 @@
private final Object mLock = new Object();
- private View[] mViews;
- private ViewRootImpl[] mRoots;
- private WindowManager.LayoutParams[] mParams;
+ private final ArrayList<View> mViews = new ArrayList<View>();
+ private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
+ private final ArrayList<WindowManager.LayoutParams> mParams =
+ new ArrayList<WindowManager.LayoutParams>();
+ private final ArraySet<View> mDyingViews = new ArraySet<View>();
private boolean mNeedsEglTerminate;
private Runnable mSystemPropertyUpdater;
@@ -162,11 +168,10 @@
public String[] getViewRootNames() {
synchronized (mLock) {
- if (mRoots == null) return new String[0];
- String[] mViewRoots = new String[mRoots.length];
- int i = 0;
- for (ViewRootImpl root : mRoots) {
- mViewRoots[i++] = getWindowName(root);
+ final int numRoots = mRoots.size();
+ String[] mViewRoots = new String[numRoots];
+ for (int i = 0; i < numRoots; ++i) {
+ mViewRoots[i] = getWindowName(mRoots.get(i));
}
return mViewRoots;
}
@@ -174,8 +179,8 @@
public View getRootView(String name) {
synchronized (mLock) {
- if (mRoots == null) return null;
- for (ViewRootImpl root : mRoots) {
+ for (int i = mRoots.size() - 1; i >= 0; --i) {
+ final ViewRootImpl root = mRoots.get(i);
if (name.equals(getWindowName(root))) return root.getView();
}
}
@@ -209,8 +214,8 @@
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
- for (ViewRootImpl viewRoot : mRoots) {
- viewRoot.loadSystemProperties();
+ for (int i = mRoots.size() - 1; i >= 0; --i) {
+ mRoots.get(i).loadSystemProperties();
}
}
}
@@ -220,18 +225,24 @@
int index = findViewLocked(view, false);
if (index >= 0) {
- throw new IllegalStateException("View " + view
- + " has already been added to the window manager.");
+ if (mDyingViews.contains(view)) {
+ // Don't wait for MSG_DIE to make it's way through root's queue.
+ mRoots.get(index).doDie();
+ } else {
+ throw new IllegalStateException("View " + view
+ + " has already been added to the window manager.");
+ }
+ // The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
- final int count = mViews != null ? mViews.length : 0;
- for (int i=0; i<count; i++) {
- if (mRoots[i].mWindow.asBinder() == wparams.token) {
- panelParentView = mViews[i];
+ final int count = mViews.size();
+ for (int i = 0; i < count; i++) {
+ if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
+ panelParentView = mViews.get(i);
}
}
}
@@ -240,28 +251,9 @@
view.setLayoutParams(wparams);
- if (mViews == null) {
- index = 1;
- mViews = new View[1];
- mRoots = new ViewRootImpl[1];
- mParams = new WindowManager.LayoutParams[1];
- } else {
- index = mViews.length + 1;
- Object[] old = mViews;
- mViews = new View[index];
- System.arraycopy(old, 0, mViews, 0, index-1);
- old = mRoots;
- mRoots = new ViewRootImpl[index];
- System.arraycopy(old, 0, mRoots, 0, index-1);
- old = mParams;
- mParams = new WindowManager.LayoutParams[index];
- System.arraycopy(old, 0, mParams, 0, index-1);
- }
- index--;
-
- mViews[index] = view;
- mRoots[index] = root;
- mParams[index] = wparams;
+ mViews.add(view);
+ mRoots.add(root);
+ mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
@@ -293,8 +285,9 @@
synchronized (mLock) {
int index = findViewLocked(view, true);
- ViewRootImpl root = mRoots[index];
- mParams[index] = wparams;
+ ViewRootImpl root = mRoots.get(index);
+ mParams.remove(index);
+ mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
@@ -306,7 +299,8 @@
synchronized (mLock) {
int index = findViewLocked(view, true);
- View curView = removeViewLocked(index, immediate);
+ View curView = mRoots.get(index).getView();
+ removeViewLocked(index, immediate);
if (curView == view) {
return;
}
@@ -318,16 +312,13 @@
public void closeAll(IBinder token, String who, String what) {
synchronized (mLock) {
- if (mViews == null)
- return;
-
- int count = mViews.length;
+ int count = mViews.size();
//Log.i("foo", "Closing all windows of " + token);
- for (int i=0; i<count; i++) {
+ for (int i = 0; i < count; i++) {
//Log.i("foo", "@ " + i + " token " + mParams[i].token
// + " view " + mRoots[i].getView());
- if (token == null || mParams[i].token == token) {
- ViewRootImpl root = mRoots[i];
+ if (token == null || mParams.get(i).token == token) {
+ ViewRootImpl root = mRoots.get(i);
//Log.i("foo", "Force closing " + root);
if (who != null) {
@@ -335,77 +326,52 @@
what + " " + who + " has leaked window "
+ root.getView() + " that was originally added here");
leak.setStackTrace(root.getLocation().getStackTrace());
- Log.e(TAG, leak.getMessage(), leak);
+ Log.e(TAG, "", leak);
}
removeViewLocked(i, false);
- i--;
- count--;
}
}
}
}
- private View removeViewLocked(int index, boolean immediate) {
- ViewRootImpl root = mRoots[index];
+ private void removeViewLocked(int index, boolean immediate) {
+ ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
- imm.windowDismissed(mViews[index].getWindowToken());
+ imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
- root.die(immediate);
-
- final int count = mViews.length;
-
- // remove it from the list
- View[] tmpViews = new View[count-1];
- removeItem(tmpViews, mViews, index);
- mViews = tmpViews;
-
- ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1];
- removeItem(tmpRoots, mRoots, index);
- mRoots = tmpRoots;
-
- WindowManager.LayoutParams[] tmpParams
- = new WindowManager.LayoutParams[count-1];
- removeItem(tmpParams, mParams, index);
- mParams = tmpParams;
-
+ boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
- // func doesn't allow null... does it matter if we clear them?
- //view.setLayoutParams(null);
+ if (deferred) {
+ mDyingViews.add(view);
+ }
}
- return view;
}
- private static void removeItem(Object[] dst, Object[] src, int index) {
- if (dst.length > 0) {
- if (index > 0) {
- System.arraycopy(src, 0, dst, 0, index);
- }
- if (index < dst.length) {
- System.arraycopy(src, index+1, dst, index, src.length-index-1);
+ void doRemoveView(ViewRootImpl root) {
+ synchronized (mLock) {
+ final int index = mRoots.indexOf(root);
+ if (index >= 0) {
+ mRoots.remove(index);
+ mParams.remove(index);
+ final View view = mViews.remove(index);
+ mDyingViews.remove(view);
}
}
}
private int findViewLocked(View view, boolean required) {
- if (mViews != null) {
- final int count = mViews.length;
- for (int i = 0; i < count; i++) {
- if (mViews[i] == view) {
- return i;
- }
- }
+ final int index = mViews.indexOf(view);
+ if (required && index < 0) {
+ throw new IllegalArgumentException("View=" + view + " not attached to window manager");
}
- if (required) {
- throw new IllegalArgumentException("View not attached to window manager");
- }
- return -1;
+ return index;
}
public void startTrimMemory(int level) {
@@ -418,10 +384,8 @@
// Destroy all hardware surfaces and resources associated to
// known windows
synchronized (mLock) {
- if (mViews == null) return;
- int count = mViews.length;
- for (int i = 0; i < count; i++) {
- mRoots[i].terminateHardwareResources();
+ for (int i = mRoots.size() - 1; i >= 0; --i) {
+ mRoots.get(i).terminateHardwareResources();
}
}
// Force a full memory flush
@@ -445,64 +409,60 @@
public void trimLocalMemory() {
synchronized (mLock) {
- if (mViews == null) return;
- int count = mViews.length;
- for (int i = 0; i < count; i++) {
- mRoots[i].destroyHardwareLayers();
+ for (int i = mRoots.size() - 1; i >= 0; --i) {
+ mRoots.get(i).destroyHardwareLayers();
}
}
}
public void dumpGfxInfo(FileDescriptor fd) {
FileOutputStream fout = new FileOutputStream(fd);
- PrintWriter pw = new PrintWriter(fout);
+ PrintWriter pw = new FastPrintWriter(fout);
try {
synchronized (mLock) {
- if (mViews != null) {
- final int count = mViews.length;
+ final int count = mViews.size();
- pw.println("Profile data in ms:");
+ pw.println("Profile data in ms:");
- for (int i = 0; i < count; i++) {
- ViewRootImpl root = mRoots[i];
- String name = getWindowName(root);
- pw.printf("\n\t%s", name);
+ for (int i = 0; i < count; i++) {
+ ViewRootImpl root = mRoots.get(i);
+ String name = getWindowName(root);
+ pw.printf("\n\t%s", name);
- HardwareRenderer renderer =
- root.getView().mAttachInfo.mHardwareRenderer;
- if (renderer != null) {
- renderer.dumpGfxInfo(pw);
- }
+ HardwareRenderer renderer =
+ root.getView().mAttachInfo.mHardwareRenderer;
+ if (renderer != null) {
+ renderer.dumpGfxInfo(pw);
}
-
- pw.println("\nView hierarchy:\n");
-
- int viewsCount = 0;
- int displayListsSize = 0;
- int[] info = new int[2];
-
- for (int i = 0; i < count; i++) {
- ViewRootImpl root = mRoots[i];
- root.dumpGfxInfo(info);
-
- String name = getWindowName(root);
- pw.printf(" %s\n %d views, %.2f kB of display lists",
- name, info[0], info[1] / 1024.0f);
- HardwareRenderer renderer =
- root.getView().mAttachInfo.mHardwareRenderer;
- if (renderer != null) {
- pw.printf(", %d frames rendered", renderer.getFrameCount());
- }
- pw.printf("\n\n");
-
- viewsCount += info[0];
- displayListsSize += info[1];
- }
-
- pw.printf("\nTotal ViewRootImpl: %d\n", count);
- pw.printf("Total Views: %d\n", viewsCount);
- pw.printf("Total DisplayList: %.2f kB\n\n", displayListsSize / 1024.0f);
}
+
+ pw.println("\nView hierarchy:\n");
+
+ int viewsCount = 0;
+ int displayListsSize = 0;
+ int[] info = new int[2];
+
+ for (int i = 0; i < count; i++) {
+ ViewRootImpl root = mRoots.get(i);
+ root.dumpGfxInfo(info);
+
+ String name = getWindowName(root);
+ pw.printf(" %s\n %d views, %.2f kB of display lists",
+ name, info[0], info[1] / 1024.0f);
+ HardwareRenderer renderer =
+ root.getView().mAttachInfo.mHardwareRenderer;
+ if (renderer != null) {
+ pw.printf(", %d frames rendered", renderer.getFrameCount());
+ }
+ pw.printf("\n\n");
+
+ viewsCount += info[0];
+ displayListsSize += info[1];
+ }
+
+ pw.printf("\nTotal ViewRootImpl: %d\n", count);
+ pw.printf("Total Views: %d\n", viewsCount);
+ pw.printf("Total DisplayList: %.2f kB\n\n", displayListsSize / 1024.0f);
}
} finally {
pw.flush();
@@ -516,13 +476,11 @@
public void setStoppedState(IBinder token, boolean stopped) {
synchronized (mLock) {
- if (mViews != null) {
- int count = mViews.length;
- for (int i=0; i < count; i++) {
- if (token == null || mParams[i].token == token) {
- ViewRootImpl root = mRoots[i];
- root.setStopped(stopped);
- }
+ int count = mViews.size();
+ for (int i = 0; i < count; i++) {
+ if (token == null || mParams.get(i).token == token) {
+ ViewRootImpl root = mRoots.get(i);
+ root.setStopped(stopped);
}
}
}
@@ -530,13 +488,11 @@
public void reportNewConfiguration(Configuration config) {
synchronized (mLock) {
- if (mViews != null) {
- int count = mViews.length;
- config = new Configuration(config);
- for (int i=0; i < count; i++) {
- ViewRootImpl root = mRoots[i];
- root.requestUpdateConfiguration(config);
- }
+ int count = mViews.size();
+ config = new Configuration(config);
+ for (int i=0; i < count; i++) {
+ ViewRootImpl root = mRoots.get(i);
+ root.requestUpdateConfiguration(config);
}
}
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index c0044b6..c26482b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -408,11 +408,6 @@
public int getLidState();
/**
- * Creates an input channel that will receive all input from the input dispatcher.
- */
- public InputChannel monitorInput(String name);
-
- /**
* Switch the keyboard layout for the given device.
* Direction should be +1 or -1 to go to the next or previous keyboard layout.
*/
@@ -420,6 +415,26 @@
public void shutdown(boolean confirm);
public void rebootSafeMode(boolean confirm);
+
+ /**
+ * Return the window manager lock needed to correctly call "Lw" methods.
+ */
+ public Object getWindowManagerLock();
+
+ /** Register a system listener for touch events */
+ void registerPointerEventListener(PointerEventListener listener);
+
+ /** Unregister a system listener for touch events */
+ void unregisterPointerEventListener(PointerEventListener listener);
+ }
+
+ public interface PointerEventListener {
+ /**
+ * 1. onPointerEvent will be called on the service.UiThread.
+ * 2. motionEvent will be recycled after onPointerEvent returns so if it is needed later a
+ * copy() must be made and the copy must be recycled.
+ **/
+ public void onPointerEvent(MotionEvent motionEvent);
}
/** Window has been added to the screen. */
@@ -563,12 +578,6 @@
public int getAboveUniverseLayer();
/**
- * Return true if the policy desires a full unified system nav bar. Otherwise,
- * it is a phone-style status bar with optional nav bar.
- */
- public boolean hasSystemNavBar();
-
- /**
* Return the display width available after excluding any screen
* decorations that can never be removed. That is, system bar or
* button bar.
@@ -637,7 +646,7 @@
*/
public View addStartingWindow(IBinder appToken, String packageName,
int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
- int labelRes, int icon, int windowFlags);
+ int labelRes, int icon, int logo, int windowFlags);
/**
* Called when the first window of an application has been displayed, while
@@ -816,6 +825,14 @@
public int getSystemDecorRectLw(Rect systemRect);
/**
+ * Return the rectangle of the screen that is available for applications to run in.
+ * This will be called immediately after {@link #beginLayoutLw}.
+ *
+ * @param r The rectangle to be filled with the boundaries available to applications.
+ */
+ public void getContentRectLw(Rect r);
+
+ /**
* Called for each window attached to the window manager as layout is
* proceeding. The implementation of this function must take care of
* setting the window's frame, either here or in finishLayout().
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index dbeca1f..82c8163 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -326,6 +326,7 @@
* <em>Properties:</em></br>
* <ul>
* <li>{@link #getEventType()} - The type of the event.</li>
+ * <li>{@link #getContentChangeType()} - The type of content change.</li>
* <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
@@ -661,6 +662,18 @@
public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000;
/**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * The subtree rooted at the source node changed.
+ */
+ public static final int CONTENT_CHANGE_TYPE_SUBTREE = 0;
+
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * Only the source node changed.
+ */
+ public static final int CONTENT_CHANGE_TYPE_NODE = 1;
+
+ /**
* Mask for {@link AccessibilityEvent} all types.
*
* @see #TYPE_VIEW_CLICKED
@@ -695,6 +708,7 @@
private long mEventTime;
int mMovementGranularity;
int mAction;
+ int mContentChangeType;
private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
@@ -714,6 +728,7 @@
mEventType = event.mEventType;
mMovementGranularity = event.mMovementGranularity;
mAction = event.mAction;
+ mContentChangeType = event.mContentChangeType;
mEventTime = event.mEventTime;
mPackageName = event.mPackageName;
}
@@ -777,6 +792,33 @@
}
/**
+ * Gets the type of node tree change signaled by an
+ * {@link #TYPE_WINDOW_CONTENT_CHANGED} event.
+ *
+ * @see #CONTENT_CHANGE_TYPE_NODE
+ * @see #CONTENT_CHANGE_TYPE_SUBTREE
+ *
+ * @return The change type.
+ */
+ public int getContentChangeType() {
+ return mContentChangeType;
+ }
+
+ /**
+ * Sets the type of node tree change signaled by an
+ * {@link #TYPE_WINDOW_CONTENT_CHANGED} event.
+ *
+ * @see #CONTENT_CHANGE_TYPE_NODE
+ * @see #CONTENT_CHANGE_TYPE_SUBTREE
+ *
+ * @param changeType The change type.
+ */
+ public void setContentChangeType(int changeType) {
+ enforceNotSealed();
+ mContentChangeType = changeType;
+ }
+
+ /**
* Sets the event type.
*
* @param eventType The event type.
@@ -943,6 +985,7 @@
mEventType = 0;
mMovementGranularity = 0;
mAction = 0;
+ mContentChangeType = 0;
mPackageName = null;
mEventTime = 0;
while (!mRecords.isEmpty()) {
@@ -961,6 +1004,7 @@
mEventType = parcel.readInt();
mMovementGranularity = parcel.readInt();
mAction = parcel.readInt();
+ mContentChangeType = parcel.readInt();
mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
mEventTime = parcel.readLong();
mConnectionId = parcel.readInt();
@@ -1013,6 +1057,7 @@
parcel.writeInt(mEventType);
parcel.writeInt(mMovementGranularity);
parcel.writeInt(mAction);
+ parcel.writeInt(mContentChangeType);
TextUtils.writeToParcel(mPackageName, parcel, 0);
parcel.writeLong(mEventTime);
parcel.writeInt(mConnectionId);
@@ -1074,6 +1119,7 @@
builder.append(super.toString());
if (DEBUG) {
builder.append("\n");
+ builder.append("; ContentChangeType: ").append(mContentChangeType);
builder.append("; sourceWindowId: ").append(mSourceWindowId);
builder.append("; mSourceNodeId: ").append(mSourceNodeId);
for (int i = 0; i < mRecords.size(); i++) {
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 84d7e72..139df3e 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -163,7 +163,7 @@
public AccessibilityNodeInfo getRootInActiveWindow(int connectionId) {
return findAccessibilityNodeInfoByAccessibilityId(connectionId,
AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
- AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
+ false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
}
/**
@@ -177,18 +177,22 @@
* where to start the search. Use
* {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
* to start from the root.
+ * @param bypassCache Whether to bypass the cache while looking for the node.
* @param prefetchFlags flags to guide prefetching.
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
*/
public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
- int accessibilityWindowId, long accessibilityNodeId, int prefetchFlags) {
+ int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache,
+ int prefetchFlags) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(
- accessibilityNodeId);
- if (cachedInfo != null) {
- return cachedInfo;
+ if (!bypassCache) {
+ AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(
+ accessibilityNodeId);
+ if (cachedInfo != null) {
+ return cachedInfo;
+ }
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
@@ -350,7 +354,7 @@
}
} catch (RemoteException re) {
if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote findAccessibilityFocus", re);
+ Log.w(LOG_TAG, "Error while calling remote findFocus", re);
}
}
return null;
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 732699b..04ce7e2 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -16,14 +16,17 @@
package android.view.accessibility;
+import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -132,29 +135,6 @@
}
/**
- * Creates the singleton AccessibilityManager to be shared across users. This
- * has to be called before the local AccessibilityManager is created to ensure
- * it registers itself in the system correctly.
- * <p>
- * Note: Calling this method requires INTERACT_ACROSS_USERS_FULL or
- * INTERACT_ACROSS_USERS permission.
- * </p>
- * @param context Context in which this manager operates.
- * @throws IllegalStateException if not called before the local
- * AccessibilityManager is instantiated.
- *
- * @hide
- */
- public static void createAsSharedAcrossUsers(Context context) {
- synchronized (sInstanceSync) {
- if (sInstance != null) {
- throw new IllegalStateException("AccessibilityManager already created.");
- }
- createSingletonInstance(context, UserHandle.USER_CURRENT);
- }
- }
-
- /**
* Get an AccessibilityManager instance (create one if necessary).
*
* @param context Context in which this manager operates.
@@ -164,25 +144,27 @@
public static AccessibilityManager getInstance(Context context) {
synchronized (sInstanceSync) {
if (sInstance == null) {
- createSingletonInstance(context, UserHandle.myUserId());
+ final int userId;
+ if (Binder.getCallingUid() == Process.SYSTEM_UID
+ || context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS)
+ == PackageManager.PERMISSION_GRANTED
+ || context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED) {
+ userId = UserHandle.USER_CURRENT;
+ } else {
+ userId = UserHandle.myUserId();
+ }
+ IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
+ sInstance = new AccessibilityManager(context, service, userId);
}
}
return sInstance;
}
/**
- * Creates the singleton instance.
- *
- * @param context Context in which this manager operates.
- * @param userId The user id under which to operate.
- */
- private static void createSingletonInstance(Context context, int userId) {
- IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
- IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
- sInstance = new AccessibilityManager(context, service, userId);
- }
-
- /**
* Create an instance.
*
* @param context A {@link Context}.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index d9c9b69..e924e8b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -21,6 +21,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.InputType;
import android.util.Pools.SynchronizedPool;
import android.util.SparseLongArray;
import android.view.View;
@@ -267,6 +268,23 @@
public static final int ACTION_SET_SELECTION = 0x00020000;
/**
+ * Action to expand an expandable node.
+ */
+ public static final int ACTION_EXPAND = 0x00040000;
+
+ /**
+ * Action to collapse an expandable node.
+ */
+ public static final int ACTION_COLLAPSE = 0x00080000;
+
+ /**
+ * Action to dismiss a dismissable node.
+ */
+ public static final int ACTION_DISMISS = 0x00100000;
+
+ // Action arguments
+
+ /**
* Argument for which movement granularity to be used when traversing the node text.
* <p>
* <strong>Type:</strong> int<br>
@@ -333,6 +351,8 @@
public static final String ACTION_ARGUMENT_SELECTION_END_INT =
"ACTION_ARGUMENT_SELECTION_END_INT";
+ // Focus types
+
/**
* The input focus.
*/
@@ -398,6 +418,20 @@
private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000;
+ private static final int BOOLEAN_PROPERTY_LIVE_REGION = 0x00002000;
+
+ private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 0x00004000;
+
+ private static final int BOOLEAN_PROPERTY_EXPANDABLE = 0x00008000;
+
+ private static final int BOOLEAN_PROPERTY_EXPANDED = 0x00010000;
+
+ private static final int BOOLEAN_PROPERTY_DISMISSABLE = 0x00020000;
+
+ private static final int BOOLEAN_PROPERTY_MULTI_LINE = 0x00040000;
+
+ private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00080000;
+
/**
* Bits that provide the id of a virtual descendant of a view.
*/
@@ -482,9 +516,16 @@
private int mTextSelectionStart = UNDEFINED;
private int mTextSelectionEnd = UNDEFINED;
+ private int mInputType = InputType.TYPE_NULL;
+
+ private Bundle mBundle;
private int mConnectionId = UNDEFINED;
+ private RangeInfo mRangeInfo;
+ private CollectionInfo mCollectionInfo;
+ private CollectionItemInfo mCollectionItemInfo;
+
/**
* Hide constructor from clients.
*/
@@ -594,16 +635,20 @@
* since it represents a view that is no longer in the view tree and should
* be recycled.
* </p>
+ *
+ * @param bypassCache Whether to bypass the cache.
* @return Whether the refresh succeeded.
+ *
+ * @hide
*/
- public boolean refresh() {
+ public boolean refresh(boolean bypassCache) {
enforceSealed();
if (!canPerformRequestOverConnection(mSourceNodeId)) {
return false;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId(
- mConnectionId, mWindowId, mSourceNodeId, 0);
+ mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0);
if (refreshedInfo == null) {
return false;
}
@@ -613,6 +658,19 @@
}
/**
+ * Refreshes this info with the latest state of the view it represents.
+ * <p>
+ * <strong>Note:</strong> If this method returns false this info is obsolete
+ * since it represents a view that is no longer in the view tree and should
+ * be recycled.
+ * </p>
+ * @return Whether the refresh succeeded.
+ */
+ public boolean refresh() {
+ return refresh(false);
+ }
+
+ /**
* @return The ids of the children.
*
* @hide
@@ -652,7 +710,7 @@
final long childId = mChildNodeIds.get(index);
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
- childId, FLAG_PREFETCH_DESCENDANTS);
+ childId, false, FLAG_PREFETCH_DESCENDANTS);
}
/**
@@ -878,7 +936,7 @@
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ mWindowId, mParentNodeId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
}
/**
@@ -1313,6 +1371,255 @@
}
/**
+ * Gets the collection info if the node is a collection. A collection
+ * child is always a collection item.
+ *
+ * @return The collection info.
+ */
+ public CollectionInfo getCollectionInfo() {
+ return mCollectionInfo;
+ }
+
+ /**
+ * Sets the collection info if the node is a collection. A collection
+ * child is always a collection item.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param collectionInfo The collection info.
+ */
+ public void setCollectionInfo(CollectionInfo collectionInfo) {
+ enforceNotSealed();
+ mCollectionInfo = collectionInfo;
+ }
+
+ /**
+ * Gets the collection item info if the node is a collection item. A collection
+ * item is always a child of a collection.
+ *
+ * @return The collection item info.
+ */
+ public CollectionItemInfo getCollectionItemInfo() {
+ return mCollectionItemInfo;
+ }
+
+ /**
+ * Sets the collection item info if the node is a collection item. A collection
+ * item is always a child of a collection.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @return collectionItem True if the node is an item.
+ */
+ public void setCollectionItemInfo(CollectionItemInfo collectionItemInfo) {
+ enforceNotSealed();
+ mCollectionItemInfo = collectionItemInfo;
+ }
+
+ /**
+ * Gets the range info if this node is a range.
+ *
+ * @return The range.
+ */
+ public RangeInfo getRangeInfo() {
+ return mRangeInfo;
+ }
+
+ /**
+ * Sets the range info if this node is a range.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param rangeInfo The range info.
+ */
+ public void setRangeInfo(RangeInfo rangeInfo) {
+ enforceNotSealed();
+ mRangeInfo = rangeInfo;
+ }
+
+ /**
+ * Gets if the content of this node is invalid. For example,
+ * a date is not well-formed.
+ *
+ * @return If the node content is invalid.
+ */
+ public boolean isContentInvalid() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID);
+ }
+
+ /**
+ * Sets if the content of this node is invalid. For example,
+ * a date is not well-formed.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param contentInvalid If the node content is invalid.
+ */
+ public void setContentInvalid(boolean contentInvalid) {
+ setBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID, contentInvalid);
+ }
+
+ /**
+ * Gets if the node is a live region for whose changes the user
+ * should be notified. It is the responsibility of the accessibility
+ * service to monitor this region and notify the user if it changes.
+ *
+ * @return If the node is a live region.
+ */
+ public boolean isLiveRegion() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_LIVE_REGION);
+ }
+
+ /**
+ * Sets if the node is a live region for whose changes the user
+ * should be notified. It is the responsibility of the accessibility
+ * service to monitor this region and notify the user if it changes.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param liveRegion If the node is a live region.
+ */
+ public void setLiveRegion(boolean liveRegion) {
+ enforceNotSealed();
+ setBooleanProperty(BOOLEAN_PROPERTY_LIVE_REGION, liveRegion);
+ }
+
+ /**
+ * Gets if the node is a multi line editable text.
+ *
+ * @return True if the node is multi line.
+ */
+ public boolean isMultiLine() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE);
+ }
+
+ /**
+ * Sets if the node is a multi line editable text.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param multiLine True if the node is multi line.
+ */
+ public void setMultiLine(boolean multiLine) {
+ enforceNotSealed();
+ setBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE, multiLine);
+ }
+
+ /**
+ * Gets if this node opens a popup or a dialog.
+ *
+ * @return If the the node opens a popup.
+ */
+ public boolean getOpensPopup() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP);
+ }
+
+ /**
+ * Sets if this node opens a popup or a dialog.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param opensPopup If the the node opens a popup.
+ */
+ public void setOpensPopup(boolean opensPopup) {
+ enforceNotSealed();
+ setBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP, opensPopup);
+ }
+
+ /**
+ * Gets if the node can be expanded.
+ *
+ * @return If the node can be expanded.
+ */
+ public boolean isExpandable() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_EXPANDABLE);
+ }
+
+ /**
+ * Sets if the node can be expanded.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param expandable If the node can be expanded.
+ */
+ public void setExpandable(boolean expandable) {
+ enforceNotSealed();
+ setBooleanProperty(BOOLEAN_PROPERTY_EXPANDABLE, expandable);
+ }
+
+ /**
+ * Gets if the node is expanded.
+ *
+ * @return If the node is expanded.
+ */
+ public boolean isExpanded() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_EXPANDED);
+ }
+
+ /**
+ * Sets if the node is expanded.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param expanded If the node is expanded.
+ */
+ public void setExpanded(boolean expanded) {
+ enforceNotSealed();
+ setBooleanProperty(BOOLEAN_PROPERTY_EXPANDED, expanded);
+ }
+
+ /**
+ * Gets if the node can be dismissed.
+ *
+ * @return If the node can be dismissed.
+ */
+ public boolean isDismissable() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE);
+ }
+
+ /**
+ * Sets if the node can be dismissed.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param dismissable If the node can be dismissed.
+ */
+ public void setDismissable(boolean dismissable) {
+ enforceNotSealed();
+ setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable);
+ }
+
+ /**
* Gets the package this node comes from.
*
* @return The package name.
@@ -1470,7 +1777,7 @@
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mLabelForId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ mWindowId, mLabelForId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
}
/**
@@ -1527,7 +1834,7 @@
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mLabeledById, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ mWindowId, mLabeledById, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
}
/**
@@ -1600,6 +1907,52 @@
}
/**
+ * Gets the input type of the source as defined by {@link InputType}.
+ *
+ * @return The input type.
+ */
+ public int getInputType() {
+ return mInputType;
+ }
+
+ /**
+ * Sets the input type of the source as defined by {@link InputType}.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an
+ * AccessibilityService.
+ * </p>
+ *
+ * @param inputType The input type.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setInputType(int inputType) {
+ mInputType = inputType;
+ }
+
+ /**
+ * Gets an optional bundle with additional data. The bundle
+ * is lazily created and never <code>null</code>.
+ * <p>
+ * <strong>Note:</strong> It is recommended to use the package
+ * name of your application as a prefix for the keys to avoid
+ * collisions which may confuse an accessibility service if the
+ * same key has different meaning when emitted from different
+ * applications.
+ * </p>
+ *
+ * @return The bundle.
+ */
+ public Bundle getBundle() {
+ if (mBundle == null) {
+ mBundle = new Bundle();
+ }
+ return mBundle;
+ }
+
+ /**
* Gets the value of a boolean property.
*
* @param property The property.
@@ -1845,6 +2198,44 @@
parcel.writeInt(mTextSelectionStart);
parcel.writeInt(mTextSelectionEnd);
+ parcel.writeInt(mInputType);
+
+ if (mBundle != null) {
+ parcel.writeInt(1);
+ parcel.writeBundle(mBundle);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ if (mRangeInfo != null) {
+ parcel.writeInt(1);
+ parcel.writeInt(mRangeInfo.getType());
+ parcel.writeFloat(mRangeInfo.getMin());
+ parcel.writeFloat(mRangeInfo.getMax());
+ parcel.writeFloat(mRangeInfo.getCurrent());
+ } else {
+ parcel.writeInt(0);
+ }
+
+ if (mCollectionInfo != null) {
+ parcel.writeInt(1);
+ parcel.writeInt(mCollectionInfo.getHorizontalSize());
+ parcel.writeInt(mCollectionInfo.getVerticalSize());
+ parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ if (mCollectionItemInfo != null) {
+ parcel.writeInt(1);
+ parcel.writeInt(mCollectionItemInfo.getHorizontalPosition());
+ parcel.writeInt(mCollectionItemInfo.getHorizontalSpan());
+ parcel.writeInt(mCollectionItemInfo.getVerticalPosition());
+ parcel.writeInt(mCollectionItemInfo.getVerticalSpan());
+ parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0);
+ } else {
+ parcel.writeInt(0);
+ }
// Since instances of this class are fetched via synchronous i.e. blocking
// calls in IPCs we always recycle as soon as the instance is marshaled.
@@ -1876,10 +2267,17 @@
mMovementGranularities = other.mMovementGranularities;
final int otherChildIdCount = other.mChildNodeIds.size();
for (int i = 0; i < otherChildIdCount; i++) {
- mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
+ mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
}
mTextSelectionStart = other.mTextSelectionStart;
mTextSelectionEnd = other.mTextSelectionEnd;
+ mInputType = other.mInputType;
+ if (other.mBundle != null && !other.mBundle.isEmpty()) {
+ getBundle().putAll(other.mBundle);
+ }
+ mRangeInfo = other.mRangeInfo;
+ mCollectionInfo = other.mCollectionInfo;
+ mCollectionItemInfo = other.mCollectionItemInfo;
}
/**
@@ -1927,6 +2325,36 @@
mTextSelectionStart = parcel.readInt();
mTextSelectionEnd = parcel.readInt();
+
+ mInputType = parcel.readInt();
+
+ if (parcel.readInt() == 1) {
+ getBundle().putAll(parcel.readBundle());
+ }
+
+ if (parcel.readInt() == 1) {
+ mRangeInfo = RangeInfo.obtain(
+ parcel.readInt(),
+ parcel.readFloat(),
+ parcel.readFloat(),
+ parcel.readFloat());
+ }
+
+ if (parcel.readInt() == 1) {
+ mCollectionInfo = CollectionInfo.obtain(
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readInt() == 1);
+ }
+
+ if (parcel.readInt() == 1) {
+ mCollectionItemInfo = CollectionItemInfo.obtain(
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readInt() == 1);
+ }
}
/**
@@ -1953,6 +2381,22 @@
mActions = 0;
mTextSelectionStart = UNDEFINED;
mTextSelectionEnd = UNDEFINED;
+ mInputType = InputType.TYPE_NULL;
+ if (mBundle != null) {
+ mBundle.clear();
+ }
+ if (mRangeInfo != null) {
+ mRangeInfo.recycle();
+ mRangeInfo = null;
+ }
+ if (mCollectionInfo != null) {
+ mCollectionInfo.recycle();
+ mCollectionInfo = null;
+ }
+ if (mCollectionItemInfo != null) {
+ mCollectionItemInfo.recycle();
+ mCollectionItemInfo = null;
+ }
}
/**
@@ -2132,6 +2576,311 @@
}
/**
+ * Class with information if a node is a range. Use
+ * {@link RangeInfo#obtain(int, float, float, float) to get an instance.
+ */
+ public static final class RangeInfo {
+ private static final int MAX_POOL_SIZE = 10;
+
+ /** Range type: integer. */
+ public static final int RANGE_TYPE_INT = 0;
+ /** Range type: float. */
+ public static final int RANGE_TYPE_FLOAT = 1;
+ /** Range type: percent with values from zero to one.*/
+ public static final int RANGE_TYPE_PERCENT = 2;
+
+ private static final SynchronizedPool<RangeInfo> sPool =
+ new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE);
+
+ private int mType;
+ private float mMin;
+ private float mMax;
+ private float mCurrent;
+
+ /**
+ * Obtains a pooled instance.
+ *
+ * @param type The type of the range.
+ * @param min The min value.
+ * @param max The max value.
+ * @param current The current value.
+ */
+ public static RangeInfo obtain(int type, float min, float max, float current) {
+ RangeInfo info = sPool.acquire();
+ return (info != null) ? info : new RangeInfo(type, min, max, current);
+ }
+
+ /**
+ * Creates a new range.
+ *
+ * @param type The type of the range.
+ * @param min The min value.
+ * @param max The max value.
+ * @param current The current value.
+ */
+ private RangeInfo(int type, float min, float max, float current) {
+ mType = type;
+ mMin = min;
+ mMax = max;
+ mCurrent = current;
+ }
+
+ /**
+ * Gets the range type.
+ *
+ * @return The range type.
+ *
+ * @see #RANGE_TYPE_INT
+ * @see #RANGE_TYPE_FLOAT
+ * @see #RANGE_TYPE_PERCENT
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Gets the min value.
+ *
+ * @return The min value.
+ */
+ public float getMin() {
+ return mMin;
+ }
+
+ /**
+ * Gets the max value.
+ *
+ * @return The max value.
+ */
+ public float getMax() {
+ return mMax;
+ }
+
+ /**
+ * Gets the current value.
+ *
+ * @return The current value.
+ */
+ public float getCurrent() {
+ return mCurrent;
+ }
+
+ /**
+ * Recycles this instance.
+ */
+ void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ private void clear() {
+ mType = 0;
+ mMin = 0;
+ mMax = 0;
+ mCurrent = 0;
+ }
+ }
+
+ /**
+ * Class with information if a node is a collection. Use
+ * {@link CollectionInfo#obtain(int, int, boolean) to an instance.
+ */
+ public static final class CollectionInfo {
+ private static final int MAX_POOL_SIZE = 20;
+
+ private static final SynchronizedPool<CollectionInfo> sPool =
+ new SynchronizedPool<CollectionInfo>(MAX_POOL_SIZE);
+
+ private int mHorizontalSize;
+ private int mVerticalSize;
+ private boolean mHierarchical;
+
+ /**
+ * Obtains a pooled instance.
+ *
+ * @param horizontalSize The horizontal size.
+ * @param verticalSize The vertical size.
+ * @param hierarchical Whether the collection is hierarchical.
+ */
+ public static CollectionInfo obtain(int horizontalSize, int verticalSize,
+ boolean hierarchical) {
+ CollectionInfo info = sPool.acquire();
+ return (info != null) ? info : new CollectionInfo(horizontalSize,
+ verticalSize, hierarchical);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param horizontalSize The horizontal size.
+ * @param verticalSize The vertical size.
+ * @param hierarchical Whether the collection is hierarchical.
+ */
+ private CollectionInfo(int horizontalSize, int verticalSize,
+ boolean hierarchical) {
+ mHorizontalSize = horizontalSize;
+ mVerticalSize = verticalSize;
+ mHierarchical = hierarchical;
+ }
+
+ /**
+ * Gets the horizontal size in terms of item positions.
+ *
+ * @return The size.
+ */
+ public int getHorizontalSize() {
+ return mHorizontalSize;
+ }
+
+ /**
+ * Gets the vertical size in terms of item positions.
+ *
+ * @return The size.
+ */
+ public int getVerticalSize() {
+ return mVerticalSize;
+ }
+
+ /**
+ * Gets if the collection is a hierarchically ordered.
+ *
+ * @return Whether the collection is hierarchical.
+ */
+ public boolean isHierarchical() {
+ return mHierarchical;
+ }
+
+ /**
+ * Recycles this instance.
+ */
+ void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ private void clear() {
+ mHorizontalSize = 0;
+ mVerticalSize = 0;
+ mHierarchical = false;
+ }
+ }
+
+ /**
+ * Class with information if a node is a collection item. Use
+ * {@link CollectionItemInfo#obtain(int, int, int, int, boolean) to get an instance.
+ */
+ public static final class CollectionItemInfo {
+ private static final int MAX_POOL_SIZE = 20;
+
+ private static final SynchronizedPool<CollectionItemInfo> sPool =
+ new SynchronizedPool<CollectionItemInfo>(MAX_POOL_SIZE);
+
+ /**
+ * Obtains a pooled instance.
+ *
+ * @param horizontalPosition The horizontal item position.
+ * @param horizontalSpan The horizontal item span.
+ * @param verticalPosition The vertical item position.
+ * @param verticalSpan The vertical item span.
+ * @param heading Whether the item is a heading.
+ */
+ public static CollectionItemInfo obtain(int horizontalPosition, int horizontalSpan,
+ int verticalPosition, int verticalSpan, boolean heading) {
+ CollectionItemInfo info = sPool.acquire();
+ return (info != null) ? info : new CollectionItemInfo(horizontalPosition,
+ horizontalSpan, verticalPosition, verticalSpan, heading);
+ }
+
+ private boolean mHeading;
+ private int mHorizontalPosition;
+ private int mVerticalPosition;
+ private int mHorizontalSpan;
+ private int mVerticalSpan;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param horizontalPosition The horizontal item position.
+ * @param horizontalSpan The horizontal item span.
+ * @param verticalPosition The vertical item position.
+ * @param verticalSpan The vertical item span.
+ * @param heading Whether the item is a heading.
+ */
+ private CollectionItemInfo(int horizontalPosition, int horizontalSpan,
+ int verticalPosition, int verticalSpan, boolean heading) {
+ mHorizontalPosition = horizontalPosition;
+ mHorizontalSpan = horizontalSpan;
+ mVerticalPosition = verticalPosition;
+ mVerticalSpan = verticalSpan;
+ mHeading = heading;
+ }
+
+ /**
+ * Gets the horizontal item position in the parent collection.
+ *
+ * @return The position.
+ */
+ public int getHorizontalPosition() {
+ return mHorizontalPosition;
+ }
+
+ /**
+ * Gets the vertical item position in the parent collection.
+ *
+ * @return The position.
+ */
+ public int getVerticalPosition() {
+ return mVerticalPosition;
+ }
+
+ /**
+ * Gets the horizontal span in terms of item positions
+ * of the parent collection.
+ *
+ * @return The span.
+ */
+ public int getHorizontalSpan() {
+ return mHorizontalSpan;
+ }
+
+ /**
+ * Gets the vertical span in terms of item positions
+ * of the parent collection.
+ *
+ * @return The span.
+ */
+ public int getVerticalSpan() {
+ return mVerticalSpan;
+ }
+
+ /**
+ * Gets if the collection item is a heading. For example, section
+ * heading, table header, etc.
+ *
+ * @return If the item is a heading.
+ */
+ public boolean isHeading() {
+ return mHeading;
+ }
+
+ /**
+ * Recycles this instance.
+ */
+ void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ private void clear() {
+ mHorizontalPosition = 0;
+ mHorizontalSpan = 0;
+ mVerticalPosition = 0;
+ mVerticalSpan = 0;
+ mHeading = false;
+ }
+ }
+
+ /**
* @see Parcelable.Creator
*/
public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
index 28518aa..dded74c 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
@@ -39,9 +39,9 @@
private static final boolean ENABLED = true;
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
- private static final boolean CHECK_INTEGRITY = true;
+ private static final boolean CHECK_INTEGRITY_IF_DEBUGGABLE_BUILD = true;
private final Object mLock = new Object();
@@ -67,16 +67,12 @@
if (ENABLED) {
final int eventType = event.getEventType();
switch (eventType) {
- case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
- // New window so we clear the cache.
- mWindowId = event.getWindowId();
- clear();
- } break;
+ case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
final int windowId = event.getWindowId();
+ // If a new window, we clear the cache.
if (mWindowId != windowId) {
- // New window so we clear the cache.
mWindowId = windowId;
clear();
}
@@ -87,34 +83,48 @@
case AccessibilityEvent.TYPE_VIEW_SELECTED:
case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
- // Since we prefetch the descendants of a node we
- // just remove the entire subtree since when the node
- // is fetched we will gets its descendant anyway.
+ refreshCachedNode(event.getSourceNodeId());
+ } break;
+ case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
+ clearSubTreeLocked(event.getSourceNodeId());
+ } break;
+ case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
synchronized (mLock) {
final long sourceId = event.getSourceNodeId();
- clearSubTreeLocked(sourceId);
- if (eventType == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
- clearSubtreeWithOldInputFocusLocked(sourceId);
+ if (event.getContentChangeType()
+ == AccessibilityEvent.CONTENT_CHANGE_TYPE_NODE) {
+ refreshCachedNode(sourceId);
+ } else {
+ clearSubTreeLocked(sourceId);
}
- if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
- clearSubtreeWithOldAccessibilityFocusLocked(sourceId);
- }
- }
- } break;
- case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
- case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
- synchronized (mLock) {
- final long accessibilityNodeId = event.getSourceNodeId();
- clearSubTreeLocked(accessibilityNodeId);
}
} break;
}
- if (Build.IS_DEBUGGABLE && CHECK_INTEGRITY) {
+ if (Build.IS_DEBUGGABLE && CHECK_INTEGRITY_IF_DEBUGGABLE_BUILD) {
checkIntegrity();
}
}
}
+ private void refreshCachedNode(long sourceId) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Refresing cached node.");
+ }
+ synchronized (mLock) {
+ AccessibilityNodeInfo cachedInfo = mCacheImpl.get(sourceId);
+ // If the source is not in the cache - nothing to do.
+ if (cachedInfo == null) {
+ return;
+ }
+ // The node changed so we will just refresh it right now.
+ if (cachedInfo.refresh(false)) {
+ return;
+ }
+ // Weird, we could not refresh. Just evict the entire sub-tree.
+ clearSubTreeLocked(sourceId);
+ }
+ }
+
/**
* Gets a cached {@link AccessibilityNodeInfo} given its accessibility node id.
*
@@ -131,7 +141,7 @@
info = AccessibilityNodeInfo.obtain(info);
}
if (DEBUG) {
- Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
+// Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
}
return info;
}
@@ -149,7 +159,7 @@
if (ENABLED) {
synchronized(mLock) {
if (DEBUG) {
- Log.i(LOG_TAG, "add(" + info + ")");
+// Log.i(LOG_TAG, "add(" + info + ")");
}
final long sourceId = info.getSourceNodeId();
@@ -212,6 +222,13 @@
* @param rootNodeId The root id.
*/
private void clearSubTreeLocked(long rootNodeId) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Clearing cached subtree.");
+ }
+ clearSubTreeRecursiveLocked(rootNodeId);
+ }
+
+ private void clearSubTreeRecursiveLocked(long rootNodeId) {
AccessibilityNodeInfo current = mCacheImpl.get(rootNodeId);
if (current == null) {
return;
@@ -221,41 +238,7 @@
final int childCount = childNodeIds.size();
for (int i = 0; i < childCount; i++) {
final long childNodeId = childNodeIds.valueAt(i);
- clearSubTreeLocked(childNodeId);
- }
- }
-
- /**
- * We are enforcing the invariant for a single input focus.
- *
- * @param currentInputFocusId The current input focused node.
- */
- private void clearSubtreeWithOldInputFocusLocked(long currentInputFocusId) {
- final int cacheSize = mCacheImpl.size();
- for (int i = 0; i < cacheSize; i++) {
- AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
- final long infoSourceId = info.getSourceNodeId();
- if (infoSourceId != currentInputFocusId && info.isFocused()) {
- clearSubTreeLocked(infoSourceId);
- return;
- }
- }
- }
-
- /**
- * We are enforcing the invariant for a single accessibility focus.
- *
- * @param currentAccessibilityFocusId The current input focused node.
- */
- private void clearSubtreeWithOldAccessibilityFocusLocked(long currentAccessibilityFocusId) {
- final int cacheSize = mCacheImpl.size();
- for (int i = 0; i < cacheSize; i++) {
- AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
- final long infoSourceId = info.getSourceNodeId();
- if (infoSourceId != currentAccessibilityFocusId && info.isAccessibilityFocused()) {
- clearSubTreeLocked(infoSourceId);
- return;
- }
+ clearSubTreeRecursiveLocked(childNodeId);
}
}
@@ -327,16 +310,17 @@
}
// Check for disconnected nodes or ones from another window.
- final int cacheSize = mCacheImpl.size();
- for (int i = 0; i < cacheSize; i++) {
+ for (int i = 0; i < mCacheImpl.size(); i++) {
AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
if (!seen.contains(info)) {
if (info.getWindowId() == windowId) {
- Log.e(LOG_TAG, "Disconneced node: ");
+ Log.e(LOG_TAG, "Disconneced node: " + info);
} else {
Log.e(LOG_TAG, "Node from: " + info.getWindowId() + " not from:"
+ windowId + " " + info);
}
+ mCacheImpl.removeAt(i);
+ i--;
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 7147c57..3fcd218 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -164,7 +164,7 @@
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId,
- mSourceNodeId, GET_SOURCE_PREFETCH_FLAGS);
+ mSourceNodeId, false, GET_SOURCE_PREFETCH_FLAGS);
}
/**
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index e8c1d23..890909b 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -29,19 +29,19 @@
/**
* Indicates a transformation that has no effect (alpha = 1 and identity matrix.)
*/
- public static int TYPE_IDENTITY = 0x0;
+ public static final int TYPE_IDENTITY = 0x0;
/**
* Indicates a transformation that applies an alpha only (uses an identity matrix.)
*/
- public static int TYPE_ALPHA = 0x1;
+ public static final int TYPE_ALPHA = 0x1;
/**
* Indicates a transformation that applies a matrix only (alpha = 1.)
*/
- public static int TYPE_MATRIX = 0x2;
+ public static final int TYPE_MATRIX = 0x2;
/**
* Indicates a transformation that applies an alpha and a matrix.
*/
- public static int TYPE_BOTH = TYPE_ALPHA | TYPE_MATRIX;
+ public static final int TYPE_BOTH = TYPE_ALPHA | TYPE_MATRIX;
protected Matrix mMatrix;
protected float mAlpha;
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index d6b973e..f730cf7 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -269,8 +269,9 @@
if (content != null) {
beginBatchEdit();
removeComposingSpans(content);
- endBatchEdit();
+ // Note: sendCurrentText does nothing unless mDummyMode is set
sendCurrentText();
+ endBatchEdit();
}
return true;
}
@@ -467,8 +468,9 @@
content.setSpan(COMPOSING, a, b,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
- endBatchEdit();
+ // Note: sendCurrentText does nothing unless mDummyMode is set
sendCurrentText();
+ endBatchEdit();
}
return true;
}
diff --git a/core/java/android/view/transition/AutoTransition.java b/core/java/android/view/transition/AutoTransition.java
new file mode 100644
index 0000000..7ddac7e
--- /dev/null
+++ b/core/java/android/view/transition/AutoTransition.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+/**
+ * Utility class for creating a default transition that automatically fades,
+ * moves, and resizes views during a scene change.
+ */
+public class AutoTransition extends TransitionGroup {
+
+ /**
+ * Constructs an AutoTransition object, which is a TransitionGroup which
+ * first fades out disappearing targets, then moves and resizes existing
+ * targets, and finally fades in appearing targets.
+ *
+ */
+ public AutoTransition() {
+ setOrdering(SEQUENTIALLY);
+ addTransitions(new Fade(Fade.OUT), new Move(), new Fade(Fade.IN));
+ }
+}
diff --git a/core/java/android/view/transition/Crossfade.java b/core/java/android/view/transition/Crossfade.java
new file mode 100644
index 0000000..7cfba70
--- /dev/null
+++ b/core/java/android/view/transition/Crossfade.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOverlay;
+
+import java.util.Map;
+
+/**
+ * This transition captures bitmap representations of target views before and
+ * after the scene change and fades between them.
+ *
+ * <p>Note: This transition is not compatible with {@link TextureView}
+ * or {@link SurfaceView}.</p>
+ */
+public class Crossfade extends Transition {
+ // TODO: Add a hook that lets a Transition call user code to query whether it should run on
+ // a given target view. This would save bitmap comparisons in this transition, for example.
+
+ private static final String LOG_TAG = "Crossfade";
+
+ private static final String PROPNAME_BITMAP = "android:crossfade:bitmap";
+ private static final String PROPNAME_DRAWABLE = "android:crossfade:drawable";
+ private static final String PROPNAME_BOUNDS = "android:crossfade:bounds";
+
+ private static RectEvaluator sRectEvaluator = new RectEvaluator();
+
+ private int mFadeBehavior = FADE_BEHAVIOR_REVEAL;
+ private int mResizeBehavior = RESIZE_BEHAVIOR_SCALE;
+
+ /**
+ * Flag specifying that the fading animation should cross-fade
+ * between the old and new representation of all affected target
+ * views. This means that the old representation will fade out
+ * while the new one fades in. This effect may work well on views
+ * without solid backgrounds, such as TextViews.
+ *
+ * @see #setFadeBehavior(int)
+ */
+ public static final int FADE_BEHAVIOR_CROSSFADE = 0;
+ /**
+ * Flag specifying that the fading animation should reveal the
+ * new representation of all affected target views. This means
+ * that the old representation will fade out, gradually
+ * revealing the new representation, which remains opaque
+ * the whole time. This effect may work well on views
+ * with solid backgrounds, such as ImageViews.
+ *
+ * @see #setFadeBehavior(int)
+ */
+ public static final int FADE_BEHAVIOR_REVEAL = 1;
+
+ /**
+ * Flag specifying that the transition should not animate any
+ * changes in size between the old and new target views.
+ * This means that no scaling will take place as a result of
+ * this transition
+ *
+ * @see #setResizeBehavior(int)
+ */
+ public static final int RESIZE_BEHAVIOR_NONE = 0;
+ /**
+ * Flag specifying that the transition should animate any
+ * changes in size between the old and new target views.
+ * This means that the animation will scale the start/end
+ * representations of affected views from the starting size
+ * to the ending size over the course of the animation.
+ * This effect may work well on images, but is not recommended
+ * for text.
+ *
+ * @see #setResizeBehavior(int)
+ */
+ public static final int RESIZE_BEHAVIOR_SCALE = 1;
+
+ // TODO: Add fade/resize behaviors to xml resources
+
+ /**
+ * Sets the type of fading animation that will be run, one of
+ * {@link #FADE_BEHAVIOR_CROSSFADE} and {@link #FADE_BEHAVIOR_REVEAL}.
+ *
+ * @param fadeBehavior The type of fading animation to use when this
+ * transition is run.
+ */
+ public void setFadeBehavior(int fadeBehavior) {
+ if (fadeBehavior >= FADE_BEHAVIOR_CROSSFADE && fadeBehavior <= FADE_BEHAVIOR_REVEAL) {
+ mFadeBehavior = fadeBehavior;
+ }
+ }
+
+ public int getFadeBehavior() {
+ return mFadeBehavior;
+ }
+
+ /**
+ * Sets the type of resizing behavior that will be used during the
+ * transition animation, one of {@link #RESIZE_BEHAVIOR_NONE} and
+ * {@link #RESIZE_BEHAVIOR_SCALE}.
+ *
+ * @param resizeBehavior The type of resizing behavior to use when this
+ * transition is run.
+ */
+ public void setResizeBehavior(int resizeBehavior) {
+ if (resizeBehavior >= RESIZE_BEHAVIOR_NONE && resizeBehavior <= RESIZE_BEHAVIOR_SCALE) {
+ mResizeBehavior = resizeBehavior;
+ }
+ }
+
+ public int getResizeBehavior() {
+ return mResizeBehavior;
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ final View view = endValues.view;
+ Map<String, Object> startVals = startValues.values;
+ Map<String, Object> endVals = endValues.values;
+ Rect startBounds = (Rect) startVals.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endVals.get(PROPNAME_BOUNDS);
+ Bitmap startBitmap = (Bitmap) startVals.get(PROPNAME_BITMAP);
+ Bitmap endBitmap = (Bitmap) endVals.get(PROPNAME_BITMAP);
+ final BitmapDrawable startDrawable = (BitmapDrawable) startVals.get(PROPNAME_DRAWABLE);
+ final BitmapDrawable endDrawable = (BitmapDrawable) endVals.get(PROPNAME_DRAWABLE);
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "StartBitmap.sameAs(endBitmap) = " + startBitmap.sameAs(endBitmap) +
+ " for start, end: " + startBitmap + ", " + endBitmap);
+ }
+ if (startDrawable != null && endDrawable != null && !startBitmap.sameAs(endBitmap)) {
+ ViewOverlay overlay = (mFadeBehavior == FADE_BEHAVIOR_REVEAL) ?
+ view.getOverlay() : ((ViewGroup) view.getParent()).getOverlay();
+ if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
+ overlay.add(endDrawable);
+ }
+ overlay.add(startDrawable);
+ // The transition works by placing the end drawable under the start drawable and
+ // gradually fading out the start drawable. So it's not really a cross-fade, but rather
+ // a reveal of the end scene over time. Also, animate the bounds of both drawables
+ // to mimic the change in the size of the view itself between scenes.
+ ObjectAnimator anim = ObjectAnimator.ofInt(startDrawable, "alpha", 0);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ // TODO: some way to auto-invalidate views based on drawable changes? callbacks?
+ view.invalidate(startDrawable.getBounds());
+ }
+ });
+ ObjectAnimator anim1 = null;
+ if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
+ anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
+ }
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Crossfade: created anim " + anim + " for start, end values " +
+ startValues + ", " + endValues);
+ }
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ViewOverlay overlay = (mFadeBehavior == FADE_BEHAVIOR_REVEAL) ?
+ view.getOverlay() : ((ViewGroup) view.getParent()).getOverlay();
+ overlay.remove(startDrawable);
+ if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
+ overlay.remove(endDrawable);
+ }
+ }
+ });
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(anim);
+ if (anim1 != null) {
+ set.playTogether(anim1);
+ }
+ if (mResizeBehavior == RESIZE_BEHAVIOR_SCALE && !startBounds.equals(endBounds)) {
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "animating from startBounds to endBounds: " +
+ startBounds + ", " + endBounds);
+ }
+ Animator anim2 = ObjectAnimator.ofObject(startDrawable, "bounds",
+ sRectEvaluator, startBounds, endBounds);
+ set.playTogether(anim2);
+ if (mResizeBehavior == RESIZE_BEHAVIOR_SCALE) {
+ // TODO: How to handle resizing with a CROSSFADE (vs. REVEAL) effect
+ // when we are animating the view directly?
+ Animator anim3 = ObjectAnimator.ofObject(endDrawable, "bounds",
+ sRectEvaluator, startBounds, endBounds);
+ set.playTogether(anim3);
+ }
+ }
+ return set;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ View view = values.view;
+ Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
+ bounds.offset(view.getLeft(), view.getTop());
+ }
+ values.values.put(PROPNAME_BOUNDS, bounds);
+
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Captured bounds " + values.values.get(PROPNAME_BOUNDS) + ": start = " +
+ start);
+ }
+ Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ if (view instanceof TextureView) {
+ bitmap = ((TextureView) view).getBitmap();
+ } else {
+ Canvas c = new Canvas(bitmap);
+ view.draw(c);
+ }
+ values.values.put(PROPNAME_BITMAP, bitmap);
+ // TODO: I don't have resources, can't call the non-deprecated method?
+ BitmapDrawable drawable = new BitmapDrawable(bitmap);
+ // TODO: lrtb will be wrong if the view has transXY set
+ drawable.setBounds(bounds);
+ values.values.put(PROPNAME_DRAWABLE, drawable);
+ }
+
+}
diff --git a/core/java/android/view/transition/Fade.java b/core/java/android/view/transition/Fade.java
new file mode 100644
index 0000000..c2aa90b
--- /dev/null
+++ b/core/java/android/view/transition/Fade.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This transition tracks changes to the visibility of target views in the
+ * start and end scenes and fades views in or out when they become visible
+ * or non-visible. Visibility is determined by both the
+ * {@link View#setVisibility(int)} state of the view as well as whether it
+ * is parented in the current view hierarchy.
+ */
+public class Fade extends Visibility {
+
+ private static final String LOG_TAG = "Fade";
+ private static final String PROPNAME_SCREEN_X = "android:fade:screenX";
+ private static final String PROPNAME_SCREEN_Y = "android:fade:screenY";
+
+ /**
+ * Fading mode used in {@link #Fade(int)} to make the transition
+ * operate on targets that are appearing. Maybe be combined with
+ * {@link #OUT} to fade both in and out.
+ */
+ public static final int IN = 0x1;
+ /**
+ * Fading mode used in {@link #Fade(int)} to make the transition
+ * operate on targets that are disappearing. Maybe be combined with
+ * {@link #IN} to fade both in and out.
+ */
+ public static final int OUT = 0x2;
+
+ private int mFadingMode;
+
+ /**
+ * Constructs a Fade transition that will fade targets in and out.
+ */
+ public Fade() {
+ this(IN | OUT);
+ }
+
+ /**
+ * Constructs a Fade transition that will fade targets in
+ * and/or out, according to the value of fadingMode.
+ *
+ * @param fadingMode The behavior of this transition, a combination of
+ * {@link #IN} and {@link #OUT}.
+ */
+ public Fade(int fadingMode) {
+ mFadingMode = fadingMode;
+ }
+
+ /**
+ * Utility method to handle creating and running the Animator.
+ */
+ private Animator runAnimation(View view, float startAlpha, float endAlpha,
+ Animator.AnimatorListener listener) {
+ final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha);
+ if (listener != null) {
+ anim.addListener(listener);
+ }
+ // TODO: Maybe extract a method into Transition to run an animation that handles the
+ // duration/startDelay stuff for all subclasses.
+ return anim;
+ }
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ super.captureValues(values, start);
+ int[] loc = new int[2];
+ values.view.getLocationOnScreen(loc);
+ values.values.put(PROPNAME_SCREEN_X, loc[0]);
+ values.values.put(PROPNAME_SCREEN_Y, loc[1]);
+ }
+
+ @Override
+ protected Animator appear(ViewGroup sceneRoot,
+ TransitionValues startValues, int startVisibility,
+ TransitionValues endValues, int endVisibility) {
+ if ((mFadingMode & IN) != IN || endValues == null) {
+ return null;
+ }
+ final View endView = endValues.view;
+ endView.setAlpha(0);
+ final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Always end animation with full alpha, in case it's canceled mid-stream
+ endView.setAlpha(1);
+ }
+ };
+ return runAnimation(endView, 0, 1, endListener);
+ }
+
+ @Override
+ protected Animator disappear(ViewGroup sceneRoot,
+ TransitionValues startValues, int startVisibility,
+ TransitionValues endValues, int endVisibility) {
+ if ((mFadingMode & OUT) != OUT) {
+ return null;
+ }
+ View view;
+ View startView = (startValues != null) ? startValues.view : null;
+ View endView = (endValues != null) ? endValues.view : null;
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Fade.predisappear: startView, startVis, endView, endVis = " +
+ startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
+ }
+ View overlayView = null;
+ View viewToKeep = null;
+ if (endView == null) {
+ // view was removed: add the start view to the Overlay
+ view = startView;
+ overlayView = view;
+ } else {
+ // visibility change
+ if (endVisibility == View.INVISIBLE) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ // Becoming GONE
+ if (startView == endView) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ view = startView;
+ overlayView = view;
+ }
+ }
+ }
+ final int finalVisibility = endVisibility;
+ // TODO: add automatic facility to Visibility superclass for keeping views around
+ if (overlayView != null) {
+ // TODO: Need to do this for general case of adding to overlay
+ int screenX = (Integer) startValues.values.get(PROPNAME_SCREEN_X);
+ int screenY = (Integer) startValues.values.get(PROPNAME_SCREEN_Y);
+ int[] loc = new int[2];
+ sceneRoot.getLocationOnScreen(loc);
+ overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
+ overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
+ sceneRoot.getOverlay().add(overlayView);
+ // TODO: add automatic facility to Visibility superclass for keeping views around
+ final float startAlpha = view.getAlpha();
+ float endAlpha = 0;
+ final View finalView = view;
+ final View finalOverlayView = overlayView;
+ final View finalViewToKeep = viewToKeep;
+ final ViewGroup finalSceneRoot = sceneRoot;
+ final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finalView.setAlpha(startAlpha);
+ // TODO: restore view offset from overlay repositioning
+ if (finalViewToKeep != null) {
+ finalViewToKeep.setVisibility(finalVisibility);
+ }
+ if (finalOverlayView != null) {
+ finalSceneRoot.getOverlay().remove(finalOverlayView);
+ }
+ }
+ };
+ return runAnimation(view, startAlpha, endAlpha, endListener);
+ }
+ if (viewToKeep != null) {
+ // TODO: find a different way to do this, like just changing the view to be
+ // VISIBLE for the duration of the transition
+ viewToKeep.setVisibility((View.VISIBLE));
+ // TODO: add automatic facility to Visibility superclass for keeping views around
+ final float startAlpha = view.getAlpha();
+ float endAlpha = 0;
+ final View finalView = view;
+ final View finalOverlayView = overlayView;
+ final View finalViewToKeep = viewToKeep;
+ final ViewGroup finalSceneRoot = sceneRoot;
+ final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finalView.setAlpha(startAlpha);
+ // TODO: restore view offset from overlay repositioning
+ if (finalViewToKeep != null) {
+ finalViewToKeep.setVisibility(finalVisibility);
+ }
+ if (finalOverlayView != null) {
+ finalSceneRoot.getOverlay().remove(finalOverlayView);
+ }
+ }
+ };
+ return runAnimation(view, startAlpha, endAlpha, endListener);
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/android/view/transition/Move.java b/core/java/android/view/transition/Move.java
new file mode 100644
index 0000000..ceda5a5
--- /dev/null
+++ b/core/java/android/view/transition/Move.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Map;
+
+/**
+ * This transition captures the layout bounds of target views before and after
+ * the scene change and animates those changes during the transition.
+ */
+public class Move extends Transition {
+
+ private static final String PROPNAME_BOUNDS = "android:move:bounds";
+ private static final String PROPNAME_PARENT = "android:move:parent";
+ private static final String PROPNAME_WINDOW_X = "android:move:windowX";
+ private static final String PROPNAME_WINDOW_Y = "android:move:windowY";
+ int[] tempLocation = new int[2];
+ boolean mResizeClip = false;
+ boolean mReparent = false;
+ private static final String LOG_TAG = "Move";
+
+ private static RectEvaluator sRectEvaluator = new RectEvaluator();
+
+ public void setResizeClip(boolean resizeClip) {
+ mResizeClip = resizeClip;
+ }
+
+ /**
+ * Setting this flag tells Move to track the before/after parent
+ * of every view using this transition. The flag is not enabled by
+ * default because it requires the parent instances to be the same
+ * in the two scenes or else all parents must use ids to allow
+ * the transition to determine which parents are the same.
+ *
+ * @param reparent true if the transition should track the parent
+ * container of target views and animate parent changes.
+ */
+ public void setReparent(boolean reparent) {
+ mReparent = reparent;
+ }
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ View view = values.view;
+ values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(),
+ view.getRight(), view.getBottom()));
+ values.values.put(PROPNAME_PARENT, values.view.getParent());
+ values.view.getLocationInWindow(tempLocation);
+ values.values.put(PROPNAME_WINDOW_X, tempLocation[0]);
+ values.values.put(PROPNAME_WINDOW_Y, tempLocation[1]);
+ }
+
+ @Override
+ protected Animator play(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ Map<String, Object> startParentVals = startValues.values;
+ Map<String, Object> endParentVals = endValues.values;
+ ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT);
+ ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT);
+ if (startParent == null || endParent == null) {
+ return null;
+ }
+ final View view = endValues.view;
+ boolean parentsEqual = (startParent == endParent) ||
+ (startParent.getId() == endParent.getId());
+ // TODO: Might want reparenting to be separate/subclass transition, or at least
+ // triggered by a property on Move. Otherwise, we're forcing the requirement that
+ // all parents in layouts have IDs to avoid layout-inflation resulting in a side-effect
+ // of reparenting the views.
+ if (!mReparent || parentsEqual) {
+ Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ int startLeft = startBounds.left;
+ int endLeft = endBounds.left;
+ int startTop = startBounds.top;
+ int endTop = endBounds.top;
+ int startRight = startBounds.right;
+ int endRight = endBounds.right;
+ int startBottom = startBounds.bottom;
+ int endBottom = endBounds.bottom;
+ int startWidth = startRight - startLeft;
+ int startHeight = startBottom - startTop;
+ int endWidth = endRight - endLeft;
+ int endHeight = endBottom - endTop;
+ int numChanges = 0;
+ if (startWidth != 0 && startHeight != 0 && endWidth != 0 && endHeight != 0) {
+ if (startLeft != endLeft) ++numChanges;
+ if (startTop != endTop) ++numChanges;
+ if (startRight != endRight) ++numChanges;
+ if (startBottom != endBottom) ++numChanges;
+ }
+ if (numChanges > 0) {
+ if (!mResizeClip) {
+ PropertyValuesHolder pvh[] = new PropertyValuesHolder[numChanges];
+ int pvhIndex = 0;
+ if (startLeft != endLeft) view.setLeft(startLeft);
+ if (startTop != endTop) view.setTop(startTop);
+ if (startRight != endRight) view.setRight(startRight);
+ if (startBottom != endBottom) view.setBottom(startBottom);
+ if (startLeft != endLeft) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("left", startLeft, endLeft);
+ }
+ if (startTop != endTop) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("top", startTop, endTop);
+ }
+ if (startRight != endRight) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("right",
+ startRight, endRight);
+ }
+ if (startBottom != endBottom) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("bottom",
+ startBottom, endBottom);
+ }
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
+ if (view.getParent() instanceof ViewGroup) {
+ final ViewGroup parent = (ViewGroup) view.getParent();
+ parent.suppressLayout(true);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ parent.suppressLayout(false);
+ }
+ });
+ }
+ return anim;
+ } else {
+ if (startWidth != endWidth) view.setRight(endLeft +
+ Math.max(startWidth, endWidth));
+ if (startHeight != endHeight) view.setBottom(endTop +
+ Math.max(startHeight, endHeight));
+ // TODO: don't clobber TX/TY
+ if (startLeft != endLeft) view.setTranslationX(startLeft - endLeft);
+ if (startTop != endTop) view.setTranslationY(startTop - endTop);
+ // Animate location with translationX/Y and size with clip bounds
+ float transXDelta = endLeft - startLeft;
+ float transYDelta = endTop - startTop;
+ int widthDelta = endWidth - startWidth;
+ int heightDelta = endHeight - startHeight;
+ numChanges = 0;
+ if (transXDelta != 0) numChanges++;
+ if (transYDelta != 0) numChanges++;
+ if (widthDelta != 0 || heightDelta != 0) numChanges++;
+ PropertyValuesHolder pvh[] = new PropertyValuesHolder[numChanges];
+ int pvhIndex = 0;
+ if (transXDelta != 0) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofFloat("translationX",
+ view.getTranslationX(), 0);
+ }
+ if (transYDelta != 0) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofFloat("translationY",
+ view.getTranslationY(), 0);
+ }
+ if (widthDelta != 0 || heightDelta != 0) {
+ Rect tempStartBounds = new Rect(0, 0, startWidth, startHeight);
+ Rect tempEndBounds = new Rect(0, 0, endWidth, endHeight);
+ pvh[pvhIndex++] = PropertyValuesHolder.ofObject("clipBounds",
+ sRectEvaluator, tempStartBounds, tempEndBounds);
+ }
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
+ if (view.getParent() instanceof ViewGroup) {
+ final ViewGroup parent = (ViewGroup) view.getParent();
+ parent.suppressLayout(true);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ parent.suppressLayout(false);
+ }
+ });
+ }
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setClipBounds(null);
+ }
+ });
+ return anim;
+ }
+ }
+ } else {
+ int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
+ int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
+ int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X);
+ int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y);
+ // TODO: also handle size changes: check bounds and animate size changes
+ if (startX != endX || startY != endY) {
+ sceneRoot.getLocationInWindow(tempLocation);
+ Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ final BitmapDrawable drawable = new BitmapDrawable(bitmap);
+ view.setVisibility(View.INVISIBLE);
+ sceneRoot.getOverlay().add(drawable);
+ Rect startBounds1 = new Rect(startX - tempLocation[0], startY - tempLocation[1],
+ startX - tempLocation[0] + view.getWidth(),
+ startY - tempLocation[1] + view.getHeight());
+ Rect endBounds1 = new Rect(endX - tempLocation[0], endY - tempLocation[1],
+ endX - tempLocation[0] + view.getWidth(),
+ endY - tempLocation[1] + view.getHeight());
+ ObjectAnimator anim = ObjectAnimator.ofObject(drawable, "bounds",
+ sRectEvaluator, startBounds1, endBounds1);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ sceneRoot.getOverlay().remove(drawable);
+ view.setVisibility(View.VISIBLE);
+ }
+ });
+ return anim;
+ }
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/view/transition/Recolor.java b/core/java/android/view/transition/Recolor.java
new file mode 100644
index 0000000..e4858c4
--- /dev/null
+++ b/core/java/android/view/transition/Recolor.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.animation.Animator;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.ArrayMap;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.Map;
+
+/**
+ * This transition tracks changes during scene changes to the
+ * {@link View#setBackground(android.graphics.drawable.Drawable) background}
+ * property of its target views (when the background is a
+ * {@link ColorDrawable}, as well as the
+ * {@link TextView#setTextColor(android.content.res.ColorStateList)
+ * color} of the text for target TextViews. If the color changes between
+ * scenes, the color change is animated.
+ */
+public class Recolor extends Transition {
+
+ private static final String PROPNAME_BACKGROUND = "android:recolor:background";
+ private static final String PROPNAME_TEXT_COLOR = "android:recolor:textColor";
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ values.values.put(PROPNAME_BACKGROUND, values.view.getBackground());
+ if (values.view instanceof TextView) {
+ values.values.put(PROPNAME_TEXT_COLOR, ((TextView)values.view).getCurrentTextColor());
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ final View view = endValues.view;
+ Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND);
+ Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND);
+ boolean changed = false;
+ if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
+ ColorDrawable startColor = (ColorDrawable) startBackground;
+ ColorDrawable endColor = (ColorDrawable) endBackground;
+ if (startColor.getColor() != endColor.getColor()) {
+ endColor.setColor(startColor.getColor());
+ changed = true;
+ return ObjectAnimator.ofObject(endBackground, "color",
+ new ArgbEvaluator(), startColor.getColor(), endColor.getColor());
+ }
+ }
+ if (view instanceof TextView) {
+ TextView textView = (TextView) view;
+ int start = (Integer) startValues.values.get(PROPNAME_TEXT_COLOR);
+ int end = (Integer) endValues.values.get(PROPNAME_TEXT_COLOR);
+ if (start != end) {
+ textView.setTextColor(end);
+ changed = true;
+ return ObjectAnimator.ofObject(textView, "textColor",
+ new ArgbEvaluator(), start, end);
+ }
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/view/transition/Rotate.java b/core/java/android/view/transition/Rotate.java
new file mode 100644
index 0000000..d35a6dc7
--- /dev/null
+++ b/core/java/android/view/transition/Rotate.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This transition captures the rotation property of targets before and after
+ * the scene change and animates any changes.
+ */
+public class Rotate extends Transition {
+
+ private static final String PROPNAME_ROTATION = "android:rotate:rotation";
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ values.values.put(PROPNAME_ROTATION, values.view.getRotation());
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ final View view = endValues.view;
+ float startRotation = (Float) startValues.values.get(PROPNAME_ROTATION);
+ float endRotation = (Float) endValues.values.get(PROPNAME_ROTATION);
+ if (startRotation != endRotation) {
+ view.setRotation(startRotation);
+ return ObjectAnimator.ofFloat(view, View.ROTATION,
+ startRotation, endRotation);
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/view/transition/Scene.java b/core/java/android/view/transition/Scene.java
new file mode 100644
index 0000000..cf3eadb
--- /dev/null
+++ b/core/java/android/view/transition/Scene.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+/**
+ * A scene represents the collection of values that various properties in the
+ * View hierarchy will have when the scene is applied. A Scene can be
+ * configured to automatically run a Transition when it is applied, which will
+ * animate the various property changes that take place during the
+ * scene change.
+ */
+public final class Scene {
+
+ private Context mContext;
+ private int mLayoutId = -1;
+ private ViewGroup mSceneRoot;
+ private ViewGroup mLayout; // alternative to layoutId
+ Runnable mEnterAction, mExitAction;
+
+ /**
+ * Constructs a Scene with no information about how values will change
+ * when this scene is applied. This constructor might be used when
+ * a Scene is created with the intention of being dynamically configured,
+ * through setting {@link #setEnterAction(Runnable)} and possibly
+ * {@link #setExitAction(Runnable)}.
+ *
+ * @param sceneRoot The root of the hierarchy in which scene changes
+ * and transitions will take place.
+ */
+ public Scene(ViewGroup sceneRoot) {
+ mSceneRoot = sceneRoot;
+ }
+
+ /**
+ * Constructs a Scene which, when entered, will remove any
+ * children from the sceneRoot container and will inflate and add
+ * the hierarchy specified by the layoutId resource file.
+ *
+ * @param sceneRoot The root of the hierarchy in which scene changes
+ * and transitions will take place.
+ * @param layoutId The id of a resource file that defines the view
+ * hierarchy of this scene.
+ * @param context The context used in the process of inflating
+ * the layout resource.
+ */
+ public Scene(ViewGroup sceneRoot, int layoutId, Context context) {
+ mContext = context;
+ mSceneRoot = sceneRoot;
+ mLayoutId = layoutId;
+ }
+
+ /**
+ * Constructs a Scene which, when entered, will remove any
+ * children from the sceneRoot container and add the layout
+ * object as a new child of that container.
+ *
+ * @param sceneRoot The root of the hierarchy in which scene changes
+ * and transitions will take place.
+ * @param layout The view hiearrchy of this scene, added as a child
+ * of sceneRoot when this scene is entered.
+ */
+ public Scene(ViewGroup sceneRoot, ViewGroup layout) {
+ mSceneRoot = sceneRoot;
+ mLayout = layout;
+ }
+
+ /**
+ * Gets the root of the scene, which is the root of the view hierarchy
+ * affected by changes due to this scene, and which will be animated
+ * when this scene is entered.
+ *
+ * @return The root of the view hierarchy affected by this scene.
+ */
+ public ViewGroup getSceneRoot() {
+ return mSceneRoot;
+ }
+
+ /**
+ * Exits this scene, if it is the {@link ViewGroup#getCurrentScene()
+ * currentScene} on the scene's {@link #getSceneRoot() scene root}.
+ * Exiting a scene involves removing the layout added if the scene
+ * has either a layoutId or layout view group (set at construction
+ * time) and running the {@link #setExitAction(Runnable) exit action}
+ * if there is one.
+ */
+ public void exit() {
+ if (mSceneRoot.getCurrentScene() == this) {
+ if (mLayoutId >= 0 || mLayout != null) {
+ // Undo layout change caused by entering this scene
+ getSceneRoot().removeAllViews();
+ }
+ if (mExitAction != null) {
+ mExitAction.run();
+ }
+ }
+ }
+
+ /**
+ * Enters this scene, which entails changing all values that
+ * are specified by this scene. These may be values associated
+ * with a layout view group or layout resource file which will
+ * now be added to the scene root, or it may be values changed by
+ * an {@link #setEnterAction(Runnable)} enter action}, or a
+ * combination of the these. No transition will be run when the
+ * scene is entered. To get transition behavior in scene changes,
+ * use one of the methods in {@link TransitionManager} instead.
+ */
+ public void enter() {
+
+ // Apply layout change, if any
+ if (mLayoutId >= 0 || mLayout != null) {
+ // redundant with exit() action of previous scene, but must
+ // empty out that parent container before adding to it
+ getSceneRoot().removeAllViews();
+
+ if (mLayoutId >= 0) {
+ LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot);
+ } else {
+ mSceneRoot.addView(mLayout);
+ }
+ }
+
+ // Notify next scene that it is entering. Subclasses may override to configure scene.
+ if (mEnterAction != null) {
+ mEnterAction.run();
+ }
+
+ mSceneRoot.setCurrentScene(this );
+ }
+
+ /**
+ * Scenes that are not defined with layout resources or
+ * hierarchies, or which need to perform additional steps
+ * after those hierarchies are changed to, should set an enter
+ * action, and possibly an exit action as well. An enter action
+ * will cause Scene to call back into application code to do
+ * anything else the application needs after transitions have
+ * captured pre-change values and after any other scene changes
+ * have been applied, such as the layout (if any) being added to
+ * the view hierarchy. After this method is called, Transitions will
+ * be played.
+ *
+ * @param action The runnable whose {@link Runnable#run() run()} method will
+ * be called when this scene is entered
+ * @see #setExitAction(Runnable)
+ * @see Scene#Scene(ViewGroup, int, Context)
+ * @see Scene#Scene(ViewGroup, ViewGroup)
+ */
+ public void setEnterAction(Runnable action) {
+ mEnterAction = action;
+ }
+
+ /**
+ * Scenes that are not defined with layout resources or
+ * hierarchies, or which need to perform additional steps
+ * after those hierarchies are changed to, should set an enter
+ * action, and possibly an exit action as well. An exit action
+ * will cause Scene to call back into application code to do
+ * anything the application needs to do after applicable transitions have
+ * captured pre-change values, but before any other scene changes
+ * have been applied, such as the new layout (if any) being added to
+ * the view hierarchy. After this method is called, the next scene
+ * will be entered, including a call to {@link #setEnterAction(Runnable)}
+ * if an enter action is set.
+ *
+ * @see #setEnterAction(Runnable)
+ * @see Scene#Scene(ViewGroup, int, Context)
+ * @see Scene#Scene(ViewGroup, ViewGroup)
+ */
+ public void setExitAction(Runnable action) {
+ mExitAction = action;
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/android/view/transition/Slide.java b/core/java/android/view/transition/Slide.java
new file mode 100644
index 0000000..b2f5db5
--- /dev/null
+++ b/core/java/android/view/transition/Slide.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * This transition captures the visibility of target objects before and
+ * after a scene change and animates any changes by sliding the target
+ * objects into or out of place.
+ */
+public class Slide extends Visibility {
+
+ // TODO: Add parameter for sliding factor - it's hard-coded below
+
+ private static final TimeInterpolator sAccelerator = new AccelerateInterpolator();
+ private static final TimeInterpolator sDecelerator = new DecelerateInterpolator();
+
+ @Override
+ protected Animator appear(ViewGroup sceneRoot,
+ TransitionValues startValues, int startVisibility,
+ TransitionValues endValues, int endVisibility) {
+ View endView = (endValues != null) ? endValues.view : null;
+ endView.setTranslationY(-2 * endView.getHeight());
+ ObjectAnimator anim = ObjectAnimator.ofFloat(endView, View.TRANSLATION_Y,
+ -2 * endView.getHeight(), 0);
+ anim.setInterpolator(sDecelerator);
+ return anim;
+ }
+
+ @Override
+ protected Animator disappear(ViewGroup sceneRoot,
+ TransitionValues startValues, int startVisibility,
+ TransitionValues endValues, int endVisibility) {
+ View startView = (startValues != null) ? startValues.view : null;
+ startView.setTranslationY(0);
+ ObjectAnimator anim = ObjectAnimator.ofFloat(startView, View.TRANSLATION_Y, 0,
+ -2 * startView.getHeight());
+ anim.setInterpolator(sAccelerator);
+ return anim;
+ }
+
+}
diff --git a/core/java/android/view/transition/TextChange.java b/core/java/android/view/transition/TextChange.java
new file mode 100644
index 0000000..f033e63
--- /dev/null
+++ b/core/java/android/view/transition/TextChange.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.util.ArrayMap;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.Map;
+
+/**
+ * This transition tracks changes to the text in TextView targets. If the text
+ * changes between the start and end scenes, the transition ensures that the
+ * starting text stays until the transition ends, at which point it changes
+ * to the end text. This is useful in situations where you want to resize a
+ * text view to its new size before displaying the text that goes there.
+ */
+public class TextChange extends Transition {
+ private static final String PROPNAME_TEXT = "android:textchange:text";
+
+ // TODO: think about other options we could have here, like cross-fading the text, or fading
+ // it out/in. These could be parameters to supply to the constructors (and xml attributes).
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ if (values.view instanceof TextView) {
+ TextView textview = (TextView) values.view;
+ values.values.put(PROPNAME_TEXT, textview.getText());
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null || !(endValues.view instanceof TextView)) {
+ return null;
+ }
+ final TextView view = (TextView) endValues.view;
+ Map<String, Object> startVals = startValues.values;
+ Map<String, Object> endVals = endValues.values;
+ String startText = (String) startVals.get(PROPNAME_TEXT);
+ final String endText = (String) endVals.get(PROPNAME_TEXT);
+ if (!startText.equals(endText)) {
+ view.setText(startText);
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setText(endText);
+ }
+ });
+ return anim;
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/view/transition/Transition.java b/core/java/android/view/transition/Transition.java
new file mode 100644
index 0000000..2cffd26
--- /dev/null
+++ b/core/java/android/view/transition/Transition.java
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOverlay;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+
+/**
+ * A Transition holds information about animations that will be run on its
+ * targets during a scene change. Subclasses of this abstract class may
+ * choreograph several child transitions ({@link TransitionGroup} or they may
+ * perform custom animations themselves. Any Transition has two main jobs:
+ * (1) capture property values, and (2) play animations based on changes to
+ * captured property values. A custom transition knows what property values
+ * on View objects are of interest to it, and also knows how to animate
+ * changes to those values. For example, the {@link Fade} transition tracks
+ * changes to visibility-related properties and is able to construct and run
+ * animations that fade items in or out based on changes to those properties.
+ *
+ * <p>Note: Transitions may not work correctly with either {@link SurfaceView}
+ * or {@link TextureView}, due to the way that these views are displayed
+ * on the screen. For SurfaceView, the problem is that the view is updated from
+ * a non-UI thread, so changes to the view due to transitions (such as moving
+ * and resizing the view) may be out of sync with the display inside those bounds.
+ * TextureView is more compatible with transitions in general, but some
+ * specific transitions (such as {@link Crossfade}) may not be compatible
+ * with TextureView because they rely on {@link ViewOverlay} functionality,
+ * which does not currently work with TextureView.</p>
+ */
+public abstract class Transition implements Cloneable {
+
+ private static final String LOG_TAG = "Transition";
+ static final boolean DBG = false;
+
+ long mStartDelay = -1;
+ long mDuration = -1;
+ TimeInterpolator mInterpolator = null;
+ int[] mTargetIds;
+ View[] mTargets;
+ private TransitionValuesMaps mStartValues = new TransitionValuesMaps();
+ private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
+ TransitionGroup mParent = null;
+
+ // Scene Root is set at play() time in the cloned Transition
+ ViewGroup mSceneRoot = null;
+
+ // Used to carry data between setup() and play(), cleared before every scene transition
+ private ArrayList<TransitionValues> mPlayStartValuesList = new ArrayList<TransitionValues>();
+ private ArrayList<TransitionValues> mPlayEndValuesList = new ArrayList<TransitionValues>();
+
+ // Track all animators in use in case the transition gets canceled and needs to
+ // cancel running animators
+ private ArrayList<Animator> mCurrentAnimators = new ArrayList<Animator>();
+
+ // Number of per-target instances of this Transition currently running. This count is
+ // determined by calls to startTransition() and endTransition()
+ int mNumInstances = 0;
+
+
+ /**
+ * The set of listeners to be sent transition lifecycle events.
+ */
+ ArrayList<TransitionListener> mListeners = null;
+
+ /**
+ * Constructs a Transition object with no target objects. A transition with
+ * no targets defaults to running on all target objects in the scene hierarchy
+ * (if the transition is not contained in a TransitionGroup), or all target
+ * objects passed down from its parent (if it is in a TransitionGroup).
+ */
+ public Transition() {}
+
+ /**
+ * Sets the duration of this transition. By default, there is no duration
+ * (indicated by a negative number), which means that the Animator created by
+ * the transition will have its own specified duration. If the duration of a
+ * Transition is set, that duration will override the Animator duration.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @return This transition object.
+ */
+ public Transition setDuration(long duration) {
+ mDuration = duration;
+ return this;
+ }
+
+ public long getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Sets the startDelay of this transition. By default, there is no delay
+ * (indicated by a negative number), which means that the Animator created by
+ * the transition will have its own specified startDelay. If the delay of a
+ * Transition is set, that delay will override the Animator delay.
+ *
+ * @param startDelay The length of the delay, in milliseconds.
+ */
+ public void setStartDelay(long startDelay) {
+ mStartDelay = startDelay;
+ }
+
+ public long getStartDelay() {
+ return mStartDelay;
+ }
+
+ /**
+ * Sets the interpolator of this transition. By default, the interpolator
+ * is null, which means that the Animator created by the transition
+ * will have its own specified interpolator. If the interpolator of a
+ * Transition is set, that interpolator will override the Animator interpolator.
+ *
+ * @param interpolator The time interpolator used by the transition
+ */
+ public void setInterpolator(TimeInterpolator interpolator) {
+ mInterpolator = interpolator;
+ }
+
+ public TimeInterpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ /**
+ * This method is called by the transition's parent (all the way up to the
+ * topmost Transition in the hierarchy) with the sceneRoot and start/end
+ * values that the transition may need to set up initial target values
+ * and construct an appropriate animation. For example, if an overall
+ * Transition is a {@link TransitionGroup} consisting of several
+ * child transitions in sequence, then some of the child transitions may
+ * want to set initial values on target views prior to the overall
+ * Transition commencing, to put them in an appropriate state for the
+ * delay between that start and the child Transition start time. For
+ * example, a transition that fades an item in may wish to set the starting
+ * alpha value to 0, to avoid it blinking in prior to the transition
+ * actually starting the animation. This is necessary because the scene
+ * change that triggers the Transition will automatically set the end-scene
+ * on all target views, so a Transition that wants to animate from a
+ * different value should set that value prior to returning from this method.
+ *
+ * <p>Additionally, a Transition can perform logic to determine whether
+ * the transition needs to run on the given target and start/end values.
+ * For example, a transition that resizes objects on the screen may wish
+ * to avoid running for views which are not present in either the start
+ * or end scenes. A return value of <code>null</code> indicates that
+ * no animation should run. The default implementation returns null.</p>
+ *
+ * <p>If there is an animator created and returned from this method, the
+ * transition mechanism will apply any applicable duration, startDelay,
+ * and interpolator to that animation and start it. A return value of
+ * <code>null</code> indicates that no animation should run. The default
+ * implementation returns null.</p>
+ *
+ * <p>The method is called for every applicable target object, which is
+ * stored in the {@link TransitionValues#view} field.</p>
+ *
+ * @param sceneRoot
+ * @param startValues
+ * @param endValues
+ * @return A non-null Animator to be started at the appropriate time in the
+ * overall transition for this scene change, null otherwise.
+ */
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ return null;
+ }
+
+ /**
+ * This version of play() is called with the entire set of start/end
+ * values. The implementation in Transition iterates through these lists
+ * and calls {@link #play(ViewGroup, TransitionValues, TransitionValues)}
+ * with each set of start/end values on this transition. The
+ * TransitionGroup subclass overrides this method and delegates it to
+ * each of its children in succession.
+ *
+ * @hide
+ */
+ protected void play(ViewGroup sceneRoot, TransitionValuesMaps startValues,
+ TransitionValuesMaps endValues) {
+ mPlayStartValuesList.clear();
+ mPlayEndValuesList.clear();
+ ArrayMap<View, TransitionValues> endCopy =
+ new ArrayMap<View, TransitionValues>(endValues.viewValues);
+ SparseArray<TransitionValues> endIdCopy =
+ new SparseArray<TransitionValues>(endValues.idValues.size());
+ for (int i = 0; i < endValues.idValues.size(); ++i) {
+ int id = endValues.idValues.keyAt(i);
+ endIdCopy.put(id, endValues.idValues.valueAt(i));
+ }
+ LongSparseArray<TransitionValues> endItemIdCopy =
+ new LongSparseArray<TransitionValues>(endValues.itemIdValues.size());
+ for (int i = 0; i < endValues.itemIdValues.size(); ++i) {
+ long id = endValues.itemIdValues.keyAt(i);
+ endItemIdCopy.put(id, endValues.itemIdValues.valueAt(i));
+ }
+ // Walk through the start values, playing everything we find
+ // Remove from the end set as we go
+ ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>();
+ ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>();
+ for (View view : startValues.viewValues.keySet()) {
+ TransitionValues start = null;
+ TransitionValues end = null;
+ boolean isInListView = false;
+ if (view.getParent() instanceof ListView) {
+ isInListView = true;
+ }
+ if (!isInListView) {
+ int id = view.getId();
+ start = startValues.viewValues.get(view) != null ?
+ startValues.viewValues.get(view) : startValues.idValues.get(id);
+ if (endValues.viewValues.get(view) != null) {
+ end = endValues.viewValues.get(view);
+ endCopy.remove(view);
+ } else {
+ end = endValues.idValues.get(id);
+ View removeView = null;
+ for (View viewToRemove : endCopy.keySet()) {
+ if (viewToRemove.getId() == id) {
+ removeView = viewToRemove;
+ }
+ }
+ if (removeView != null) {
+ endCopy.remove(removeView);
+ }
+ }
+ endIdCopy.remove(id);
+ if (isValidTarget(view, id)) {
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ } else {
+ ListView parent = (ListView) view.getParent();
+ if (parent.getAdapter().hasStableIds()) {
+ int position = parent.getPositionForView(view);
+ long itemId = parent.getItemIdAtPosition(position);
+ start = startValues.itemIdValues.get(itemId);
+ endItemIdCopy.remove(itemId);
+ // TODO: deal with targetIDs for itemIDs for ListView items
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ }
+ int startItemIdCopySize = startValues.itemIdValues.size();
+ for (int i = 0; i < startItemIdCopySize; ++i) {
+ long id = startValues.itemIdValues.keyAt(i);
+ if (isValidTarget(null, id)) {
+ TransitionValues start = startValues.itemIdValues.get(id);
+ TransitionValues end = endValues.itemIdValues.get(id);
+ endItemIdCopy.remove(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ // Now walk through the remains of the end set
+ for (View view : endCopy.keySet()) {
+ int id = view.getId();
+ if (isValidTarget(view, id)) {
+ TransitionValues start = startValues.viewValues.get(view) != null ?
+ startValues.viewValues.get(view) : startValues.idValues.get(id);
+ TransitionValues end = endCopy.get(view);
+ endIdCopy.remove(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ int endIdCopySize = endIdCopy.size();
+ for (int i = 0; i < endIdCopySize; ++i) {
+ int id = endIdCopy.keyAt(i);
+ if (isValidTarget(null, id)) {
+ TransitionValues start = startValues.idValues.get(id);
+ TransitionValues end = endIdCopy.get(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ int endItemIdCopySize = endItemIdCopy.size();
+ for (int i = 0; i < endItemIdCopySize; ++i) {
+ long id = endItemIdCopy.keyAt(i);
+ // TODO: Deal with targetIDs and itemIDs
+ TransitionValues start = startValues.itemIdValues.get(id);
+ TransitionValues end = endItemIdCopy.get(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ for (int i = 0; i < startValuesList.size(); ++i) {
+ TransitionValues start = startValuesList.get(i);
+ TransitionValues end = endValuesList.get(i);
+ // TODO: what to do about targetIds and itemIds?
+ Animator animator = play(sceneRoot, start, end);
+ if (animator != null) {
+ mAnimatorMap.put(new Pair(start, end), animator);
+ // Note: we've already done the check against targetIDs in these lists
+ mPlayStartValuesList.add(start);
+ mPlayEndValuesList.add(end);
+ }
+ }
+ }
+
+ ArrayMap<Pair<TransitionValues, TransitionValues>, Animator> mAnimatorMap =
+ new ArrayMap<Pair<TransitionValues, TransitionValues>, Animator>();
+
+ /**
+ * Internal utility method for checking whether a given view/id
+ * is valid for this transition, where "valid" means that either
+ * the Transition has no target/targetId list (the default, in which
+ * cause the transition should act on all views in the hiearchy), or
+ * the given view is in the target list or the view id is in the
+ * targetId list. If the target parameter is null, then the target list
+ * is not checked (this is in the case of ListView items, where the
+ * views are ignored and only the ids are used).
+ */
+ boolean isValidTarget(View target, long targetId) {
+ if (mTargetIds == null && mTargets == null) {
+ return true;
+ }
+ if (mTargetIds != null) {
+ for (int i = 0; i < mTargetIds.length; ++i) {
+ if (mTargetIds[i] == targetId) {
+ return true;
+ }
+ }
+ }
+ if (target != null && mTargets != null) {
+ for (int i = 0; i < mTargets.length; ++i) {
+ if (mTargets[i] == target) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This is called internally once all animations have been set up by the
+ * transition hierarchy. \
+ *
+ * @hide
+ */
+ protected void runAnimations() {
+
+ startTransition();
+ // Now walk the list of TransitionValues, calling play for each pair
+ for (int i = 0; i < mPlayStartValuesList.size(); ++i) {
+ TransitionValues start = mPlayStartValuesList.get(i);
+ TransitionValues end = mPlayEndValuesList.get(i);
+ startTransition();
+ runAnimator(mAnimatorMap.get(new Pair(start, end)));
+ }
+ mPlayStartValuesList.clear();
+ mPlayEndValuesList.clear();
+ mAnimatorMap.clear();
+ endTransition();
+ }
+
+ private void runAnimator(Animator animator) {
+ if (animator != null) {
+ // TODO: could be a single listener instance for all of them since it uses the param
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mCurrentAnimators.add(animation);
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentAnimators.remove(animation);
+ }
+ });
+ animate(animator);
+ }
+ }
+
+ /**
+ * Captures the current scene of values for the properties that this
+ * transition monitors. These values can be either the start or end
+ * values used in a subsequent call to
+ * {@link #play(ViewGroup, TransitionValues, TransitionValues)}, as indicated by
+ * <code>start</code>. The main concern for an implementation is what the
+ * properties are that the transition cares about and what the values are
+ * for all of those properties. The start and end values will be compared
+ * later during the
+ * {@link #play(android.view.ViewGroup, TransitionValues, TransitionValues)}
+ * method to determine what, if any, animations, should be run.
+ *
+ * @param transitionValues The holder for any values that the Transition
+ * wishes to store. Values are stored in the <code>values</code> field
+ * of this TransitionValues object and are keyed from
+ * a String value. For example, to store a view's rotation value,
+ * a transition might call
+ * <code>transitionValues.values.put("appname:transitionname:rotation",
+ * view.getRotation())</code>. The target view will already be stored in
+ * the transitionValues structure when this method is called.
+ */
+ protected abstract void captureValues(TransitionValues transitionValues, boolean start);
+
+ /**
+ * Sets the ids of target views that this Transition is interested in
+ * animating. By default, there are no targetIds, and a Transition will
+ * listen for changes on every view in the hierarchy below the sceneRoot
+ * of the Scene being transitioned into. Setting targetIDs constrains
+ * the Transition to only listen for, and act on, views with these IDs.
+ * Views with different IDs, or no IDs whatsoever, will be ignored.
+ *
+ * @see View#getId()
+ * @param targetIds A list of IDs which specify the set of Views on which
+ * the Transition will act.
+ * @return Transition The Transition on which the targetIds have been set.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionGroup.addTransitions(new Fade()).setTargetIds(someId);</code>
+ */
+ public Transition setTargetIds(int... targetIds) {
+ int numTargets = targetIds.length;
+ mTargetIds = new int[numTargets];
+ System.arraycopy(targetIds, 0, mTargetIds, 0, numTargets);
+ return this;
+ }
+
+ /**
+ * Sets the target view instances that this Transition is interested in
+ * animating. By default, there are no targets, and a Transition will
+ * listen for changes on every view in the hierarchy below the sceneRoot
+ * of the Scene being transitioned into. Setting targets constrains
+ * the Transition to only listen for, and act on, these views.
+ * All other views will be ignored.
+ *
+ * <p>The target list is like the {@link #setTargetIds(int...) targetId}
+ * list except this list specifies the actual View instances, not the ids
+ * of the views. This is an important distinction when scene changes involve
+ * view hierarchies which have been inflated separately; different views may
+ * share the same id but not actually be the same instance. If the transition
+ * should treat those views as the same, then seTargetIds() should be used
+ * instead of setTargets(). If, on the other hand, scene changes involve
+ * changes all within the same view hierarchy, among views which do not
+ * necessary have ids set on them, then the target list may be more
+ * convenient.</p>
+ *
+ * @see #setTargetIds(int...)
+ * @param targets A list of Views on which the Transition will act.
+ * @return Transition The Transition on which the targets have been set.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionGroup.addTransitions(new Fade()).setTargets(someView);</code>
+ */
+ public Transition setTargets(View... targets) {
+ int numTargets = targets.length;
+ mTargets = new View[numTargets];
+ System.arraycopy(targets, 0, mTargets, 0, numTargets);
+ return this;
+ }
+
+ /**
+ * Returns the array of target IDs that this transition limits itself to
+ * tracking and animating. If the array is null for both this method and
+ * {@link #getTargets()}, then this transition is
+ * not limited to specific views, and will handle changes to any views
+ * in the hierarchy of a scene change.
+ *
+ * @return the list of target IDs
+ */
+ public int[] getTargetIds() {
+ return mTargetIds;
+ }
+
+ /**
+ * Returns the array of target views that this transition limits itself to
+ * tracking and animating. If the array is null for both this method and
+ * {@link #getTargetIds()}, then this transition is
+ * not limited to specific views, and will handle changes to any views
+ * in the hierarchy of a scene change.
+ *
+ * @return the list of target views
+ */
+ public View[] getTargets() {
+ return mTargets;
+ }
+
+ /**
+ * Recursive method that captures values for the given view and the
+ * hierarchy underneath it.
+ * @param sceneRoot The root of the view hierarchy being captured
+ * @param start true if this capture is happening before the scene change,
+ * false otherwise
+ */
+ void captureValues(ViewGroup sceneRoot, boolean start) {
+ if (mTargetIds != null && mTargetIds.length > 0 ||
+ mTargets != null && mTargets.length > 0) {
+ if (mTargetIds != null) {
+ for (int i = 0; i < mTargetIds.length; ++i) {
+ int id = mTargetIds[i];
+ View view = sceneRoot.findViewById(id);
+ if (view != null) {
+ TransitionValues values = new TransitionValues();
+ values.view = view;
+ captureValues(values, start);
+ if (start) {
+ mStartValues.viewValues.put(view, values);
+ if (id >= 0) {
+ mStartValues.idValues.put(id, values);
+ }
+ } else {
+ mEndValues.viewValues.put(view, values);
+ if (id >= 0) {
+ mEndValues.idValues.put(id, values);
+ }
+ }
+ }
+ }
+ }
+ if (mTargets != null) {
+ for (int i = 0; i < mTargets.length; ++i) {
+ View view = mTargets[i];
+ if (view != null) {
+ TransitionValues values = new TransitionValues();
+ values.view = view;
+ captureValues(values, start);
+ if (start) {
+ mStartValues.viewValues.put(view, values);
+ } else {
+ mEndValues.viewValues.put(view, values);
+ }
+ }
+ }
+ }
+ } else {
+ captureHierarchy(sceneRoot, start);
+ }
+ }
+
+ /**
+ * Recursive method which captures values for an entire view hierarchy,
+ * starting at some root view. Transitions without targetIDs will use this
+ * method to capture values for all possible views.
+ *
+ * @param view The view for which to capture values. Children of this View
+ * will also be captured, recursively down to the leaf nodes.
+ * @param start true if values are being captured in the start scene, false
+ * otherwise.
+ */
+ private void captureHierarchy(View view, boolean start) {
+ if (view == null) {
+ return;
+ }
+ boolean isListViewItem = false;
+ if (view.getParent() instanceof ListView) {
+ isListViewItem = true;
+ }
+ if (isListViewItem && !((ListView) view.getParent()).getAdapter().hasStableIds()) {
+ // ignore listview children unless we can track them with stable IDs
+ return;
+ }
+ long id;
+ if (!isListViewItem) {
+ id = view.getId();
+ } else {
+ ListView listview = (ListView) view.getParent();
+ int position = listview.getPositionForView(view);
+ id = listview.getItemIdAtPosition(position);
+ view.setHasTransientState(true);
+ }
+ TransitionValues values = new TransitionValues();
+ values.view = view;
+ captureValues(values, start);
+ if (start) {
+ if (!isListViewItem) {
+ mStartValues.viewValues.put(view, values);
+ if (id >= 0) {
+ mStartValues.idValues.put((int) id, values);
+ }
+ } else {
+ mStartValues.itemIdValues.put(id, values);
+ }
+ } else {
+ if (!isListViewItem) {
+ mEndValues.viewValues.put(view, values);
+ if (id >= 0) {
+ mEndValues.idValues.put((int) id, values);
+ }
+ } else {
+ mEndValues.itemIdValues.put(id, values);
+ }
+ }
+ if (view instanceof ViewGroup) {
+ ViewGroup parent = (ViewGroup) view;
+ for (int i = 0; i < parent.getChildCount(); ++i) {
+ captureHierarchy(parent.getChildAt(i), start);
+ }
+ }
+ }
+
+ /**
+ * This method can be called by transitions to get the TransitionValues for
+ * any particular view during the transition-playing process. This might be
+ * necessary, for example, to query the before/after state of related views
+ * for a given transition.
+ */
+ protected TransitionValues getTransitionValues(View view, boolean start) {
+ if (mParent != null) {
+ return mParent.getTransitionValues(view, start);
+ }
+ TransitionValuesMaps valuesMaps = start ? mStartValues : mEndValues;
+ TransitionValues values = valuesMaps.viewValues.get(view);
+ if (values == null) {
+ int id = view.getId();
+ if (id >= 0) {
+ values = valuesMaps.idValues.get(id);
+ }
+ if (values == null && view.getParent() instanceof ListView) {
+ ListView listview = (ListView) view.getParent();
+ int position = listview.getPositionForView(view);
+ long itemId = listview.getItemIdAtPosition(position);
+ values = valuesMaps.itemIdValues.get(itemId);
+ }
+ // TODO: Doesn't handle the case where a view was parented to a
+ // ListView (with an itemId), but no longer is
+ }
+ return values;
+ }
+
+ /**
+ * Called by TransitionManager to play the transition. This calls
+ * play() to set things up and create all of the animations and then
+ * runAnimations() to actually start the animations.
+ */
+ void playTransition(ViewGroup sceneRoot) {
+ // setup() must be called on entire transition hierarchy and set of views
+ // before calling play() on anything; every transition needs a chance to set up
+ // target views appropriately before transitions begin running
+ play(sceneRoot, mStartValues, mEndValues);
+ runAnimations();
+ }
+
+ /**
+ * This is a utility method used by subclasses to handle standard parts of
+ * setting up and running an Animator: it sets the {@link #getDuration()
+ * duration} and the {@link #getStartDelay() startDelay}, starts the
+ * animation, and, when the animator ends, calls {@link #endTransition()}.
+ *
+ * @param animator The Animator to be run during this transition.
+ *
+ * @hide
+ */
+ protected void animate(Animator animator) {
+ // TODO: maybe pass auto-end as a boolean parameter?
+ if (animator == null) {
+ endTransition();
+ } else {
+ if (getDuration() >= 0) {
+ animator.setDuration(getDuration());
+ }
+ if (getStartDelay() >= 0) {
+ animator.setStartDelay(getStartDelay());
+ }
+ if (getInterpolator() != null) {
+ animator.setInterpolator(getInterpolator());
+ }
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endTransition();
+ animation.removeListener(this);
+ }
+ });
+ animator.start();
+ }
+ }
+
+ /**
+ * Subclasses may override to receive notice of when the transition starts.
+ * This is equivalent to listening for the
+ * {@link TransitionListener#onTransitionStart(Transition)} callback.
+ */
+ protected void onTransitionStart() {
+ }
+
+ /**
+ * Subclasses may override to receive notice of when the transition is
+ * canceled. This is equivalent to listening for the
+ * {@link TransitionListener#onTransitionCancel(Transition)} callback.
+ */
+ protected void onTransitionCancel() {
+ }
+
+ /**
+ * Subclasses may override to receive notice of when the transition ends.
+ * This is equivalent to listening for the
+ * {@link TransitionListener#onTransitionEnd(Transition)} callback.
+ */
+ protected void onTransitionEnd() {
+ }
+
+ /**
+ * This method is called automatically by the transition and
+ * TransitionGroup classes prior to a Transition subclass starting;
+ * subclasses should not need to call it directly.
+ *
+ * @hide
+ */
+ protected void startTransition() {
+ if (mNumInstances == 0) {
+ onTransitionStart();
+ if (mListeners != null && mListeners.size() > 0) {
+ ArrayList<TransitionListener> tmpListeners =
+ (ArrayList<TransitionListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onTransitionStart(this);
+ }
+ }
+ }
+ mNumInstances++;
+ }
+
+ /**
+ * This method is called automatically by the Transition and
+ * TransitionGroup classes when a transition finishes, either because
+ * a transition did nothing (returned a null Animator from
+ * {@link Transition#play(ViewGroup, TransitionValues,
+ * TransitionValues)}) or because the transition returned a valid
+ * Animator and endTransition() was called in the onAnimationEnd()
+ * callback of the AnimatorListener.
+ *
+ * @hide
+ */
+ protected void endTransition() {
+ --mNumInstances;
+ if (mNumInstances == 0) {
+ onTransitionEnd();
+ if (mListeners != null && mListeners.size() > 0) {
+ ArrayList<TransitionListener> tmpListeners =
+ (ArrayList<TransitionListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onTransitionEnd(this);
+ }
+ }
+ for (int i = 0; i < mStartValues.itemIdValues.size(); ++i) {
+ TransitionValues tv = mStartValues.itemIdValues.valueAt(i);
+ View v = tv.view;
+ if (v.hasTransientState()) {
+ v.setHasTransientState(false);
+ }
+ }
+ for (int i = 0; i < mEndValues.itemIdValues.size(); ++i) {
+ TransitionValues tv = mEndValues.itemIdValues.valueAt(i);
+ View v = tv.view;
+ if (v.hasTransientState()) {
+ v.setHasTransientState(false);
+ }
+ }
+ mStartValues.viewValues.clear();
+ mStartValues.idValues.clear();
+ mStartValues.itemIdValues.clear();
+ mEndValues.viewValues.clear();
+ mEndValues.idValues.clear();
+ mEndValues.itemIdValues.clear();
+ mCurrentAnimators.clear();
+ }
+ }
+
+ /**
+ * This method cancels a transition that is currently running.
+ * Implementation TBD.
+ */
+ protected void cancelTransition() {
+ // TODO: how does this work with instances?
+ // TODO: this doesn't actually do *anything* yet
+ int numAnimators = mCurrentAnimators.size();
+ for (int i = numAnimators - 1; i >= 0; i--) {
+ Animator animator = mCurrentAnimators.get(i);
+ animator.cancel();
+ }
+ onTransitionCancel();
+ if (mListeners != null && mListeners.size() > 0) {
+ ArrayList<TransitionListener> tmpListeners =
+ (ArrayList<TransitionListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onTransitionCancel(this);
+ }
+ }
+ }
+
+ /**
+ * Adds a listener to the set of listeners that are sent events through the
+ * life of an animation, such as start, repeat, and end.
+ *
+ * @param listener the listener to be added to the current set of listeners
+ * for this animation.
+ */
+ public void addListener(TransitionListener listener) {
+ if (mListeners == null) {
+ mListeners = new ArrayList<TransitionListener>();
+ }
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from the set listening to this animation.
+ *
+ * @param listener the listener to be removed from the current set of
+ * listeners for this transition.
+ */
+ public void removeListener(TransitionListener listener) {
+ if (mListeners == null) {
+ return;
+ }
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ mListeners = null;
+ }
+ }
+
+ /**
+ * Gets the set of {@link TransitionListener} objects that are currently
+ * listening for events on this <code>Transition</code> object.
+ *
+ * @return ArrayList<TransitionListener> The set of listeners.
+ */
+ public ArrayList<TransitionListener> getListeners() {
+ return mListeners;
+ }
+
+ void setSceneRoot(ViewGroup sceneRoot) {
+ mSceneRoot = sceneRoot;
+ }
+
+ @Override
+ public String toString() {
+ return toString("");
+ }
+
+ @Override
+ public Transition clone() {
+ Transition clone = null;
+ try {
+ clone = (Transition) super.clone();
+ } catch (CloneNotSupportedException e) {}
+
+ return clone;
+ }
+
+ String toString(String indent) {
+ String result = indent + getClass().getSimpleName() + "@" +
+ Integer.toHexString(hashCode()) + ": ";
+ result += "dur(" + mDuration + ") ";
+ result += "dly(" + mStartDelay + ") ";
+ result += "interp(" + mInterpolator + ") ";
+ result += "tgts(";
+ if (mTargetIds != null) {
+ for (int i = 0; i < mTargetIds.length; ++i) {
+ if (i > 0) {
+ result += ", ";
+ }
+ result += mTargetIds[i];
+ }
+ }
+ if (mTargets != null) {
+ for (int i = 0; i < mTargets.length; ++i) {
+ if (i > 0) {
+ result += ", ";
+ }
+ result += mTargets[i];
+ }
+ }
+ result += ")";
+ return result;
+ }
+
+ /**
+ * A transition listener receives notifications from a transition.
+ * Notifications indicate transition lifecycle events: when the transition
+ * begins, ends, or is canceled.
+ */
+ public static interface TransitionListener {
+ /**
+ * Notification about the start of the transition.
+ *
+ * @param transition The started transition.
+ */
+ void onTransitionStart(Transition transition);
+
+ /**
+ * Notification about the end of the transition. Canceled transitions
+ * will always notify listeners of both the cancellation and end
+ * events. That is, {@link #onTransitionEnd()} is always called,
+ * regardless of whether the transition was canceled or played
+ * through to completion.
+ *
+ * @param transition The transition which reached its end.
+ */
+ void onTransitionEnd(Transition transition);
+
+ /**
+ * Notification about the cancellation of the transition.
+ *
+ * @param transition The transition which was canceled.
+ */
+ void onTransitionCancel(Transition transition);
+ }
+
+ /**
+ * Utility adapter class to avoid having to override all three methods
+ * whenever someone just wants to listen for a single event.
+ *
+ * @hide
+ * */
+ public static class TransitionListenerAdapter implements TransitionListener {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ }
+ }
+
+}
diff --git a/core/java/android/view/transition/TransitionGroup.java b/core/java/android/view/transition/TransitionGroup.java
new file mode 100644
index 0000000..313e33e
--- /dev/null
+++ b/core/java/android/view/transition/TransitionGroup.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.util.AndroidRuntimeException;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * A TransitionGroup is a parent of child transitions (including other
+ * TransitionGroups). Using TransitionGroups enables more complex
+ * choreography of transitions, where some groups play {@link #TOGETHER} and
+ * others play {@link #SEQUENTIALLY}. For example, {@link AutoTransition}
+ * uses a TransitionGroup to sequentially play a Fade(Fade.OUT), followed by
+ * a {@link Move}, followed by a Fade(Fade.OUT) transition.
+ */
+public class TransitionGroup extends Transition {
+
+ ArrayList<Transition> mTransitions = new ArrayList<Transition>();
+ private boolean mPlayTogether = true;
+ int mCurrentListeners;
+ boolean mStarted = false;
+
+ /**
+ * A flag used to indicate that the child transitions of this group
+ * should all start at the same time.
+ */
+ public static final int TOGETHER = 0;
+ /**
+ * A flag used to indicate that the child transitions of this group should
+ * play in sequence; when one child transition ends, the next child
+ * transition begins. Note that a transition does not end until all
+ * instances of it (which are playing on all applicable targets of the
+ * transition) end.
+ */
+ public static final int SEQUENTIALLY = 1;
+
+ /**
+ * Constructs an empty transition group. Add child transitions to the
+ * group by calling to {@link #addTransitions(Transition...)} )}. By default,
+ * child transitions will play {@link #TOGETHER}.
+ */
+ public TransitionGroup() {
+ }
+
+ /**
+ * Constructs an empty transition group with the specified ordering.
+ *
+ * @param ordering {@link #TOGETHER} to start this group's child
+ * transitions together, {@link #SEQUENTIALLY} to play the child
+ * transitions in sequence.
+ * @see #setOrdering(int)
+ */
+ public TransitionGroup(int ordering) {
+ setOrdering(ordering);
+ }
+
+ /**
+ * Sets the play order of this group's child transitions.
+ *
+ * @param ordering {@link #TOGETHER} to start this group's child
+ * transitions together, {@link #SEQUENTIALLY} to play the child
+ * transitions in sequence.
+ */
+ public void setOrdering(int ordering) {
+ switch (ordering) {
+ case SEQUENTIALLY:
+ mPlayTogether = false;
+ break;
+ case TOGETHER:
+ mPlayTogether = true;
+ break;
+ default:
+ throw new AndroidRuntimeException("Invalid parameter for TransitionGroup " +
+ "ordering: " + ordering);
+ }
+ }
+
+ /**
+ * Adds child transitions to this group. The order of the child transitions
+ * passed in determines the order in which they are started.
+ *
+ * @param transitions A list of child transition to be added to this group.
+ */
+ public void addTransitions(Transition... transitions) {
+ if (transitions != null) {
+ int numTransitions = transitions.length;
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.add(transitions[i]);
+ transitions[i].mParent = this;
+ if (mDuration >= 0) {
+ transitions[i].setDuration(mDuration);
+ }
+ }
+ }
+ }
+
+ /**
+ * Setting a non-negative duration on a TransitionGroup causes all of the child
+ * transitions (current and future) to inherit this duration.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @return This transitionGroup object.
+ */
+ @Override
+ public Transition setDuration(long duration) {
+ super.setDuration(duration);
+ if (mDuration >= 0) {
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.get(i).setDuration(duration);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Removes the specified child transition from this group.
+ *
+ * @param transition The transition to be removed.
+ */
+ public void removeTransition(Transition transition) {
+ mTransitions.remove(transition);
+ transition.mParent = null;
+ }
+
+ /**
+ * Sets up listeners for each of the child transitions. This is used to
+ * determine when this transition group is finished (all child transitions
+ * must finish first).
+ */
+ private void setupStartEndListeners() {
+ TransitionGroupListener listener = new TransitionGroupListener(this);
+ for (Transition childTransition : mTransitions) {
+ childTransition.addListener(listener);
+ }
+ mCurrentListeners = mTransitions.size();
+ }
+
+ /**
+ * This listener is used to detect when all child transitions are done, at
+ * which point this transition group is also done.
+ */
+ static class TransitionGroupListener extends TransitionListenerAdapter {
+ TransitionGroup mTransitionGroup;
+ TransitionGroupListener(TransitionGroup transitionGroup) {
+ mTransitionGroup = transitionGroup;
+ }
+ @Override
+ public void onTransitionStart(Transition transition) {
+ if (!mTransitionGroup.mStarted) {
+ mTransitionGroup.startTransition();
+ mTransitionGroup.mStarted = true;
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ --mTransitionGroup.mCurrentListeners;
+ if (mTransitionGroup.mCurrentListeners == 0) {
+ // All child trans
+ mTransitionGroup.mStarted = false;
+ mTransitionGroup.endTransition();
+ }
+ transition.removeListener(this);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void play(ViewGroup sceneRoot, TransitionValuesMaps startValues,
+ TransitionValuesMaps endValues) {
+ for (Transition childTransition : mTransitions) {
+ childTransition.play(sceneRoot, startValues, endValues);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void runAnimations() {
+ setupStartEndListeners();
+ if (!mPlayTogether) {
+ // Setup sequence with listeners
+ // TODO: Need to add listeners in such a way that we can remove them later if canceled
+ for (int i = 1; i < mTransitions.size(); ++i) {
+ Transition previousTransition = mTransitions.get(i - 1);
+ final Transition nextTransition = mTransitions.get(i);
+ previousTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ nextTransition.runAnimations();
+ transition.removeListener(this);
+ }
+ });
+ }
+ Transition firstTransition = mTransitions.get(0);
+ if (firstTransition != null) {
+ firstTransition.runAnimations();
+ }
+ } else {
+ for (Transition childTransition : mTransitions) {
+ childTransition.runAnimations();
+ }
+ }
+ }
+
+ @Override
+ protected void captureValues(TransitionValues transitionValues, boolean start) {
+ int targetId = transitionValues.view.getId();
+ for (Transition childTransition : mTransitions) {
+ if (childTransition.isValidTarget(transitionValues.view, targetId)) {
+ childTransition.captureValues(transitionValues, start);
+ }
+ }
+ }
+
+ @Override
+ protected void cancelTransition() {
+ super.cancelTransition();
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.get(i).cancelTransition();
+ }
+ }
+
+ @Override
+ void setSceneRoot(ViewGroup sceneRoot) {
+ super.setSceneRoot(sceneRoot);
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.get(i).setSceneRoot(sceneRoot);
+ }
+ }
+
+ @Override
+ String toString(String indent) {
+ String result = super.toString(indent);
+ for (int i = 0; i < mTransitions.size(); ++i) {
+ result += "\n" + mTransitions.get(i).toString(indent + " ");
+ }
+ return result;
+ }
+
+ @Override
+ public TransitionGroup clone() {
+ TransitionGroup clone = (TransitionGroup) super.clone();
+ clone.mTransitions = new ArrayList<Transition>();
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; ++i) {
+ clone.mTransitions.add((Transition) mTransitions.get(i).clone());
+ }
+ return clone;
+ }
+
+}
diff --git a/core/java/android/view/transition/TransitionInflater.java b/core/java/android/view/transition/TransitionInflater.java
new file mode 100644
index 0000000..be658af
--- /dev/null
+++ b/core/java/android/view/transition/TransitionInflater.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.Xml;
+import android.view.InflateException;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * This class inflates scenes and transitions from resource files.
+ */
+public class TransitionInflater {
+
+ // We only need one inflater for any given context. Also, this allows us to associate
+ // ids with unique instances per-Context, used to avoid re-inflating
+ // already-inflated resources into new/different instances
+ private static final ArrayMap<Context, TransitionInflater> sInflaterMap =
+ new ArrayMap<Context, TransitionInflater>();
+
+ private Context mContext;
+ // TODO: do we need id maps for transitions and transitionMgrs as well?
+ SparseArray<Scene> mScenes = new SparseArray<Scene>();
+
+ private TransitionInflater(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Obtains the TransitionInflater from the given context.
+ */
+ public static TransitionInflater from(Context context) {
+ TransitionInflater inflater = sInflaterMap.get(context);
+ if (inflater != null) {
+ return inflater;
+ }
+ inflater = new TransitionInflater(context);
+ sInflaterMap.put(context, inflater);
+ return inflater;
+ }
+
+ /**
+ * Loads a {@link Transition} object from a resource
+ *
+ * @param resource The resource id of the transition to load
+ * @return The loaded Transition object
+ * @throws android.content.res.Resources.NotFoundException when the
+ * transition cannot be loaded
+ */
+ public Transition inflateTransition(int resource) {
+ XmlResourceParser parser = mContext.getResources().getXml(resource);
+ try {
+ return createTransitionFromXml(parser, Xml.asAttributeSet(parser), null);
+ } catch (XmlPullParserException e) {
+ InflateException ex = new InflateException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } catch (IOException e) {
+ InflateException ex = new InflateException(
+ parser.getPositionDescription()
+ + ": " + e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } finally {
+ parser.close();
+ }
+ }
+
+ /**
+ * Loads a {@link TransitionManager} object from a resource
+ *
+ *
+ *
+ * @param resource The resource id of the transition manager to load
+ * @return The loaded TransitionManager object
+ * @throws android.content.res.Resources.NotFoundException when the
+ * transition manager cannot be loaded
+ */
+ public TransitionManager inflateTransitionManager(int resource, ViewGroup sceneRoot) {
+ XmlResourceParser parser = mContext.getResources().getXml(resource);
+ try {
+ return createTransitionManagerFromXml(parser, Xml.asAttributeSet(parser), sceneRoot);
+ } catch (XmlPullParserException e) {
+ InflateException ex = new InflateException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } catch (IOException e) {
+ InflateException ex = new InflateException(
+ parser.getPositionDescription()
+ + ": " + e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } finally {
+ parser.close();
+ }
+ }
+
+ /**
+ * Loads a {@link Scene} object from a resource
+ *
+ * @param resource The resource id of the scene to load
+ * @return The loaded Scene object
+ * @throws android.content.res.Resources.NotFoundException when the scene
+ * cannot be loaded
+ */
+ public Scene inflateScene(int resource, ViewGroup parent) {
+ Scene scene = mScenes.get(resource);
+ if (scene != null) {
+ return scene;
+ }
+ XmlResourceParser parser = mContext.getResources().getXml(resource);
+ try {
+ scene = createSceneFromXml(parser, Xml.asAttributeSet(parser), parent);
+ mScenes.put(resource, scene);
+ return scene;
+ } catch (XmlPullParserException e) {
+ InflateException ex = new InflateException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } catch (IOException e) {
+ InflateException ex = new InflateException(
+ parser.getPositionDescription()
+ + ": " + e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } finally {
+ parser.close();
+ }
+ }
+
+
+ //
+ // Transition loading
+ //
+
+ private Transition createTransitionFromXml(XmlPullParser parser,
+ AttributeSet attrs, TransitionGroup transitionGroup)
+ throws XmlPullParserException, IOException {
+
+ Transition transition = null;
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ boolean newTransition = false;
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ if ("fade".equals(name)) {
+ transition = new Fade();
+ newTransition = true;
+ } else if ("move".equals(name)) {
+ transition = new Move();
+ newTransition = true;
+ } else if ("slide".equals(name)) {
+ transition = new Slide();
+ newTransition = true;
+ } else if ("autoTransition".equals(name)) {
+ transition = new AutoTransition();
+ newTransition = true;
+ } else if ("recolor".equals(name)) {
+ transition = new Recolor();
+ newTransition = true;
+ } else if ("transitionGroup".equals(name)) {
+ transition = new TransitionGroup();
+ createTransitionFromXml(parser, attrs, ((TransitionGroup) transition));
+ newTransition = true;
+ } else if ("targets".equals(name)) {
+ if (parser.getDepth() - 1 > depth && transition != null) {
+ // We're inside the child tag - add targets to the child
+ getTargetIDs(parser, attrs, transition);
+ } else if (parser.getDepth() - 1 == depth && transitionGroup != null) {
+ // add targets to the group
+ getTargetIDs(parser, attrs, transitionGroup);
+ }
+ }
+ if (transition != null || "targets".equals(name)) {
+ if (newTransition) {
+ loadTransition(transition, attrs);
+ if (transitionGroup != null) {
+ transitionGroup.addTransitions(transition);
+ }
+ }
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+
+ return transition;
+ }
+
+ private void getTargetIDs(XmlPullParser parser,
+ AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException {
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+
+ ArrayList<Integer> targetIds = new ArrayList<Integer>();
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ if (name.equals("target")) {
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.Transition);
+ int id = a.getResourceId(com.android.internal.R.styleable.Transition_targetID, -1);
+ if (id >= 0) {
+ targetIds.add(id);
+ }
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+ int numTargets = targetIds.size();
+ if (numTargets > 0) {
+ int[] targetsArray = new int[numTargets];
+ for (int i = 0; i < targetIds.size(); ++i) {
+ targetsArray[i] = targetIds.get(i);
+ }
+ transition.setTargetIds(targetsArray);
+ }
+ }
+
+ private Transition loadTransition(Transition transition, AttributeSet attrs)
+ throws Resources.NotFoundException {
+
+ TypedArray a =
+ mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Transition);
+ long duration = a.getInt(com.android.internal.R.styleable.Transition_duration, -1);
+ if (duration >= 0) {
+ transition.setDuration(duration);
+ }
+ long startOffset = a.getInt(com.android.internal.R.styleable.Transition_startOffset, -1);
+ if (startOffset > 0) {
+ transition.setStartDelay(startOffset);
+ }
+ final int resID =
+ a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
+ if (resID > 0) {
+ transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID));
+ }
+ a.recycle();
+ return transition;
+ }
+
+ //
+ // TransitionManager loading
+ //
+
+ private TransitionManager createTransitionManagerFromXml(XmlPullParser parser,
+ AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException {
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+ TransitionManager transitionManager = null;
+
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ if (name.equals("transitionManager")) {
+ transitionManager = new TransitionManager();
+ } else if (name.equals("transition") && (transitionManager != null)) {
+ loadTransition(attrs, sceneRoot, transitionManager);
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+ return transitionManager;
+ }
+
+ private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot,
+ TransitionManager transitionManager)
+ throws Resources.NotFoundException {
+
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.TransitionManager);
+ int transitionId = attrs.getAttributeResourceValue(
+ com.android.internal.R.styleable.TransitionManager_transition, -1);
+ Scene fromScene = null, toScene = null;
+ int fromId = attrs.getAttributeResourceValue(
+ com.android.internal.R.styleable.TransitionManager_fromScene, -1);
+ if (fromId >= 0) fromScene = inflateScene(fromId, sceneRoot);
+ int toId = attrs.getAttributeResourceValue(
+ com.android.internal.R.styleable.TransitionManager_toScene, -1);
+ if (toId >= 0) toScene = inflateScene(toId, sceneRoot);
+ if (transitionId >= 0) {
+ Transition transition = inflateTransition(transitionId);
+ if (transition != null) {
+ if (fromScene != null) {
+ if (toScene == null){
+ throw new RuntimeException("No matching toScene for given fromScene " +
+ "for transition ID " + transitionId);
+ } else {
+ transitionManager.setTransition(fromScene, toScene, transition);
+ }
+ } else if (toId >= 0) {
+ transitionManager.setTransition(toScene, transition);
+ }
+ }
+ }
+ a.recycle();
+ }
+
+ //
+ // Scene loading
+ //
+
+ private Scene createSceneFromXml(XmlPullParser parser, AttributeSet attrs, ViewGroup parent)
+ throws XmlPullParserException, IOException {
+ Scene scene = null;
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ if (name.equals("scene")) {
+ scene = loadScene(attrs, parent);
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+
+ return scene;
+ }
+
+ private Scene loadScene(AttributeSet attrs, ViewGroup parent)
+ throws Resources.NotFoundException {
+
+ Scene scene;
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.Scene);
+ int layoutId = a.getResourceId(com.android.internal.R.styleable.Scene_layout, -1);
+ if (layoutId >= 0) {
+ scene = new Scene(parent, layoutId, mContext);
+ } else {
+ scene = new Scene(parent);
+ }
+ a.recycle();
+ return scene;
+ }
+}
diff --git a/core/java/android/view/transition/TransitionManager.java b/core/java/android/view/transition/TransitionManager.java
new file mode 100644
index 0000000..59b07b1
--- /dev/null
+++ b/core/java/android/view/transition/TransitionManager.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.util.ArrayMap;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import java.util.ArrayList;
+
+/**
+ * This class manages the set of transitions that fire when there is a
+ * change of {@link Scene}. To use the manager, add scenes along with
+ * transition objects with calls to {@link #setTransition(Scene, Transition)}
+ * or {@link #setTransition(Scene, Scene, Transition)}. Setting specific
+ * transitions for scene changes is not required; by default, a Scene change
+ * will use {@link AutoTransition} to do something reasonable for most
+ * situations. Specifying other transitions for particular scene changes is
+ * only necessary if the application wants different transition behavior
+ * in these situations.
+ */
+public class TransitionManager {
+ // TODO: how to handle enter/exit?
+
+ private static final Transition sDefaultTransition = new AutoTransition();
+ private Transition mDefaultTransition = new AutoTransition();
+
+ ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>();
+ ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions =
+ new ArrayMap<Scene, ArrayMap<Scene, Transition>>();
+ static ArrayMap<ViewGroup, Transition> sRunningTransitions =
+ new ArrayMap<ViewGroup, Transition>();
+ private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>();
+
+
+ /**
+ * Sets the transition to be used for any scene change for which no
+ * other transition is explicitly set. The initial value is
+ * an {@link AutoTransition} instance.
+ *
+ * @param transition The default transition to be used for scene changes.
+ */
+ public void setDefaultTransition(Transition transition) {
+ mDefaultTransition = transition;
+ }
+
+ /**
+ * Gets the current default transition. The initial value is an {@link
+ * AutoTransition} instance.
+ *
+ * @return The current default transition.
+ * @see #setDefaultTransition(Transition)
+ */
+ public Transition getDefaultTransition() {
+ return mDefaultTransition;
+ }
+
+ /**
+ * Sets a specific transition to occur when the given scene is entered.
+ *
+ * @param scene The scene which, when applied, will cause the given
+ * transition to run.
+ * @param transition The transition that will play when the given scene is
+ * entered. A value of null will result in the default behavior of
+ * using {@link AutoTransition}.
+ */
+ public void setTransition(Scene scene, Transition transition) {
+ mSceneTransitions.put(scene, transition);
+ }
+
+ /**
+ * Sets a specific transition to occur when the given pair of scenes is
+ * exited/entered.
+ *
+ * @param fromScene The scene being exited when the given transition will
+ * be run
+ * @param toScene The scene being entered when the given transition will
+ * be run
+ * @param transition The transition that will play when the given scene is
+ * entered. A value of null will result in the default behavior of
+ * using {@link AutoTransition}.
+ */
+ public void setTransition(Scene fromScene, Scene toScene, Transition transition) {
+ ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
+ if (sceneTransitionMap == null) {
+ sceneTransitionMap = new ArrayMap<Scene, Transition>();
+ mScenePairTransitions.put(toScene, sceneTransitionMap);
+ }
+ sceneTransitionMap.put(fromScene, transition);
+ }
+
+ /**
+ * Returns the Transition for the given scene being entered. The result
+ * depends not only on the given scene, but also the scene which the
+ * {@link Scene#getSceneRoot() sceneRoot} of the Scene is currently in.
+ *
+ * @param scene The scene being entered
+ * @return The Transition to be used for the given scene change. If no
+ * Transition was specified for this scene change, {@link AutoTransition}
+ * will be used instead.
+ */
+ private Transition getTransition(Scene scene) {
+ Transition transition = null;
+ ViewGroup sceneRoot = scene.getSceneRoot();
+ if (sceneRoot != null) {
+ // TODO: cached in Scene instead? long-term, cache in View itself
+ Scene currScene = sceneRoot.getCurrentScene();
+ if (currScene != null) {
+ ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(scene);
+ if (sceneTransitionMap != null) {
+ transition = sceneTransitionMap.get(currScene);
+ if (transition != null) {
+ return transition;
+ }
+ }
+ }
+ }
+ transition = mSceneTransitions.get(scene);
+ return (transition != null) ? transition : new AutoTransition();
+ }
+
+ /**
+ * This is where all of the work of a transition/scene-change is
+ * orchestrated. This method captures the start values for the given
+ * transition, exits the current Scene, enters the new scene, captures
+ * the end values for the transition, and finally plays the
+ * resulting values-populated transition.
+ *
+ * @param scene The scene being entered
+ * @param transition The transition to play for this scene change
+ */
+ private static void changeScene(Scene scene, Transition transition) {
+
+ final ViewGroup sceneRoot = scene.getSceneRoot();
+
+ Transition transitionClone = transition.clone();
+ transitionClone.setSceneRoot(sceneRoot);
+
+ sceneChangeSetup(sceneRoot, transitionClone);
+
+ scene.enter();
+
+ sceneChangeRunTransition(sceneRoot, transitionClone);
+ }
+
+ private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
+ final Transition transition) {
+ if (transition != null) {
+ final ViewTreeObserver observer = sceneRoot.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ // Add to running list, handle end to remove it
+ sRunningTransitions.put(sceneRoot, transition);
+ transition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ sRunningTransitions.remove(sceneRoot);
+ }
+ });
+ transition.captureValues(sceneRoot, false);
+ transition.playTransition(sceneRoot);
+ return true;
+ }
+ });
+ }
+ }
+
+ private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
+
+ Transition runningTransition = sRunningTransitions.get(sceneRoot);
+ if (runningTransition != null) {
+ runningTransition.cancelTransition();
+ }
+
+ // Capture current values
+ if (transition != null) {
+ transition.captureValues(sceneRoot, true);
+ }
+
+ // Notify previous scene that it is being exited
+ Scene previousScene = sceneRoot.getCurrentScene();
+ if (previousScene != null) {
+ previousScene.exit();
+ }
+ }
+
+ /**
+ * Change to the given scene, using the
+ * appropriate transition for this particular scene change
+ * (as specified to the TransitionManager, or the default
+ * if no such transition exists).
+ *
+ * @param scene The Scene to change to
+ */
+ public void transitionTo(Scene scene) {
+ // Auto transition if there is no transition declared for the Scene, but there is
+ // a root or parent view
+ changeScene(scene, getTransition(scene));
+
+ }
+
+ /**
+ * Static utility method to simply change to the given scene using
+ * the default transition for TransitionManager.
+ *
+ * @param scene The Scene to change to
+ */
+ public static void go(Scene scene) {
+ changeScene(scene, sDefaultTransition);
+ }
+
+ /**
+ * Static utility method to simply change to the given scene using
+ * the given transition.
+ *
+ * <p>Passing in <code>null</code> for the transition parameter will
+ * result in the scene changing without any transition running, and is
+ * equivalent to calling {@link Scene#exit()} on the scene root's
+ * {@link ViewGroup#getCurrentScene() current scene}, followed by
+ * {@link Scene#enter()} on the scene specified by the <code>scene</code>
+ * parameter.</p>
+ *
+ * @param scene The Scene to change to
+ * @param transition The transition to use for this scene change. A
+ * value of null causes the scene change to happen with no transition.
+ */
+ public static void go(Scene scene, Transition transition) {
+ changeScene(scene, transition);
+ }
+
+ /**
+ * Static utility method to simply change to a scene defined by the
+ * code in the given runnable, which will be executed after
+ * the current values have been captured for the transition.
+ * This is equivalent to creating a Scene and calling {@link
+ * Scene#setEnterAction(Runnable)} with the runnable, then calling
+ * {@link #go(Scene, Transition)}. The transition used will be the
+ * default provided by TransitionManager.
+ *
+ * @param sceneRoot The root of the View hierarchy used when this scene
+ * runs a transition automatically.
+ * @param action The runnable whose {@link Runnable#run() run()} method will
+ * be called.
+ */
+ public static void go(ViewGroup sceneRoot, Runnable action) {
+ Scene scene = new Scene(sceneRoot);
+ scene.setEnterAction(action);
+ changeScene(scene, sDefaultTransition);
+ }
+
+ /**
+ * Static utility method to simply change to a scene defined by the
+ * code in the given runnable, which will be executed after
+ * the current values have been captured for the transition.
+ * This is equivalent to creating a Scene and calling {@link
+ * Scene#setEnterAction(Runnable)} with the runnable, then calling
+ * {@link #go(Scene, Transition)}. The given transition will be
+ * used to animate the changes.
+ *
+ * <p>Passing in <code>null</code> for the transition parameter will
+ * result in the scene changing without any transition running, and is
+ * equivalent to calling {@link Scene#exit()} on the scene root's
+ * {@link ViewGroup#getCurrentScene() current scene}, followed by
+ * {@link Scene#enter()} on a new scene specified by the
+ * <code>action</code> parameter.</p>
+ *
+ * @param sceneRoot The root of the View hierarchy to run the transition on.
+ * @param action The runnable whose {@link Runnable#run() run()} method will
+ * be called.
+ * @param transition The transition to use for this change. A
+ * value of null causes the change to happen with no transition.
+ */
+ public static void go(ViewGroup sceneRoot, Runnable action, Transition transition) {
+ Scene scene = new Scene(sceneRoot);
+ scene.setEnterAction(action);
+ changeScene(scene, transition);
+ }
+
+ /**
+ * Static utility method to animate to a new scene defined by all changes within
+ * the given scene root between calling this method and the next rendering frame.
+ * Calling this method causes TransitionManager to capture current values in the
+ * scene root and then post a request to run a transition on the next frame.
+ * At that time, the new values in the scene root will be captured and changes
+ * will be animated. There is no need to create a Scene; it is implied by
+ * changes which take place between calling this method and the next frame when
+ * the transition begins.
+ *
+ * <p>Calling this method several times before the next frame (for example, if
+ * unrelated code also wants to make dynamic changes and run a transition on
+ * the same scene root), only the first call will trigger capturing values
+ * and exiting the current scene. Subsequent calls to the method with the
+ * same scene root during the same frame will be ignored.</p>
+ *
+ * <p>Passing in <code>null</code> for the transition parameter will
+ * cause the TransitionManager to use its default transition.</p>
+ *
+ * @param sceneRoot The root of the View hierarchy to run the transition on.
+ * @param transition The transition to use for this change. A
+ * value of null causes the TransitionManager to use the default transition.
+ */
+ public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {
+
+ if (!sPendingTransitions.contains(sceneRoot)) {
+ sPendingTransitions.add(sceneRoot);
+ if (transition == null) {
+ transition = sDefaultTransition;
+ }
+ final Transition finalTransition = transition.clone();
+ sceneChangeSetup(sceneRoot, transition);
+ sceneRoot.setCurrentScene(null);
+ sceneRoot.postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ sPendingTransitions.remove(sceneRoot);
+ sceneChangeRunTransition(sceneRoot, finalTransition);
+ }
+ });
+ }
+ }
+}
diff --git a/core/java/android/view/transition/TransitionValues.java b/core/java/android/view/transition/TransitionValues.java
new file mode 100644
index 0000000..f361666
--- /dev/null
+++ b/core/java/android/view/transition/TransitionValues.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.util.ArrayMap;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Map;
+
+/**
+ * Data structure which holds cached values for the transition.
+ * The view field is the target which all of the values pertain to.
+ * The values field is a map which holds information for fields
+ * according to names selected by the transitions. These names should
+ * be unique to avoid clobbering values stored by other transitions,
+ * such as the convention project:transition_name:property_name. For
+ * example, the platform might store a property "alpha" in a transition
+ * "Fader" as "android:fader:alpha".
+ *
+ * <p>These values are cached during the
+ * {@link Transition#captureValues(TransitionValues, boolean)}
+ * capture} phases of a scene change, once when the start values are captured
+ * and again when the end values are captured. These start/end values are then
+ * passed into the transitions via the
+ * for {@link Transition#play(ViewGroup, TransitionValues, TransitionValues)}
+ * method.</p>
+ */
+public class TransitionValues {
+
+ /**
+ * The View with these values
+ */
+ public View view;
+
+ /**
+ * The set of values tracked by transitions for this scene
+ */
+ public final Map<String, Object> values = new ArrayMap<String, Object>();
+
+ @Override
+ public String toString() {
+ String returnValue = "TransitionValues@" + Integer.toHexString(hashCode()) + ":\n";
+ returnValue += " view = " + view + "\n";
+ returnValue += " values:";
+ for (String s : values.keySet()) {
+ returnValue += " " + s + ": " + values.get(s) + "\n";
+ }
+ return returnValue;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/view/transition/TransitionValuesMaps.java b/core/java/android/view/transition/TransitionValuesMaps.java
new file mode 100644
index 0000000..4cfce4d
--- /dev/null
+++ b/core/java/android/view/transition/TransitionValuesMaps.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.view.View;
+
+class TransitionValuesMaps {
+ ArrayMap<View, TransitionValues> viewValues =
+ new ArrayMap<View, TransitionValues>();
+ SparseArray<TransitionValues> idValues = new SparseArray<TransitionValues>();
+ LongSparseArray<TransitionValues> itemIdValues =
+ new LongSparseArray<TransitionValues>();
+}
diff --git a/core/java/android/view/transition/Visibility.java b/core/java/android/view/transition/Visibility.java
new file mode 100644
index 0000000..6d39ab6
--- /dev/null
+++ b/core/java/android/view/transition/Visibility.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.transition;
+
+import android.animation.Animator;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+/**
+ * This transition tracks changes to the visibility of target views in the
+ * start and end scenes. Visibility is determined not just by the
+ * {@link View#setVisibility(int)} state of views, but also whether
+ * views exist in the current view hierarchy. The class is intended to be a
+ * utility for subclasses such as {@link Fade}, which use this visibility
+ * information to determine the specific animations to run when visibility
+ * changes occur. Subclasses should implement one or more of the methods
+ * {@link #appear(ViewGroup, TransitionValues, int, TransitionValues, int)},
+ * {@link #disappear(ViewGroup, TransitionValues, int, TransitionValues, int)},
+ * {@link #appear(ViewGroup, TransitionValues, int, TransitionValues, int)}, and
+ * {@link #disappear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
+ */
+public abstract class Visibility extends Transition {
+
+ private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
+ private static final String PROPNAME_PARENT = "android:visibility:parent";
+
+ private static class VisibilityInfo {
+ boolean visibilityChange;
+ boolean fadeIn;
+ int startVisibility;
+ int endVisibility;
+ View startParent;
+ View endParent;
+ }
+
+ // Temporary structure, used in calculating state in setup() and play()
+ private VisibilityInfo mTmpVisibilityInfo = new VisibilityInfo();
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ int visibility = values.view.getVisibility();
+ values.values.put(PROPNAME_VISIBILITY, visibility);
+ values.values.put(PROPNAME_PARENT, values.view.getParent());
+ }
+
+ private boolean isHierarchyVisibilityChanging(ViewGroup sceneRoot, ViewGroup view) {
+
+ if (view == sceneRoot) {
+ return false;
+ }
+ TransitionValues startValues = getTransitionValues(view, true);
+ TransitionValues endValues = getTransitionValues(view, false);
+
+ if (startValues == null || endValues == null) {
+ return true;
+ }
+ int startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
+ View startParent = (View) startValues.values.get(PROPNAME_PARENT);
+ int endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
+ View endParent = (View) endValues.values.get(PROPNAME_PARENT);
+ if (startVisibility != endVisibility || startParent != endParent) {
+ return true;
+ }
+
+ ViewParent parent = view.getParent();
+ if (parent instanceof ViewGroup && parent != sceneRoot) {
+ return isHierarchyVisibilityChanging(sceneRoot, (ViewGroup) parent);
+ }
+ return false;
+ }
+
+ private VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues,
+ TransitionValues endValues) {
+ final VisibilityInfo visInfo = mTmpVisibilityInfo;
+ visInfo.visibilityChange = false;
+ visInfo.fadeIn = false;
+ if (startValues != null) {
+ visInfo.startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
+ visInfo.startParent = (View) startValues.values.get(PROPNAME_PARENT);
+ } else {
+ visInfo.startVisibility = -1;
+ visInfo.startParent = null;
+ }
+ if (endValues != null) {
+ visInfo.endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
+ visInfo.endParent = (View) endValues.values.get(PROPNAME_PARENT);
+ } else {
+ visInfo.endVisibility = -1;
+ visInfo.endParent = null;
+ }
+ if (startValues != null && endValues != null) {
+ if (visInfo.startVisibility == visInfo.endVisibility &&
+ visInfo.startParent == visInfo.endParent) {
+ return visInfo;
+ } else {
+ if (visInfo.startVisibility != visInfo.endVisibility) {
+ if (visInfo.startVisibility == View.VISIBLE) {
+ visInfo.fadeIn = false;
+ visInfo.visibilityChange = true;
+ } else if (visInfo.endVisibility == View.VISIBLE) {
+ visInfo.fadeIn = true;
+ visInfo.visibilityChange = true;
+ }
+ // no visibilityChange if going between INVISIBLE and GONE
+ } else if (visInfo.startParent != visInfo.endParent) {
+ if (visInfo.endParent == null) {
+ visInfo.fadeIn = false;
+ visInfo.visibilityChange = true;
+ } else if (visInfo.startParent == null) {
+ visInfo.fadeIn = true;
+ visInfo.visibilityChange = true;
+ }
+ }
+ }
+ }
+ if (startValues == null) {
+ visInfo.fadeIn = true;
+ visInfo.visibilityChange = true;
+ } else if (endValues == null) {
+ visInfo.fadeIn = false;
+ visInfo.visibilityChange = true;
+ }
+ return visInfo;
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
+ // Ensure not in parent hierarchy that's also becoming visible/invisible
+ if (visInfo.visibilityChange) {
+ ViewGroup parent = (ViewGroup) ((visInfo.endParent != null) ?
+ visInfo.endParent : visInfo.startParent);
+ if (parent != null) {
+ if (!isHierarchyVisibilityChanging(sceneRoot, parent)) {
+ if (visInfo.fadeIn) {
+ return appear(sceneRoot, startValues, visInfo.startVisibility,
+ endValues, visInfo.endVisibility);
+ } else {
+ return disappear(sceneRoot, startValues, visInfo.startVisibility,
+ endValues, visInfo.endVisibility
+ );
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to set up anything prior to the
+ * transition starting.
+ *
+ * @param sceneRoot
+ * @param startValues
+ * @param startVisibility
+ * @param endValues
+ * @param endVisibility
+ * @return
+ */
+ protected Animator appear(ViewGroup sceneRoot,
+ TransitionValues startValues, int startVisibility,
+ TransitionValues endValues, int endVisibility) {
+ return null;
+ }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to set up anything prior to the
+ * transition starting.
+ *
+ * @param sceneRoot
+ * @param startValues
+ * @param startVisibility
+ * @param endValues
+ * @param endVisibility
+ * @return
+ */
+ protected Animator disappear(ViewGroup sceneRoot,
+ TransitionValues startValues, int startVisibility,
+ TransitionValues endValues, int endVisibility) {
+ return null;
+ }
+
+}
diff --git a/core/java/android/view/transition/package.html b/core/java/android/view/transition/package.html
new file mode 100644
index 0000000..37dc0ec
--- /dev/null
+++ b/core/java/android/view/transition/package.html
@@ -0,0 +1,25 @@
+<html>
+<body>
+<p>The classes in this package enable "scenes & transitions" functionality for
+view hiearchies.</p>
+
+<p>A <b>Scene</b> is an encapsulation of the state of a view hiearchy,
+including the views in that hierarchy and the various values (layout-related
+and otherwise) that those views have. A scene be defined by a layout hierarchy
+directly or some code which sets up the scene dynamically as it is entered.</p>
+
+<p>A <b>Transition</b> is a mechanism to automatically animate changes that occur
+when a new scene is entered. Some transition capabilities are automatic. That
+is, entering a scene may cause animations to run which fade out views that
+go away, move and resize existing views that change, and fade in views that
+become visible. There are additional transitions that can animate other
+attributes, such as color changes, and which can optionally be specified
+to take place during particular scene changes. Finally, developers can
+define their own Transition subclasses which monitor particular property
+changes and which run custom animations when those properties change values.</p>
+
+<p><b>TransitionManager</b> is used to specify custom transitions for particular
+scene changes, and to cause scene changes with transitions to take place.</p>
+
+</body>
+</html>
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 8ae0021..19492c2 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -500,6 +500,12 @@
* Sets the default zoom density of the page. This must be called from the UI
* thread. The default is {@link ZoomDensity#MEDIUM}.
*
+ * This setting is not recommended for use in new applications. If the WebView
+ * is utilized to display mobile-oriented pages, the desired effect can be achieved by
+ * adjusting 'width' and 'initial-scale' attributes of page's 'meta viewport'
+ * tag. For pages lacking the tag, {@link android.webkit.WebView#setInitialScale}
+ * and {@link #setUseWideViewPort} can be used.
+ *
* @param zoom the zoom density
*/
public void setDefaultZoom(ZoomDensity zoom) {
@@ -510,6 +516,8 @@
* Gets the default zoom density of the page. This should be called from
* the UI thread.
*
+ * This setting is not recommended for use in new applications.
+ *
* @return the zoom density
* @see #setDefaultZoom
*/
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4998742..0149f03 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -41,6 +41,7 @@
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.AbsoluteLayout;
@@ -2051,6 +2052,13 @@
}
*/
+ @Override
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ AccessibilityNodeProvider provider =
+ mProvider.getViewDelegate().getAccessibilityNodeProvider();
+ return provider == null ? super.getAccessibilityNodeProvider() : provider;
+ }
+
@Deprecated
@Override
public boolean shouldDelayChildPressedState() {
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index a324502..5a9e6c9 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -66,6 +66,7 @@
import android.text.InputType;
import android.text.Selection;
import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -89,6 +90,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -1293,6 +1295,19 @@
// WebViewProvider bindings
static class Factory implements WebViewFactoryProvider, WebViewFactoryProvider.Statics {
+ Factory() {
+ // Touch JniUtil and WebViewCore in case this is being called from
+ // WebViewFactory.Preloader, to ensure that the JNI libraries that they use are
+ // preloaded in the zygote.
+ try {
+ Class.forName("android.webkit.JniUtil");
+ Class.forName("android.webkit.WebViewCore");
+ } catch (ClassNotFoundException e) {
+ Log.e(LOGTAG, "failed to load JNI libraries");
+ throw new AndroidRuntimeException(e);
+ }
+ }
+
@Override
public String findAddress(String addr) {
return WebViewClassic.findAddress(addr);
@@ -1755,6 +1770,11 @@
}
@Override
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ return null;
+ }
+
+ @Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
if (!mWebView.isEnabled()) {
// Only default actions are supported while disabled.
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 00d87bd..2ee0961 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -19,37 +19,56 @@
import android.os.Build;
import android.os.StrictMode;
import android.os.SystemProperties;
+import android.util.AndroidRuntimeException;
import android.util.Log;
-import dalvik.system.PathClassLoader;
-
/**
* Top level factory, used creating all the main WebView implementation classes.
*
* @hide
*/
public final class WebViewFactory {
+ public static final boolean DEFAULT_TO_EXPERIMENTAL_WEBVIEW = false;
public static final String WEBVIEW_EXPERIMENTAL_PROPERTY = "persist.sys.webview.exp";
- private static final String DEPRECATED_CHROMIUM_PROPERTY = "webview.use_chromium";
+ private static final String FORCE_PROVIDER_PROPERTY = "webview.force_provider";
+ private static final String FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM = "chromium";
+ private static final String FORCE_PROVIDER_PROPERTY_VALUE_CLASSIC = "classic";
// Default Provider factory class name.
// TODO: When the Chromium powered WebView is ready, it should be the default factory class.
private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory";
private static final String CHROMIUM_WEBVIEW_FACTORY =
"com.android.webview.chromium.WebViewChromiumFactoryProvider";
- private static final String CHROMIUM_WEBVIEW_JAR = "/system/framework/webviewchromium.jar";
private static final String LOGTAG = "WebViewFactory";
private static final boolean DEBUG = false;
+ private static class Preloader {
+ static WebViewFactoryProvider sPreloadedProvider;
+ static {
+ try {
+ sPreloadedProvider = getFactoryClass().newInstance();
+ } catch (Exception e) {
+ Log.w(LOGTAG, "error preloading provider", e);
+ }
+ }
+ }
+
// Cache the factory both for efficiency, and ensure any one process gets all webviews from the
// same provider.
private static WebViewFactoryProvider sProviderInstance;
private static final Object sProviderLock = new Object();
public static boolean isExperimentalWebViewAvailable() {
- return Build.IS_DEBUGGABLE && (new java.io.File(CHROMIUM_WEBVIEW_JAR).exists());
+ try {
+ // Pass false so we don't initialize the class at this point, as this will be wasted if
+ // it's not enabled.
+ Class.forName(CHROMIUM_WEBVIEW_FACTORY, false, WebViewFactory.class.getClassLoader());
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
}
static WebViewFactoryProvider getProvider() {
@@ -58,66 +77,59 @@
// us honest and minimize usage of WebViewClassic internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
- if (isExperimentalWebViewEnabled()) {
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- try {
- sProviderInstance = loadChromiumProvider();
- if (DEBUG) Log.v(LOGTAG, "Loaded Chromium provider: " + sProviderInstance);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
+ Class<WebViewFactoryProvider> providerClass;
+ try {
+ providerClass = getFactoryClass();
+ } catch (ClassNotFoundException e) {
+ Log.e(LOGTAG, "error loading provider", e);
+ throw new AndroidRuntimeException(e);
}
- if (sProviderInstance == null) {
- if (DEBUG) Log.v(LOGTAG, "Falling back to default provider: "
- + DEFAULT_WEBVIEW_FACTORY);
- sProviderInstance = getFactoryByName(DEFAULT_WEBVIEW_FACTORY,
- WebViewFactory.class.getClassLoader());
- if (sProviderInstance == null) {
- if (DEBUG) Log.v(LOGTAG, "Falling back to explicit linkage");
- sProviderInstance = new WebViewClassic.Factory();
- }
+ // This implicitly loads Preloader even if it wasn't preloaded at boot.
+ if (Preloader.sPreloadedProvider != null &&
+ Preloader.sPreloadedProvider.getClass() == providerClass) {
+ sProviderInstance = Preloader.sPreloadedProvider;
+ if (DEBUG) Log.v(LOGTAG, "Using preloaded provider: " + sProviderInstance);
+ return sProviderInstance;
}
- return sProviderInstance;
+
+ // The preloaded provider isn't the one we wanted; construct our own.
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ try {
+ sProviderInstance = providerClass.newInstance();
+ if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
+ return sProviderInstance;
+ } catch (Exception e) {
+ Log.e(LOGTAG, "error instantiating provider", e);
+ throw new AndroidRuntimeException(e);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
}
- // For debug builds, we allow a system property to specify that we should use the
- // experimtanl Chromium powered WebView. This enables us to switch between
- // implementations at runtime. For user (release) builds, don't allow this.
+ // We allow a system property to specify that we should use the experimental Chromium powered
+ // WebView. This enables us to switch between implementations at runtime.
private static boolean isExperimentalWebViewEnabled() {
- if (!isExperimentalWebViewAvailable())
- return false;
- if (SystemProperties.getBoolean(DEPRECATED_CHROMIUM_PROPERTY, false)) {
- Log.w(LOGTAG, String.format("The property %s has been deprecated. Please use %s.",
- DEPRECATED_CHROMIUM_PROPERTY, WEBVIEW_EXPERIMENTAL_PROPERTY));
- return true;
- }
- return SystemProperties.getBoolean(WEBVIEW_EXPERIMENTAL_PROPERTY, false);
+ if (!isExperimentalWebViewAvailable()) return false;
+ boolean use_experimental_webview = SystemProperties.getBoolean(
+ WEBVIEW_EXPERIMENTAL_PROPERTY, DEFAULT_TO_EXPERIMENTAL_WEBVIEW);
+ String forceProviderName = SystemProperties.get(FORCE_PROVIDER_PROPERTY);
+ if (forceProviderName.isEmpty()) return use_experimental_webview;
+
+ Log.i(LOGTAG, String.format("Provider overridden by property: %s=%s",
+ FORCE_PROVIDER_PROPERTY, forceProviderName));
+ if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM)) return true;
+ if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CLASSIC)) return false;
+ Log.e(LOGTAG, String.format("Unrecognized provider: %s", forceProviderName));
+ return use_experimental_webview;
}
- // TODO: This allows us to have the legacy and Chromium WebView coexist for development
- // and side-by-side testing. After transition, remove this when no longer required.
- private static WebViewFactoryProvider loadChromiumProvider() {
- ClassLoader clazzLoader = new PathClassLoader(CHROMIUM_WEBVIEW_JAR, null,
- WebViewFactory.class.getClassLoader());
- return getFactoryByName(CHROMIUM_WEBVIEW_FACTORY, clazzLoader);
- }
-
- private static WebViewFactoryProvider getFactoryByName(String providerName,
- ClassLoader loader) {
- try {
- if (DEBUG) Log.v(LOGTAG, "attempt to load class " + providerName);
- Class<?> c = Class.forName(providerName, true, loader);
- if (DEBUG) Log.v(LOGTAG, "instantiating factory");
- return (WebViewFactoryProvider) c.newInstance();
- } catch (ClassNotFoundException e) {
- Log.e(LOGTAG, "error loading " + providerName, e);
- } catch (IllegalAccessException e) {
- Log.e(LOGTAG, "error loading " + providerName, e);
- } catch (InstantiationException e) {
- Log.e(LOGTAG, "error loading " + providerName, e);
+ private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {
+ if (isExperimentalWebViewEnabled()) {
+ return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY);
+ } else {
+ return (Class<WebViewFactoryProvider>) Class.forName(DEFAULT_WEBVIEW_FACTORY);
}
- return null;
}
}
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index fa17ab9..41d6333 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -32,6 +32,7 @@
import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.webkit.WebView.HitTestResult;
@@ -279,6 +280,8 @@
interface ViewDelegate {
public boolean shouldDelayChildPressedState();
+ public AccessibilityNodeProvider getAccessibilityNodeProvider();
+
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
public void onInitializeAccessibilityEvent(AccessibilityEvent event);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 219891c..1e20ab2 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -25,11 +25,11 @@
import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.os.Debug;
-import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.StrictMode;
import android.text.Editable;
+import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
@@ -61,9 +61,12 @@
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputMethodManager;
import android.widget.RemoteViews.OnClickHandler;
@@ -1151,10 +1154,10 @@
}
if (mChoiceMode != CHOICE_MODE_NONE) {
if (mCheckStates == null) {
- mCheckStates = new SparseBooleanArray();
+ mCheckStates = new SparseBooleanArray(0);
}
if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
- mCheckedIdStates = new LongSparseArray<Integer>();
+ mCheckedIdStates = new LongSparseArray<Integer>(0);
}
// Modal multi-choice mode only has choices when the mode is active. Clear them.
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
@@ -1277,12 +1280,12 @@
}
/**
- * If fast scroll is visible, then don't draw the vertical scrollbar.
+ * If fast scroll is enabled, then don't draw the vertical scrollbar.
* @hide
*/
@Override
protected boolean isVerticalScrollBarHidden() {
- return mFastScroller != null && mFastScroller.isVisible();
+ return mFastScrollEnabled;
}
/**
@@ -1334,7 +1337,7 @@
*/
void invokeOnItemScrollListener() {
if (mFastScroller != null) {
- mFastScroller.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
+ mFastScroller.onScroll(mFirstPosition, getChildCount(), mItemCount);
}
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
@@ -1615,10 +1618,12 @@
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
+ @Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
+ @Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
@@ -1943,8 +1948,8 @@
}
final int top = getChildAt(0).getTop();
- final float fadeLength = (float) getVerticalFadingEdgeLength();
- return top < mPaddingTop ? (float) -(top - mPaddingTop) / fadeLength : fadeEdge;
+ final float fadeLength = getVerticalFadingEdgeLength();
+ return top < mPaddingTop ? -(top - mPaddingTop) / fadeLength : fadeEdge;
}
}
@@ -1961,9 +1966,9 @@
final int bottom = getChildAt(count - 1).getBottom();
final int height = getHeight();
- final float fadeLength = (float) getVerticalFadingEdgeLength();
+ final float fadeLength = getVerticalFadingEdgeLength();
return bottom > height - mPaddingBottom ?
- (float) (bottom - height + mPaddingBottom) / fadeLength : fadeEdge;
+ (bottom - height + mPaddingBottom) / fadeLength : fadeEdge;
}
}
@@ -2004,7 +2009,7 @@
}
mRecycler.markChildrenDirty();
}
-
+
if (mFastScroller != null && mItemCount != mOldItemCount) {
mFastScroller.onItemCountChanged(mOldItemCount, mItemCount);
}
@@ -2426,7 +2431,7 @@
* @return True if the selector should be shown
*/
boolean shouldShowSelector() {
- return (hasFocus() && !isInTouchMode()) || touchModeDrawsInPressedState();
+ return (!isInTouchMode()) || (touchModeDrawsInPressedState() && isPressed());
}
private void drawSelector(Canvas canvas) {
@@ -2764,13 +2769,14 @@
}
public boolean sameWindow() {
- return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount;
+ return getWindowAttachCount() == mOriginalAttachCount;
}
}
private class PerformClick extends WindowRunnnable implements Runnable {
int mClickMotionPosition;
+ @Override
public void run() {
// The data has changed since we posted this action in the event queue,
// bail out before bad things happen
@@ -2792,6 +2798,7 @@
}
private class CheckForLongPress extends WindowRunnnable implements Runnable {
+ @Override
public void run() {
final int motionPosition = mMotionPosition;
final View child = getChildAt(motionPosition - mFirstPosition);
@@ -2815,6 +2822,7 @@
}
private class CheckForKeyLongPress extends WindowRunnnable implements Runnable {
+ @Override
public void run() {
if (isPressed() && mSelectedPosition >= 0) {
int index = mSelectedPosition - mFirstPosition;
@@ -2989,6 +2997,7 @@
}
final class CheckForTap implements Runnable {
+ @Override
public void run() {
if (mTouchMode == TOUCH_MODE_DOWN) {
mTouchMode = TOUCH_MODE_TAP;
@@ -3049,15 +3058,9 @@
mTouchMode = TOUCH_MODE_SCROLL;
mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop;
}
- final Handler handler = getHandler();
- // Handler should not be null unless the AbsListView is not attached to a
- // window, which would make it very hard to scroll it... but the monkeys
- // say it's possible.
- if (handler != null) {
- handler.removeCallbacks(mPendingCheckForLongPress);
- }
+ removeCallbacks(mPendingCheckForLongPress);
setPressed(false);
- View motionView = getChildAt(mMotionPosition - mFirstPosition);
+ final View motionView = getChildAt(mMotionPosition - mFirstPosition);
if (motionView != null) {
motionView.setPressed(false);
}
@@ -3239,6 +3242,7 @@
}
}
+ @Override
public void onTouchModeChanged(boolean isInTouchMode) {
if (isInTouchMode) {
// Get rid of the selection when we enter touch mode
@@ -3299,126 +3303,192 @@
}
}
- final int action = ev.getAction();
-
- View v;
-
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- switch (mTouchMode) {
- case TOUCH_MODE_OVERFLING: {
- mFlingRunnable.endFling();
- if (mPositionScroller != null) {
- mPositionScroller.stop();
- }
- mTouchMode = TOUCH_MODE_OVERSCROLL;
- mMotionX = (int) ev.getX();
- mMotionY = mLastY = (int) ev.getY();
- mMotionCorrection = 0;
- mActivePointerId = ev.getPointerId(0);
- mDirection = 0;
+ final int actionMasked = ev.getActionMasked();
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN: {
+ onTouchDown(ev);
break;
}
- default: {
- mActivePointerId = ev.getPointerId(0);
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
- int motionPosition = pointToPosition(x, y);
- if (!mDataChanged) {
- if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
- && (getAdapter().isEnabled(motionPosition))) {
- // User clicked on an actual view (and was not stopping a fling).
- // It might be a click or a scroll. Assume it is a click until
- // proven otherwise
- mTouchMode = TOUCH_MODE_DOWN;
- // FIXME Debounce
- if (mPendingCheckForTap == null) {
- mPendingCheckForTap = new CheckForTap();
- }
- postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
- } else {
- if (mTouchMode == TOUCH_MODE_FLING) {
- // Stopped a fling. It is a scroll.
- createScrollingCache();
- mTouchMode = TOUCH_MODE_SCROLL;
- mMotionCorrection = 0;
- motionPosition = findMotionRow(y);
- mFlingRunnable.flywheelTouch();
- }
- }
- }
+ case MotionEvent.ACTION_MOVE: {
+ onTouchMove(ev);
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ onTouchUp(ev);
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ onTouchCancel();
+ break;
+ }
+
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(ev);
+ final int x = mMotionX;
+ final int y = mMotionY;
+ final int motionPosition = pointToPosition(x, y);
if (motionPosition >= 0) {
// Remember where the motion event started
- v = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = v.getTop();
+ final View child = getChildAt(motionPosition - mFirstPosition);
+ mMotionViewOriginalTop = child.getTop();
+ mMotionPosition = motionPosition;
}
- mMotionX = x;
- mMotionY = y;
- mMotionPosition = motionPosition;
- mLastY = Integer.MIN_VALUE;
+ mLastY = y;
break;
}
- }
- if (performButtonActionOnTouchDown(ev)) {
- if (mTouchMode == TOUCH_MODE_DOWN) {
- removeCallbacks(mPendingCheckForTap);
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ // New pointers take over dragging duties
+ final int index = ev.getActionIndex();
+ final int id = ev.getPointerId(index);
+ final int x = (int) ev.getX(index);
+ final int y = (int) ev.getY(index);
+ mMotionCorrection = 0;
+ mActivePointerId = id;
+ mMotionX = x;
+ mMotionY = y;
+ final int motionPosition = pointToPosition(x, y);
+ if (motionPosition >= 0) {
+ // Remember where the motion event started
+ final View child = getChildAt(motionPosition - mFirstPosition);
+ mMotionViewOriginalTop = child.getTop();
+ mMotionPosition = motionPosition;
}
+ mLastY = y;
+ break;
}
- break;
}
- case MotionEvent.ACTION_MOVE: {
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == -1) {
- pointerIndex = 0;
- mActivePointerId = ev.getPointerId(pointerIndex);
- }
- final int y = (int) ev.getY(pointerIndex);
+ return true;
+ }
- if (mDataChanged) {
- // Re-sync everything if data has been changed
- // since the scroll operation can query the adapter.
- layoutChildren();
+ private void onTouchDown(MotionEvent ev) {
+ mActivePointerId = ev.getPointerId(0);
+
+ if (mTouchMode == TOUCH_MODE_OVERFLING) {
+ // Stopped the fling. It is a scroll.
+ mFlingRunnable.endFling();
+ if (mPositionScroller != null) {
+ mPositionScroller.stop();
+ }
+ mTouchMode = TOUCH_MODE_OVERSCROLL;
+ mMotionX = (int) ev.getX();
+ mMotionY = (int) ev.getY();
+ mLastY = mMotionY;
+ mMotionCorrection = 0;
+ mDirection = 0;
+ } else {
+ final int x = (int) ev.getX();
+ final int y = (int) ev.getY();
+ int motionPosition = pointToPosition(x, y);
+
+ if (!mDataChanged) {
+ if (mTouchMode == TOUCH_MODE_FLING) {
+ // Stopped a fling. It is a scroll.
+ createScrollingCache();
+ mTouchMode = TOUCH_MODE_SCROLL;
+ mMotionCorrection = 0;
+ motionPosition = findMotionRow(y);
+ mFlingRunnable.flywheelTouch();
+ } else if ((motionPosition >= 0) && getAdapter().isEnabled(motionPosition)) {
+ // User clicked on an actual view (and was not stopping a
+ // fling). It might be a click or a scroll. Assume it is a
+ // click until proven otherwise.
+ mTouchMode = TOUCH_MODE_DOWN;
+
+ // FIXME Debounce
+ if (mPendingCheckForTap == null) {
+ mPendingCheckForTap = new CheckForTap();
+ }
+
+ postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
+ }
}
- switch (mTouchMode) {
+ if (motionPosition >= 0) {
+ // Remember where the motion event started
+ final View v = getChildAt(motionPosition - mFirstPosition);
+ mMotionViewOriginalTop = v.getTop();
+ }
+
+ mMotionX = x;
+ mMotionY = y;
+ mMotionPosition = motionPosition;
+ mLastY = Integer.MIN_VALUE;
+ }
+
+ if (performButtonActionOnTouchDown(ev) && (mTouchMode == TOUCH_MODE_DOWN)) {
+ removeCallbacks(mPendingCheckForTap);
+ }
+ }
+
+ private void onTouchMove(MotionEvent ev) {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == -1) {
+ pointerIndex = 0;
+ mActivePointerId = ev.getPointerId(pointerIndex);
+ }
+
+ if (mDataChanged) {
+ // Re-sync everything if data has been changed
+ // since the scroll operation can query the adapter.
+ layoutChildren();
+ }
+
+ final int y = (int) ev.getY(pointerIndex);
+
+ switch (mTouchMode) {
case TOUCH_MODE_DOWN:
case TOUCH_MODE_TAP:
case TOUCH_MODE_DONE_WAITING:
// Check if we have moved far enough that it looks more like a
- // scroll than a tap
- startScrollIfNeeded(y);
+ // scroll than a tap. If so, we'll enter scrolling mode.
+ if (startScrollIfNeeded(y)) {
+ break;
+ }
+ // Otherwise, check containment within list bounds. If we're
+ // outside bounds, cancel any active presses.
+ final float x = ev.getX(pointerIndex);
+ if (!pointInView(x, y, mTouchSlop)) {
+ setPressed(false);
+ final View motionView = getChildAt(mMotionPosition - mFirstPosition);
+ if (motionView != null) {
+ motionView.setPressed(false);
+ }
+ removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
+ mPendingCheckForTap : mPendingCheckForLongPress);
+ mTouchMode = TOUCH_MODE_DONE_WAITING;
+ updateSelectorState();
+ }
break;
case TOUCH_MODE_SCROLL:
case TOUCH_MODE_OVERSCROLL:
scrollIfNeeded(y);
break;
- }
- break;
}
+ }
- case MotionEvent.ACTION_UP: {
- switch (mTouchMode) {
- case TOUCH_MODE_DOWN:
- case TOUCH_MODE_TAP:
- case TOUCH_MODE_DONE_WAITING:
- final int motionPosition = mMotionPosition;
- final View child = getChildAt(motionPosition - mFirstPosition);
+ private void onTouchUp(MotionEvent ev) {
+ switch (mTouchMode) {
+ case TOUCH_MODE_DOWN:
+ case TOUCH_MODE_TAP:
+ case TOUCH_MODE_DONE_WAITING:
+ final int motionPosition = mMotionPosition;
+ final View child = getChildAt(motionPosition - mFirstPosition);
+ if (child != null) {
+ if (mTouchMode != TOUCH_MODE_DOWN) {
+ child.setPressed(false);
+ }
final float x = ev.getX();
final boolean inList = x > mListPadding.left && x < getWidth() - mListPadding.right;
-
- if (child != null && !child.hasFocusable() && inList) {
- if (mTouchMode != TOUCH_MODE_DOWN) {
- child.setPressed(false);
- }
-
+ if (inList && !child.hasFocusable()) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
@@ -3430,11 +3500,8 @@
mResurrectToPosition = motionPosition;
if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
- final Handler handler = getHandler();
- if (handler != null) {
- handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
- mPendingCheckForTap : mPendingCheckForLongPress);
- }
+ removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
+ mPendingCheckForTap : mPendingCheckForLongPress);
mLayoutMode = LAYOUT_NORMAL;
if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
mTouchMode = TOUCH_MODE_TAP;
@@ -3470,191 +3537,140 @@
mTouchMode = TOUCH_MODE_REST;
updateSelectorState();
}
- return true;
+ return;
} else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
performClick.run();
}
}
- mTouchMode = TOUCH_MODE_REST;
- updateSelectorState();
- break;
- case TOUCH_MODE_SCROLL:
- final int childCount = getChildCount();
- if (childCount > 0) {
- final int firstChildTop = getChildAt(0).getTop();
- final int lastChildBottom = getChildAt(childCount - 1).getBottom();
- final int contentTop = mListPadding.top;
- final int contentBottom = getHeight() - mListPadding.bottom;
- if (mFirstPosition == 0 && firstChildTop >= contentTop &&
- mFirstPosition + childCount < mItemCount &&
- lastChildBottom <= getHeight() - contentBottom) {
- mTouchMode = TOUCH_MODE_REST;
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- } else {
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-
- final int initialVelocity = (int)
- (velocityTracker.getYVelocity(mActivePointerId) * mVelocityScale);
- // Fling if we have enough velocity and we aren't at a boundary.
- // Since we can potentially overfling more than we can overscroll, don't
- // allow the weird behavior where you can scroll to a boundary then
- // fling further.
- if (Math.abs(initialVelocity) > mMinimumVelocity &&
- !((mFirstPosition == 0 &&
- firstChildTop == contentTop - mOverscrollDistance) ||
- (mFirstPosition + childCount == mItemCount &&
- lastChildBottom == contentBottom + mOverscrollDistance))) {
- if (mFlingRunnable == null) {
- mFlingRunnable = new FlingRunnable();
- }
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
-
- mFlingRunnable.start(-initialVelocity);
- } else {
- mTouchMode = TOUCH_MODE_REST;
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- if (mFlingRunnable != null) {
- mFlingRunnable.endFling();
- }
- if (mPositionScroller != null) {
- mPositionScroller.stop();
- }
- }
- }
- } else {
+ }
+ mTouchMode = TOUCH_MODE_REST;
+ updateSelectorState();
+ break;
+ case TOUCH_MODE_SCROLL:
+ final int childCount = getChildCount();
+ if (childCount > 0) {
+ final int firstChildTop = getChildAt(0).getTop();
+ final int lastChildBottom = getChildAt(childCount - 1).getBottom();
+ final int contentTop = mListPadding.top;
+ final int contentBottom = getHeight() - mListPadding.bottom;
+ if (mFirstPosition == 0 && firstChildTop >= contentTop &&
+ mFirstPosition + childCount < mItemCount &&
+ lastChildBottom <= getHeight() - contentBottom) {
mTouchMode = TOUCH_MODE_REST;
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- }
- break;
-
- case TOUCH_MODE_OVERSCROLL:
- if (mFlingRunnable == null) {
- mFlingRunnable = new FlingRunnable();
- }
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
- if (Math.abs(initialVelocity) > mMinimumVelocity) {
- mFlingRunnable.startOverfling(-initialVelocity);
} else {
- mFlingRunnable.startSpringback();
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+
+ final int initialVelocity = (int)
+ (velocityTracker.getYVelocity(mActivePointerId) * mVelocityScale);
+ // Fling if we have enough velocity and we aren't at a boundary.
+ // Since we can potentially overfling more than we can overscroll, don't
+ // allow the weird behavior where you can scroll to a boundary then
+ // fling further.
+ if (Math.abs(initialVelocity) > mMinimumVelocity &&
+ !((mFirstPosition == 0 &&
+ firstChildTop == contentTop - mOverscrollDistance) ||
+ (mFirstPosition + childCount == mItemCount &&
+ lastChildBottom == contentBottom + mOverscrollDistance))) {
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ }
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+
+ mFlingRunnable.start(-initialVelocity);
+ } else {
+ mTouchMode = TOUCH_MODE_REST;
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ if (mFlingRunnable != null) {
+ mFlingRunnable.endFling();
+ }
+ if (mPositionScroller != null) {
+ mPositionScroller.stop();
+ }
+ }
}
-
- break;
- }
-
- setPressed(false);
-
- if (mEdgeGlowTop != null) {
- mEdgeGlowTop.onRelease();
- mEdgeGlowBottom.onRelease();
- }
-
- // Need to redraw since we probably aren't drawing the selector anymore
- invalidate();
-
- final Handler handler = getHandler();
- if (handler != null) {
- handler.removeCallbacks(mPendingCheckForLongPress);
- }
-
- recycleVelocityTracker();
-
- mActivePointerId = INVALID_POINTER;
-
- if (PROFILE_SCROLLING) {
- if (mScrollProfilingStarted) {
- Debug.stopMethodTracing();
- mScrollProfilingStarted = false;
- }
- }
-
- if (mScrollStrictSpan != null) {
- mScrollStrictSpan.finish();
- mScrollStrictSpan = null;
- }
- break;
- }
-
- case MotionEvent.ACTION_CANCEL: {
- switch (mTouchMode) {
- case TOUCH_MODE_OVERSCROLL:
- if (mFlingRunnable == null) {
- mFlingRunnable = new FlingRunnable();
- }
- mFlingRunnable.startSpringback();
- break;
-
- case TOUCH_MODE_OVERFLING:
- // Do nothing - let it play out.
- break;
-
- default:
+ } else {
mTouchMode = TOUCH_MODE_REST;
- setPressed(false);
- View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
- if (motionView != null) {
- motionView.setPressed(false);
- }
- clearScrollingCache();
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ }
+ break;
- final Handler handler = getHandler();
- if (handler != null) {
- handler.removeCallbacks(mPendingCheckForLongPress);
- }
+ case TOUCH_MODE_OVERSCROLL:
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ }
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
- recycleVelocityTracker();
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+ if (Math.abs(initialVelocity) > mMinimumVelocity) {
+ mFlingRunnable.startOverfling(-initialVelocity);
+ } else {
+ mFlingRunnable.startSpringback();
}
- if (mEdgeGlowTop != null) {
- mEdgeGlowTop.onRelease();
- mEdgeGlowBottom.onRelease();
- }
- mActivePointerId = INVALID_POINTER;
break;
}
- case MotionEvent.ACTION_POINTER_UP: {
- onSecondaryPointerUp(ev);
- final int x = mMotionX;
- final int y = mMotionY;
- final int motionPosition = pointToPosition(x, y);
- if (motionPosition >= 0) {
- // Remember where the motion event started
- v = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = v.getTop();
- mMotionPosition = motionPosition;
- }
- mLastY = y;
- break;
+ setPressed(false);
+
+ if (mEdgeGlowTop != null) {
+ mEdgeGlowTop.onRelease();
+ mEdgeGlowBottom.onRelease();
}
- case MotionEvent.ACTION_POINTER_DOWN: {
- // New pointers take over dragging duties
- final int index = ev.getActionIndex();
- final int id = ev.getPointerId(index);
- final int x = (int) ev.getX(index);
- final int y = (int) ev.getY(index);
- mMotionCorrection = 0;
- mActivePointerId = id;
- mMotionX = x;
- mMotionY = y;
- final int motionPosition = pointToPosition(x, y);
- if (motionPosition >= 0) {
- // Remember where the motion event started
- v = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = v.getTop();
- mMotionPosition = motionPosition;
+ // Need to redraw since we probably aren't drawing the selector anymore
+ invalidate();
+ removeCallbacks(mPendingCheckForLongPress);
+ recycleVelocityTracker();
+
+ mActivePointerId = INVALID_POINTER;
+
+ if (PROFILE_SCROLLING) {
+ if (mScrollProfilingStarted) {
+ Debug.stopMethodTracing();
+ mScrollProfilingStarted = false;
}
- mLastY = y;
- break;
- }
}
- return true;
+ if (mScrollStrictSpan != null) {
+ mScrollStrictSpan.finish();
+ mScrollStrictSpan = null;
+ }
+ }
+
+ private void onTouchCancel() {
+ switch (mTouchMode) {
+ case TOUCH_MODE_OVERSCROLL:
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ }
+ mFlingRunnable.startSpringback();
+ break;
+
+ case TOUCH_MODE_OVERFLING:
+ // Do nothing - let it play out.
+ break;
+
+ default:
+ mTouchMode = TOUCH_MODE_REST;
+ setPressed(false);
+ final View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
+ if (motionView != null) {
+ motionView.setPressed(false);
+ }
+ clearScrollingCache();
+ removeCallbacks(mPendingCheckForLongPress);
+ recycleVelocityTracker();
+ }
+
+ if (mEdgeGlowTop != null) {
+ mEdgeGlowTop.onRelease();
+ mEdgeGlowBottom.onRelease();
+ }
+ mActivePointerId = INVALID_POINTER;
}
@Override
@@ -3728,18 +3744,6 @@
canvas.restoreToCount(restoreCount);
}
}
- if (mFastScroller != null) {
- final int scrollY = mScrollY;
- if (scrollY != 0) {
- // Pin to the top/bottom during overscroll
- int restoreCount = canvas.save();
- canvas.translate(0, (float) scrollY);
- mFastScroller.draw(canvas);
- canvas.restoreToCount(restoreCount);
- } else {
- mFastScroller.draw(canvas);
- }
- }
}
/**
@@ -3796,11 +3800,8 @@
return false;
}
- if (mFastScroller != null) {
- boolean intercepted = mFastScroller.onInterceptTouchEvent(ev);
- if (intercepted) {
- return true;
- }
+ if (mFastScroller != null && mFastScroller.onInterceptTouchEvent(ev)) {
+ return true;
}
switch (action & MotionEvent.ACTION_MASK) {
@@ -3945,6 +3946,7 @@
private int mLastFlingY;
private final Runnable mCheckFlywheel = new Runnable() {
+ @Override
public void run() {
final int activeId = mActivePointerId;
final VelocityTracker vt = mVelocityTracker;
@@ -4066,6 +4068,7 @@
postDelayed(mCheckFlywheel, FLYWHEEL_TIMEOUT);
}
+ @Override
public void run() {
switch (mTouchMode) {
default:
@@ -4455,6 +4458,7 @@
removeCallbacks(this);
}
+ @Override
public void run() {
final int listHeight = getHeight();
final int firstPos = mFirstPosition;
@@ -4637,9 +4641,6 @@
/**
* The amount of friction applied to flings. The default value
* is {@link ViewConfiguration#getScrollFriction}.
- *
- * @return A scalar dimensionless value representing the coefficient of
- * friction.
*/
public void setFriction(float friction) {
if (mFlingRunnable == null) {
@@ -4806,6 +4807,7 @@
if (!isHardwareAccelerated()) {
if (mClearScrollingCache == null) {
mClearScrollingCache = new Runnable() {
+ @Override
public void run() {
if (mCachingStarted) {
mCachingStarted = mCachingActive = false;
@@ -5083,7 +5085,7 @@
requestLayout();
invalidate();
}
-
+
/**
* If there is a selection returns false.
* Otherwise resurrects the selection and returns true if resurrected.
@@ -5591,54 +5593,157 @@
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
if (isTextFilterEnabled()) {
- // XXX we need to have the text filter created, so we can get an
- // InputConnection to proxy to. Unfortunately this means we pretty
- // much need to make it as soon as a list view gets focus.
- createTextFilter(false);
if (mPublicInputConnection == null) {
mDefInputConnection = new BaseInputConnection(this, false);
- mPublicInputConnection = new InputConnectionWrapper(
- mTextFilter.onCreateInputConnection(outAttrs), true) {
- @Override
- public boolean reportFullscreenMode(boolean enabled) {
- // Use our own input connection, since it is
- // the "real" one the IME is talking with.
- return mDefInputConnection.reportFullscreenMode(enabled);
- }
-
- @Override
- public boolean performEditorAction(int editorAction) {
- // The editor is off in its own window; we need to be
- // the one that does this.
- if (editorAction == EditorInfo.IME_ACTION_DONE) {
- InputMethodManager imm = (InputMethodManager)
- getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
- return true;
- }
- return false;
- }
-
- @Override
- public boolean sendKeyEvent(KeyEvent event) {
- // Use our own input connection, since the filter
- // text view may not be shown in a window so has
- // no ViewAncestor to dispatch events with.
- return mDefInputConnection.sendKeyEvent(event);
- }
- };
+ mPublicInputConnection = new InputConnectionWrapper(outAttrs);
}
- outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_FILTER;
+ outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_FILTER;
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
return mPublicInputConnection;
}
return null;
}
+ private class InputConnectionWrapper implements InputConnection {
+ private final EditorInfo mOutAttrs;
+ private InputConnection mTarget;
+
+ public InputConnectionWrapper(EditorInfo outAttrs) {
+ mOutAttrs = outAttrs;
+ }
+
+ private InputConnection getTarget() {
+ if (mTarget == null) {
+ mTarget = getTextFilterInput().onCreateInputConnection(mOutAttrs);
+ }
+ return mTarget;
+ }
+
+ @Override
+ public boolean reportFullscreenMode(boolean enabled) {
+ // Use our own input connection, since it is
+ // the "real" one the IME is talking with.
+ return mDefInputConnection.reportFullscreenMode(enabled);
+ }
+
+ @Override
+ public boolean performEditorAction(int editorAction) {
+ // The editor is off in its own window; we need to be
+ // the one that does this.
+ if (editorAction == EditorInfo.IME_ACTION_DONE) {
+ InputMethodManager imm = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean sendKeyEvent(KeyEvent event) {
+ // Use our own input connection, since the filter
+ // text view may not be shown in a window so has
+ // no ViewAncestor to dispatch events with.
+ return mDefInputConnection.sendKeyEvent(event);
+ }
+
+ @Override
+ public CharSequence getTextBeforeCursor(int n, int flags) {
+ if (mTarget == null) return "";
+ return mTarget.getTextBeforeCursor(n, flags);
+ }
+
+ @Override
+ public CharSequence getTextAfterCursor(int n, int flags) {
+ if (mTarget == null) return "";
+ return mTarget.getTextAfterCursor(n, flags);
+ }
+
+ @Override
+ public CharSequence getSelectedText(int flags) {
+ if (mTarget == null) return "";
+ return mTarget.getSelectedText(flags);
+ }
+
+ @Override
+ public int getCursorCapsMode(int reqModes) {
+ if (mTarget == null) return InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
+ return mTarget.getCursorCapsMode(reqModes);
+ }
+
+ @Override
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ return getTarget().getExtractedText(request, flags);
+ }
+
+ @Override
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ return getTarget().deleteSurroundingText(beforeLength, afterLength);
+ }
+
+ @Override
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ return getTarget().setComposingText(text, newCursorPosition);
+ }
+
+ @Override
+ public boolean setComposingRegion(int start, int end) {
+ return getTarget().setComposingRegion(start, end);
+ }
+
+ @Override
+ public boolean finishComposingText() {
+ return mTarget == null || mTarget.finishComposingText();
+ }
+
+ @Override
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ return getTarget().commitText(text, newCursorPosition);
+ }
+
+ @Override
+ public boolean commitCompletion(CompletionInfo text) {
+ return getTarget().commitCompletion(text);
+ }
+
+ @Override
+ public boolean commitCorrection(CorrectionInfo correctionInfo) {
+ return getTarget().commitCorrection(correctionInfo);
+ }
+
+ @Override
+ public boolean setSelection(int start, int end) {
+ return getTarget().setSelection(start, end);
+ }
+
+ @Override
+ public boolean performContextMenuAction(int id) {
+ return getTarget().performContextMenuAction(id);
+ }
+
+ @Override
+ public boolean beginBatchEdit() {
+ return getTarget().beginBatchEdit();
+ }
+
+ @Override
+ public boolean endBatchEdit() {
+ return getTarget().endBatchEdit();
+ }
+
+ @Override
+ public boolean clearMetaKeyStates(int states) {
+ return getTarget().clearMetaKeyStates(states);
+ }
+
+ @Override
+ public boolean performPrivateCommand(String action, Bundle data) {
+ return getTarget().performPrivateCommand(action, data);
+ }
+ }
+
/**
* For filtering we proxy an input connection to an internal text editor,
* and this allows the proxying to happen.
@@ -5655,23 +5760,11 @@
*/
private void createTextFilter(boolean animateEntrance) {
if (mPopup == null) {
- Context c = getContext();
- PopupWindow p = new PopupWindow(c);
- LayoutInflater layoutInflater = (LayoutInflater)
- c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mTextFilter = (EditText) layoutInflater.inflate(
- com.android.internal.R.layout.typing_filter, null);
- // For some reason setting this as the "real" input type changes
- // the text view in some way that it doesn't work, and I don't
- // want to figure out why this is.
- mTextFilter.setRawInputType(EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_FILTER);
- mTextFilter.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
- mTextFilter.addTextChangedListener(this);
+ PopupWindow p = new PopupWindow(getContext());
p.setFocusable(false);
p.setTouchable(false);
p.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
- p.setContentView(mTextFilter);
+ p.setContentView(getTextFilterInput());
p.setWidth(LayoutParams.WRAP_CONTENT);
p.setHeight(LayoutParams.WRAP_CONTENT);
p.setBackgroundDrawable(null);
@@ -5686,12 +5779,28 @@
}
}
+ private EditText getTextFilterInput() {
+ if (mTextFilter == null) {
+ final LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+ mTextFilter = (EditText) layoutInflater.inflate(
+ com.android.internal.R.layout.typing_filter, null);
+ // For some reason setting this as the "real" input type changes
+ // the text view in some way that it doesn't work, and I don't
+ // want to figure out why this is.
+ mTextFilter.setRawInputType(EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_FILTER);
+ mTextFilter.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
+ mTextFilter.addTextChangedListener(this);
+ }
+ return mTextFilter;
+ }
+
/**
* Clear the text filter.
*/
public void clearTextFilter() {
if (mFiltered) {
- mTextFilter.setText("");
+ getTextFilterInput().setText("");
mFiltered = false;
if (mPopup != null && mPopup.isShowing()) {
dismissPopup();
@@ -5706,6 +5815,7 @@
return mFiltered;
}
+ @Override
public void onGlobalLayout() {
if (isShown()) {
// Show the popup if we are filtered
@@ -5725,6 +5835,7 @@
* For our text watcher that is associated with the text filter. Does
* nothing.
*/
+ @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@@ -5733,8 +5844,10 @@
* the actual filtering as the text changes, and takes care of hiding and
* showing the popup displaying the currently entered filter text.
*/
+ @Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- if (mPopup != null && isTextFilterEnabled()) {
+ if (isTextFilterEnabled()) {
+ createTextFilter(true);
int length = s.length();
boolean showing = mPopup.isShowing();
if (!showing && length > 0) {
@@ -5763,9 +5876,11 @@
* For our text watcher that is associated with the text filter. Does
* nothing.
*/
+ @Override
public void afterTextChanged(Editable s) {
}
+ @Override
public void onFilterComplete(int count) {
if (mSelectedPosition < 0 && count > 0) {
mResurrectToPosition = INVALID_POSITION;
@@ -5917,9 +6032,9 @@
/**
* Sets up the onClickHandler to be used by the RemoteViewsAdapter when inflating RemoteViews
- *
+ *
* @param handler The OnClickHandler to use when inflating RemoteViews.
- *
+ *
* @hide
*/
public void setRemoteViewsOnClickHandler(OnClickHandler handler) {
@@ -5934,6 +6049,7 @@
* This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not
* connected yet.
*/
+ @Override
public void deferNotifyDataSetChanged() {
mDeferNotifyDataSetChanged = true;
}
@@ -5941,6 +6057,7 @@
/**
* Called back when the adapter connects to the RemoteViewsService.
*/
+ @Override
public boolean onRemoteAdapterConnected() {
if (mRemoteAdapter != mAdapter) {
setAdapter(mRemoteAdapter);
@@ -5959,6 +6076,7 @@
/**
* Called back when the adapter disconnects from the RemoteViewsService.
*/
+ @Override
public void onRemoteAdapterDisconnected() {
// If the remote adapter disconnects, we keep it around
// since the currently displayed items are still cached.
@@ -6041,6 +6159,7 @@
return mWrapped != null;
}
+ @Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
if (mWrapped.onCreateActionMode(mode, menu)) {
// Initialize checked graphic state?
@@ -6050,14 +6169,17 @@
return false;
}
+ @Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return mWrapped.onPrepareActionMode(mode, menu);
}
+ @Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return mWrapped.onActionItemClicked(mode, item);
}
+ @Override
public void onDestroyActionMode(ActionMode mode) {
mWrapped.onDestroyActionMode(mode);
mChoiceActionMode = null;
@@ -6072,6 +6194,7 @@
setLongClickable(true);
}
+ @Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
mWrapped.onItemCheckedStateChanged(mode, position, id, checked);
@@ -6296,6 +6419,7 @@
}
mFirstActivePosition = firstActivePosition;
+ //noinspection MismatchedReadAndWriteOfArray
final View[] activeViews = mActiveViews;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 502de31..31ab0af 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -31,7 +31,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeProvider;
/**
* An AdapterView is a view whose children are determined by an {@link Adapter}.
@@ -1034,8 +1033,7 @@
checkSelectionChanged();
}
- //TODO: Hmm, we do not know the old state so this is sub-optimal
- notifyAccessibilityStateChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
void checkSelectionChanged() {
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index de8b80d..f1c3139 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -93,7 +93,7 @@
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
- notifyAccessibilityStateChanged();
+ notifyViewAccessibilityStateChangedIfNeeded();
}
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 452ad1b..05a8dc8 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -114,7 +114,7 @@
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
- notifyAccessibilityStateChanged();
+ notifyViewAccessibilityStateChangedIfNeeded();
// Avoid infinite recursions if setChecked() is called from a listener
if (mBroadcasting) {
@@ -199,10 +199,8 @@
unscheduleDrawable(mButtonDrawable);
}
d.setCallback(this);
- d.setState(getDrawableState());
d.setVisible(getVisibility() == VISIBLE, false);
mButtonDrawable = d;
- mButtonDrawable.setState(null);
setMinHeight(mButtonDrawable.getIntrinsicHeight());
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index f57f333..50d28ab 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,6 +16,13 @@
package android.widget;
+import android.content.UndoManager;
+import android.content.UndoOperation;
+import android.content.UndoOwner;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.InputFilter;
+import android.text.SpannableString;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.EditableInputConnection;
@@ -107,11 +114,16 @@
*/
public class Editor {
private static final String TAG = "Editor";
+ static final boolean DEBUG_UNDO = false;
static final int BLINK = 500;
private static final float[] TEMP_POSITION = new float[2];
private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
+ UndoManager mUndoManager;
+ UndoOwner mUndoOwner;
+ InputFilter mUndoInputFilter;
+
// Cursor Controllers.
InsertionPointCursorController mInsertionPointCursorController;
SelectionModifierCursorController mSelectionModifierCursorController;
@@ -181,7 +193,10 @@
// Set when this TextView gained focus with some text selected. Will start selection mode.
boolean mCreatedWithASelection;
- private EasyEditSpanController mEasyEditSpanController;
+ // The span controller helps monitoring the changes to which the Editor needs to react:
+ // - EasyEditSpans, for which we have some UI to display on attach and on hide
+ // - SelectionSpans, for which we need to call updateSelection if an IME is attached
+ private SpanController mSpanController;
WordIterator mWordIterator;
SpellChecker mSpellChecker;
@@ -466,8 +481,8 @@
}
private void hideSpanControllers() {
- if (mEasyEditSpanController != null) {
- mEasyEditSpanController.hide();
+ if (mSpanController != null) {
+ mSpanController.hide();
}
}
@@ -484,6 +499,10 @@
* Create new SpellCheckSpans on the modified region.
*/
private void updateSpellCheckSpans(int start, int end, boolean createSpellChecker) {
+ // Remove spans whose adjacent characters are text not punctuation
+ mTextView.removeAdjacentSuggestionSpans(start);
+ mTextView.removeAdjacentSuggestionSpans(end);
+
if (mTextView.isTextEditable() && mTextView.isSuggestionsEnabled() &&
!(mTextView instanceof ExtractEditText)) {
if (mSpellChecker == null && createSpellChecker) {
@@ -1082,9 +1101,12 @@
mTextView.updateAfterEdit();
reportExtractedText();
} else if (ims.mCursorChanged) {
- // Cheezy way to get us to report the current cursor location.
+ // Cheesy way to get us to report the current cursor location.
mTextView.invalidateCursor();
}
+ // sendUpdateSelection knows to avoid sending if the selection did
+ // not actually change.
+ sendUpdateSelection();
}
static final int EXTRACT_NOTHING = -2;
@@ -1205,6 +1227,27 @@
return false;
}
+ private void sendUpdateSelection() {
+ if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0) {
+ final InputMethodManager imm = InputMethodManager.peekInstance();
+ if (null != imm) {
+ final int selectionStart = mTextView.getSelectionStart();
+ final int selectionEnd = mTextView.getSelectionEnd();
+ int candStart = -1;
+ int candEnd = -1;
+ if (mTextView.getText() instanceof Spannable) {
+ final Spannable sp = (Spannable) mTextView.getText();
+ candStart = EditableInputConnection.getComposingSpanStart(sp);
+ candEnd = EditableInputConnection.getComposingSpanEnd(sp);
+ }
+ // InputMethodManager#updateSelection skips sending the message if
+ // none of the parameters have changed since the last time we called it.
+ imm.updateSelection(mTextView,
+ selectionStart, selectionEnd, candStart, candEnd);
+ }
+ }
+ }
+
void onDraw(Canvas canvas, Layout layout, Path highlight, Paint highlightPaint,
int cursorOffsetVertical) {
final int selectionStart = mTextView.getSelectionStart();
@@ -1222,17 +1265,6 @@
// input method.
reported = reportExtractedText();
}
- if (!reported && highlight != null) {
- int candStart = -1;
- int candEnd = -1;
- if (mTextView.getText() instanceof Spannable) {
- Spannable sp = (Spannable) mTextView.getText();
- candStart = EditableInputConnection.getComposingSpanStart(sp);
- candEnd = EditableInputConnection.getComposingSpanEnd(sp);
- }
- imm.updateSelection(mTextView,
- selectionStart, selectionEnd, candStart, candEnd);
- }
}
if (imm.isWatchingCursor(mTextView) && highlight != null) {
@@ -1859,17 +1891,18 @@
text.setSpan(mKeyListener, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
- if (mEasyEditSpanController == null) {
- mEasyEditSpanController = new EasyEditSpanController();
+ if (mSpanController == null) {
+ mSpanController = new SpanController();
}
- text.setSpan(mEasyEditSpanController, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ text.setSpan(mSpanController, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
/**
* Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
* pop-up should be displayed.
+ * Also monitors {@link Selection} to call back to the attached input method.
*/
- class EasyEditSpanController implements SpanWatcher {
+ class SpanController implements SpanWatcher {
private static final int DISPLAY_TIMEOUT_MS = 3000; // 3 secs
@@ -1877,9 +1910,18 @@
private Runnable mHidePopup;
+ // This function is pure but inner classes can't have static functions
+ private boolean isNonIntermediateSelectionSpan(final Spannable text,
+ final Object span) {
+ return (Selection.SELECTION_START == span || Selection.SELECTION_END == span)
+ && (text.getSpanFlags(span) & Spanned.SPAN_INTERMEDIATE) == 0;
+ }
+
@Override
public void onSpanAdded(Spannable text, Object span, int start, int end) {
- if (span instanceof EasyEditSpan) {
+ if (isNonIntermediateSelectionSpan(text, span)) {
+ sendUpdateSelection();
+ } else if (span instanceof EasyEditSpan) {
if (mPopupWindow == null) {
mPopupWindow = new EasyEditPopupWindow();
mHidePopup = new Runnable() {
@@ -1903,7 +1945,7 @@
int start = editable.getSpanStart(span);
int end = editable.getSpanEnd(span);
if (start >= 0 && end >= 0) {
- sendNotification(EasyEditSpan.TEXT_DELETED, span);
+ sendEasySpanNotification(EasyEditSpan.TEXT_DELETED, span);
mTextView.deleteText_internal(start, end);
}
editable.removeSpan(span);
@@ -1934,7 +1976,9 @@
@Override
public void onSpanRemoved(Spannable text, Object span, int start, int end) {
- if (mPopupWindow != null && span == mPopupWindow.mEasyEditSpan) {
+ if (isNonIntermediateSelectionSpan(text, span)) {
+ sendUpdateSelection();
+ } else if (mPopupWindow != null && span == mPopupWindow.mEasyEditSpan) {
hide();
}
}
@@ -1942,9 +1986,11 @@
@Override
public void onSpanChanged(Spannable text, Object span, int previousStart, int previousEnd,
int newStart, int newEnd) {
- if (mPopupWindow != null && span instanceof EasyEditSpan) {
+ if (isNonIntermediateSelectionSpan(text, span)) {
+ sendUpdateSelection();
+ } else if (mPopupWindow != null && span instanceof EasyEditSpan) {
EasyEditSpan easyEditSpan = (EasyEditSpan) span;
- sendNotification(EasyEditSpan.TEXT_MODIFIED, easyEditSpan);
+ sendEasySpanNotification(EasyEditSpan.TEXT_MODIFIED, easyEditSpan);
text.removeSpan(easyEditSpan);
}
}
@@ -1956,7 +2002,7 @@
}
}
- private void sendNotification(int textChangedType, EasyEditSpan span) {
+ private void sendEasySpanNotification(int textChangedType, EasyEditSpan span) {
try {
PendingIntent pendingIntent = span.getPendingIntent();
if (pendingIntent != null) {
@@ -1984,7 +2030,7 @@
/**
* Displays the actions associated to an {@link EasyEditSpan}. The pop-up is controlled
- * by {@link EasyEditSpanController}.
+ * by {@link SpanController}.
*/
private class EasyEditPopupWindow extends PinnedPopupWindow
implements OnClickListener {
@@ -3929,4 +3975,166 @@
mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
}
}
+
+ public static class UndoInputFilter implements InputFilter {
+ final Editor mEditor;
+
+ public UndoInputFilter(Editor editor) {
+ mEditor = editor;
+ }
+
+ @Override
+ public CharSequence filter(CharSequence source, int start, int end,
+ Spanned dest, int dstart, int dend) {
+ if (DEBUG_UNDO) {
+ Log.d(TAG, "filter: source=" + source + " (" + start + "-" + end + ")");
+ Log.d(TAG, "filter: dest=" + dest + " (" + dstart + "-" + dend + ")");
+ }
+ final UndoManager um = mEditor.mUndoManager;
+ if (um.isInUndo()) {
+ if (DEBUG_UNDO) Log.d(TAG, "*** skipping, currently performing undo/redo");
+ return null;
+ }
+
+ um.beginUpdate("Edit text");
+ TextModifyOperation op = um.getLastOperation(
+ TextModifyOperation.class, mEditor.mUndoOwner, UndoManager.MERGE_MODE_UNIQUE);
+ if (op != null) {
+ if (DEBUG_UNDO) Log.d(TAG, "Last op: range=(" + op.mRangeStart + "-" + op.mRangeEnd
+ + "), oldText=" + op.mOldText);
+ // See if we can continue modifying this operation.
+ if (op.mOldText == null) {
+ // The current operation is an add... are we adding more? We are adding
+ // more if we are either appending new text to the end of the last edit or
+ // completely replacing some or all of the last edit.
+ if (start < end && ((dstart >= op.mRangeStart && dend <= op.mRangeEnd)
+ || (dstart == op.mRangeEnd && dend == op.mRangeEnd))) {
+ op.mRangeEnd = dstart + (end-start);
+ um.endUpdate();
+ if (DEBUG_UNDO) Log.d(TAG, "*** merging with last op, mRangeEnd="
+ + op.mRangeEnd);
+ return null;
+ }
+ } else {
+ // The current operation is a delete... can we delete more?
+ if (start == end && dend == op.mRangeStart-1) {
+ SpannableStringBuilder str;
+ if (op.mOldText instanceof SpannableString) {
+ str = (SpannableStringBuilder)op.mOldText;
+ } else {
+ str = new SpannableStringBuilder(op.mOldText);
+ }
+ str.insert(0, dest, dstart, dend);
+ op.mRangeStart = dstart;
+ op.mOldText = str;
+ um.endUpdate();
+ if (DEBUG_UNDO) Log.d(TAG, "*** merging with last op, range=("
+ + op.mRangeStart + "-" + op.mRangeEnd
+ + "), oldText=" + op.mOldText);
+ return null;
+ }
+ }
+
+ // Couldn't add to the current undo operation, need to start a new
+ // undo state for a new undo operation.
+ um.commitState(null);
+ um.setUndoLabel("Edit text");
+ }
+
+ // Create a new undo state reflecting the operation being performed.
+ op = new TextModifyOperation(mEditor.mUndoOwner);
+ op.mRangeStart = dstart;
+ if (start < end) {
+ op.mRangeEnd = dstart + (end-start);
+ } else {
+ op.mRangeEnd = dstart;
+ }
+ if (dstart < dend) {
+ op.mOldText = dest.subSequence(dstart, dend);
+ }
+ if (DEBUG_UNDO) Log.d(TAG, "*** adding new op, range=(" + op.mRangeStart
+ + "-" + op.mRangeEnd + "), oldText=" + op.mOldText);
+ um.addOperation(op, UndoManager.MERGE_MODE_NONE);
+ um.endUpdate();
+ return null;
+ }
+ }
+
+ public static class TextModifyOperation extends UndoOperation<TextView> {
+ int mRangeStart, mRangeEnd;
+ CharSequence mOldText;
+
+ public TextModifyOperation(UndoOwner owner) {
+ super(owner);
+ }
+
+ public TextModifyOperation(Parcel src, ClassLoader loader) {
+ super(src, loader);
+ mRangeStart = src.readInt();
+ mRangeEnd = src.readInt();
+ mOldText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(src);
+ }
+
+ @Override
+ public void commit() {
+ }
+
+ @Override
+ public void undo() {
+ swapText();
+ }
+
+ @Override
+ public void redo() {
+ swapText();
+ }
+
+ private void swapText() {
+ // Both undo and redo involves swapping the contents of the range
+ // in the text view with our local text.
+ TextView tv = getOwnerData();
+ Editable editable = (Editable)tv.getText();
+ CharSequence curText;
+ if (mRangeStart >= mRangeEnd) {
+ curText = null;
+ } else {
+ curText = editable.subSequence(mRangeStart, mRangeEnd);
+ }
+ if (DEBUG_UNDO) {
+ Log.d(TAG, "Swap: range=(" + mRangeStart + "-" + mRangeEnd
+ + "), oldText=" + mOldText);
+ Log.d(TAG, "Swap: curText=" + curText);
+ }
+ if (mOldText == null) {
+ editable.delete(mRangeStart, mRangeEnd);
+ mRangeEnd = mRangeStart;
+ } else {
+ editable.replace(mRangeStart, mRangeEnd, mOldText);
+ mRangeEnd = mRangeStart + mOldText.length();
+ }
+ mOldText = curText;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mRangeStart);
+ dest.writeInt(mRangeEnd);
+ TextUtils.writeToParcel(mOldText, dest, flags);
+ }
+
+ public static final Parcelable.ClassLoaderCreator<TextModifyOperation> CREATOR
+ = new Parcelable.ClassLoaderCreator<TextModifyOperation>() {
+ public TextModifyOperation createFromParcel(Parcel in) {
+ return new TextModifyOperation(in, null);
+ }
+
+ public TextModifyOperation createFromParcel(Parcel in, ClassLoader loader) {
+ return new TextModifyOperation(in, loader);
+ }
+
+ public TextModifyOperation[] newArray(int size) {
+ return new TextModifyOperation[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 10b8cbe..594b130 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -16,47 +16,66 @@
package android.widget;
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.NinePatchDrawable;
-import android.os.Handler;
-import android.os.SystemClock;
+import android.os.Build;
+import android.text.TextUtils.TruncateAt;
+import android.util.IntProperty;
+import android.util.MathUtils;
+import android.util.Property;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.MeasureSpec;
import android.view.ViewConfiguration;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroupOverlay;
import android.widget.AbsListView.OnScrollListener;
+import com.android.internal.R;
+
/**
* Helper class for AbsListView to draw and control the Fast Scroll thumb
*/
class FastScroller {
- private static final String TAG = "FastScroller";
-
- // Minimum number of pages to justify showing a fast scroll thumb
- private static int MIN_PAGES = 4;
- // Scroll thumb not showing
+ /** Duration of fade-out animation. */
+ private static final int DURATION_FADE_OUT = 300;
+
+ /** Duration of fade-in animation. */
+ private static final int DURATION_FADE_IN = 150;
+
+ /** Duration of transition cross-fade animation. */
+ private static final int DURATION_CROSS_FADE = 50;
+
+ /** Duration of transition resize animation. */
+ private static final int DURATION_RESIZE = 100;
+
+ /** Inactivity timeout before fading controls. */
+ private static final long FADE_TIMEOUT = 1500;
+
+ /** Minimum number of pages to justify showing a fast scroll thumb. */
+ private static final int MIN_PAGES = 4;
+
+ /** Scroll thumb and preview not showing. */
private static final int STATE_NONE = 0;
- // Not implemented yet - fade-in transition
- private static final int STATE_ENTER = 1;
- // Scroll thumb visible and moving along with the scrollbar
- private static final int STATE_VISIBLE = 2;
- // Scroll thumb being dragged by user
- private static final int STATE_DRAGGING = 3;
- // Scroll thumb fading out due to inactivity timeout
- private static final int STATE_EXIT = 4;
- private static final int[] PRESSED_STATES = new int[] {
- android.R.attr.state_pressed
- };
+ /** Scroll thumb visible and moving along with the scrollbar. */
+ private static final int STATE_VISIBLE = 1;
- private static final int[] DEFAULT_STATES = new int[0];
+ /** Scroll thumb and preview being dragged by user. */
+ private static final int STATE_DRAGGING = 2;
+ /** Styleable attributes. */
private static final int[] ATTRS = new int[] {
android.R.attr.fastScrollTextColor,
android.R.attr.fastScrollThumbDrawable,
@@ -66,6 +85,7 @@
android.R.attr.fastScrollOverlayPosition
};
+ // Styleable attribute indices.
private static final int TEXT_COLOR = 0;
private static final int THUMB_DRAWABLE = 1;
private static final int TRACK_DRAWABLE = 2;
@@ -73,111 +93,252 @@
private static final int PREVIEW_BACKGROUND_RIGHT = 4;
private static final int OVERLAY_POSITION = 5;
+ // Positions for preview image and text.
private static final int OVERLAY_FLOATING = 0;
private static final int OVERLAY_AT_THUMB = 1;
-
- private Drawable mThumbDrawable;
- private Drawable mOverlayDrawable;
- private Drawable mTrackDrawable;
- private Drawable mOverlayDrawableLeft;
- private Drawable mOverlayDrawableRight;
+ // Indices for mPreviewResId.
+ private static final int PREVIEW_LEFT = 0;
+ private static final int PREVIEW_RIGHT = 1;
- int mThumbH;
- int mThumbW;
- int mThumbY;
+ /** Delay before considering a tap in the thumb area to be a drag. */
+ private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
- private RectF mOverlayPos;
- private int mOverlaySize;
+ private final Rect mTempBounds = new Rect();
+ private final Rect mTempMargins = new Rect();
- AbsListView mList;
- boolean mScrollCompleted;
- private int mVisibleItem;
- private Paint mPaint;
- private int mListOffset;
+ private final AbsListView mList;
+ private final ViewGroupOverlay mOverlay;
+ private final TextView mPrimaryText;
+ private final TextView mSecondaryText;
+ private final ImageView mThumbImage;
+ private final ImageView mTrackImage;
+ private final ImageView mPreviewImage;
+
+ /**
+ * Preview image resource IDs for left- and right-aligned layouts. See
+ * {@link #PREVIEW_LEFT} and {@link #PREVIEW_RIGHT}.
+ */
+ private final int[] mPreviewResId = new int[2];
+
+ /**
+ * Padding in pixels around the preview text. Applied as layout margins to
+ * the preview text and padding to the preview image.
+ */
+ private final int mPreviewPadding;
+
+ /** Whether there is a track image to display. */
+ private final boolean mHasTrackImage;
+
+ /** Set containing decoration transition animations. */
+ private AnimatorSet mDecorAnimation;
+
+ /** Set containing preview text transition animations. */
+ private AnimatorSet mPreviewAnimation;
+
+ /** Whether the primary text is showing. */
+ private boolean mShowingPrimary;
+
+ /** Whether we're waiting for completion of scrollTo(). */
+ private boolean mScrollCompleted;
+
+ /** The position of the first visible item in the list. */
+ private int mFirstVisibleItem;
+
+ /** The number of headers at the top of the view. */
+ private int mHeaderCount;
+
+ /** The number of items in the list. */
private int mItemCount = -1;
+
+ /** The index of the current section. */
+ private int mCurrentSection = -1;
+
+ /** Whether the list is long enough to need a fast scroller. */
private boolean mLongList;
-
- private Object [] mSections;
- private String mSectionText;
- private boolean mDrawOverlay;
- private ScrollFade mScrollFade;
-
+
+ private Object[] mSections;
+
+ /**
+ * Current decoration state, one of:
+ * <ul>
+ * <li>{@link #STATE_NONE}, nothing visible
+ * <li>{@link #STATE_VISIBLE}, showing track and thumb
+ * <li>{@link #STATE_DRAGGING}, visible and showing preview
+ * </ul>
+ */
private int mState;
-
- private Handler mHandler = new Handler();
-
- BaseAdapter mListAdapter;
+
+ private BaseAdapter mListAdapter;
private SectionIndexer mSectionIndexer;
- private boolean mChangedBounds;
-
- private int mPosition;
+ /** Whether decorations should be laid out from right to left. */
+ private boolean mLayoutFromRight;
+ /** Whether the scrollbar and decorations should always be shown. */
private boolean mAlwaysShow;
+ /**
+ * Position for the preview image and text. One of:
+ * <ul>
+ * <li>{@link #OVERLAY_AT_THUMB}
+ * <li>{@link #OVERLAY_FLOATING}
+ * </ul>
+ */
private int mOverlayPosition;
+ /** Whether to precisely match the thumb position to the list. */
private boolean mMatchDragPosition;
- float mInitialTouchY;
- boolean mPendingDrag;
+ private float mInitialTouchY;
+ private boolean mHasPendingDrag;
private int mScaledTouchSlop;
- private static final int FADE_TIMEOUT = 1500;
- private static final int PENDING_DRAG_DELAY = 180;
-
- private final Rect mTmpRect = new Rect();
-
private final Runnable mDeferStartDrag = new Runnable() {
+ @Override
public void run() {
if (mList.mIsAttached) {
beginDrag();
- final int viewHeight = mList.getHeight();
- // Jitter
- int newThumbY = (int) mInitialTouchY - mThumbH + 10;
- if (newThumbY < 0) {
- newThumbY = 0;
- } else if (newThumbY + mThumbH > viewHeight) {
- newThumbY = viewHeight - mThumbH;
- }
- mThumbY = newThumbY;
- scrollTo((float) mThumbY / (viewHeight - mThumbH));
+ final float pos = getPosFromMotionEvent(mInitialTouchY);
+ scrollTo(pos);
}
- mPendingDrag = false;
+ mHasPendingDrag = false;
+ }
+ };
+
+ /**
+ * Used to delay hiding fast scroll decorations.
+ */
+ private final Runnable mDeferHide = new Runnable() {
+ @Override
+ public void run() {
+ setState(STATE_NONE);
+ }
+ };
+
+ /**
+ * Used to effect a transition from primary to secondary text.
+ */
+ private final AnimatorListener mSwitchPrimaryListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mShowingPrimary = !mShowingPrimary;
}
};
public FastScroller(Context context, AbsListView listView) {
mList = listView;
- init(context);
+ mOverlay = listView.getOverlay();
+
+ mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+
+ final Resources res = context.getResources();
+ final TypedArray ta = context.getTheme().obtainStyledAttributes(ATTRS);
+
+ mTrackImage = new ImageView(context);
+
+ // Add track to overlay if it has an image.
+ final int trackResId = ta.getResourceId(TRACK_DRAWABLE, 0);
+ if (trackResId != 0) {
+ mHasTrackImage = true;
+ mTrackImage.setBackgroundResource(trackResId);
+ mOverlay.add(mTrackImage);
+ } else {
+ mHasTrackImage = false;
+ }
+
+ mThumbImage = new ImageView(context);
+
+ // Add thumb to overlay if it has an image.
+ final Drawable thumbDrawable = ta.getDrawable(THUMB_DRAWABLE);
+ if (thumbDrawable != null) {
+ mThumbImage.setImageDrawable(thumbDrawable);
+ mOverlay.add(mThumbImage);
+ }
+
+ // If necessary, apply minimum thumb width and height.
+ if (thumbDrawable.getIntrinsicWidth() <= 0 || thumbDrawable.getIntrinsicHeight() <= 0) {
+ mThumbImage.setMinimumWidth(res.getDimensionPixelSize(R.dimen.fastscroll_thumb_width));
+ mThumbImage.setMinimumHeight(
+ res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height));
+ }
+
+ final int previewSize = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_size);
+ mPreviewImage = new ImageView(context);
+ mPreviewImage.setMinimumWidth(previewSize);
+ mPreviewImage.setMinimumHeight(previewSize);
+ mPreviewImage.setAlpha(0f);
+ mOverlay.add(mPreviewImage);
+
+ mPreviewPadding = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_padding);
+
+ final int textMinSize = Math.max(0, previewSize - mPreviewPadding);
+ mPrimaryText = createPreviewTextView(context, ta);
+ mPrimaryText.setMinimumWidth(textMinSize);
+ mPrimaryText.setMinimumHeight(textMinSize);
+ mOverlay.add(mPrimaryText);
+ mSecondaryText = createPreviewTextView(context, ta);
+ mSecondaryText.setMinimumWidth(textMinSize);
+ mSecondaryText.setMinimumHeight(textMinSize);
+ mOverlay.add(mSecondaryText);
+
+ mPreviewResId[PREVIEW_LEFT] = ta.getResourceId(PREVIEW_BACKGROUND_LEFT, 0);
+ mPreviewResId[PREVIEW_RIGHT] = ta.getResourceId(PREVIEW_BACKGROUND_RIGHT, 0);
+ mOverlayPosition = ta.getInt(OVERLAY_POSITION, OVERLAY_FLOATING);
+ ta.recycle();
+
+ mScrollCompleted = true;
+ mState = STATE_VISIBLE;
+ mMatchDragPosition =
+ context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
+
+ getSectionsFromIndexer();
+ refreshDrawablePressedState();
+ setScrollbarPosition(mList.getVerticalScrollbarPosition());
+
+ mList.postDelayed(mDeferHide, FADE_TIMEOUT);
}
+ /**
+ * @param alwaysShow Whether the fast scroll thumb should always be shown
+ */
public void setAlwaysShow(boolean alwaysShow) {
mAlwaysShow = alwaysShow;
+
if (alwaysShow) {
- mHandler.removeCallbacks(mScrollFade);
setState(STATE_VISIBLE);
} else if (mState == STATE_VISIBLE) {
- mHandler.postDelayed(mScrollFade, FADE_TIMEOUT);
+ mList.postDelayed(mDeferHide, FADE_TIMEOUT);
}
}
+ /**
+ * @return Whether the fast scroll thumb will always be shown
+ * @see #setAlwaysShow(boolean)
+ */
public boolean isAlwaysShowEnabled() {
return mAlwaysShow;
}
- private void refreshDrawableState() {
- int[] state = mState == STATE_DRAGGING ? PRESSED_STATES : DEFAULT_STATES;
+ /**
+ * Immediately transitions the fast scroller decorations to a hidden state.
+ */
+ public void stop() {
+ setState(STATE_NONE);
+ }
- if (mThumbDrawable != null && mThumbDrawable.isStateful()) {
- mThumbDrawable.setState(state);
+ /**
+ * @return Whether the fast scroll thumb should be shown.
+ */
+ public boolean shouldShow() {
+ // Don't show if the list is as tall as or shorter than the thumbnail.
+ if (mList.getHeight() <= mThumbImage.getHeight()) {
+ return false;
}
- if (mTrackDrawable != null && mTrackDrawable.isStateful()) {
- mTrackDrawable.setState(state);
- }
+
+ return true;
}
public void setScrollbarPosition(int position) {
@@ -185,362 +346,403 @@
position = mList.isLayoutRtl() ?
View.SCROLLBAR_POSITION_LEFT : View.SCROLLBAR_POSITION_RIGHT;
}
- mPosition = position;
- switch (position) {
- default:
- case View.SCROLLBAR_POSITION_RIGHT:
- mOverlayDrawable = mOverlayDrawableRight;
- break;
- case View.SCROLLBAR_POSITION_LEFT:
- mOverlayDrawable = mOverlayDrawableLeft;
- break;
+
+ mLayoutFromRight = position != View.SCROLLBAR_POSITION_LEFT;
+
+ final int previewResId = mPreviewResId[mLayoutFromRight ? PREVIEW_RIGHT : PREVIEW_LEFT];
+ mPreviewImage.setBackgroundResource(previewResId);
+
+ // Add extra padding for text.
+ final Drawable background = mPreviewImage.getBackground();
+ if (background != null) {
+ final Rect padding = mTempBounds;
+ background.getPadding(padding);
+ padding.offset(mPreviewPadding, mPreviewPadding);
+ mPreviewImage.setPadding(padding.left, padding.top, padding.right, padding.bottom);
}
+
+ updateLayout();
}
public int getWidth() {
- return mThumbW;
+ return mThumbImage.getWidth();
}
- public void setState(int state) {
- switch (state) {
- case STATE_NONE:
- mHandler.removeCallbacks(mScrollFade);
- mList.invalidate();
- break;
- case STATE_VISIBLE:
- if (mState != STATE_VISIBLE) { // Optimization
- resetThumbPos();
- }
- // Fall through
- case STATE_DRAGGING:
- mHandler.removeCallbacks(mScrollFade);
- break;
- case STATE_EXIT:
- final int viewWidth = mList.getWidth();
- final int top = mThumbY;
- final int bottom = mThumbY + mThumbH;
- final int left;
- final int right;
- switch (mList.getLayoutDirection()) {
- case View.LAYOUT_DIRECTION_RTL:
- left = 0;
- right = mThumbW;
- break;
- case View.LAYOUT_DIRECTION_LTR:
- default:
- left = viewWidth - mThumbW;
- right = viewWidth;
- }
- mList.invalidate(left, top, right, bottom);
- break;
+ public void onSizeChanged(int w, int h, int oldw, int oldh) {
+ updateLayout();
+ }
+
+ public void onItemCountChanged(int oldTotalItemCount, int totalItemCount) {
+ final int visibleItemCount = mList.getChildCount();
+ final boolean hasMoreItems = totalItemCount - visibleItemCount > 0;
+ if (hasMoreItems && mState != STATE_DRAGGING) {
+ final int firstVisibleItem = mList.getFirstVisiblePosition();
+ setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount));
}
- mState = state;
- refreshDrawableState();
}
-
- public int getState() {
- return mState;
+
+ /**
+ * Creates a view into which preview text can be placed.
+ */
+ private TextView createPreviewTextView(Context context, TypedArray ta) {
+ final LayoutParams params = new LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ final Resources res = context.getResources();
+ final int minSize = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_size);
+ final ColorStateList textColor = ta.getColorStateList(TEXT_COLOR);
+ final float textSize = res.getDimension(R.dimen.fastscroll_overlay_text_size);
+ final TextView textView = new TextView(context);
+ textView.setLayoutParams(params);
+ textView.setTextColor(textColor);
+ textView.setTextSize(textSize);
+ textView.setSingleLine(true);
+ textView.setEllipsize(TruncateAt.MIDDLE);
+ textView.setGravity(Gravity.CENTER);
+ textView.setAlpha(0f);
+
+ // Manually propagate inherited layout direction.
+ textView.setLayoutDirection(mList.getLayoutDirection());
+
+ return textView;
}
-
- private void resetThumbPos() {
- final int viewWidth = mList.getWidth();
- // Bounds are always top right. Y coordinate get's translated during draw
- switch (mPosition) {
- case View.SCROLLBAR_POSITION_RIGHT:
- mThumbDrawable.setBounds(viewWidth - mThumbW, 0, viewWidth, mThumbH);
- break;
- case View.SCROLLBAR_POSITION_LEFT:
- mThumbDrawable.setBounds(0, 0, mThumbW, mThumbH);
- break;
+
+ /**
+ * Measures and layouts the scrollbar and decorations.
+ */
+ private void updateLayout() {
+ layoutThumb();
+ layoutTrack();
+
+ final Rect bounds = mTempBounds;
+ measurePreview(mPrimaryText, bounds);
+ applyLayout(mPrimaryText, bounds);
+ measurePreview(mSecondaryText, bounds);
+ applyLayout(mSecondaryText, bounds);
+
+ if (mPreviewImage != null) {
+ // Apply preview image padding.
+ bounds.left -= mPreviewImage.getPaddingLeft();
+ bounds.top -= mPreviewImage.getPaddingTop();
+ bounds.right += mPreviewImage.getPaddingRight();
+ bounds.bottom += mPreviewImage.getPaddingBottom();
+ applyLayout(mPreviewImage, bounds);
}
- mThumbDrawable.setAlpha(ScrollFade.ALPHA_MAX);
}
-
- private void useThumbDrawable(Context context, Drawable drawable) {
- mThumbDrawable = drawable;
- if (drawable instanceof NinePatchDrawable) {
- mThumbW = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.fastscroll_thumb_width);
- mThumbH = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.fastscroll_thumb_height);
+
+ /**
+ * Layouts a view within the specified bounds and pins the pivot point to
+ * the appropriate edge.
+ *
+ * @param view The view to layout.
+ * @param bounds Bounds at which to layout the view.
+ */
+ private void applyLayout(View view, Rect bounds) {
+ view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ view.setPivotX(mLayoutFromRight ? bounds.right - bounds.left : 0);
+ }
+
+ /**
+ * Measures the preview text bounds, taking preview image padding into
+ * account. This method should only be called after {@link #layoutThumb()}
+ * and {@link #layoutTrack()} have both been called at least once.
+ *
+ * @param v The preview text view to measure.
+ * @param out Rectangle into which measured bounds are placed.
+ */
+ private void measurePreview(View v, Rect out) {
+ // Apply the preview image's padding as layout margins.
+ final Rect margins = mTempMargins;
+ margins.left = mPreviewImage.getPaddingLeft();
+ margins.top = mPreviewImage.getPaddingTop();
+ margins.right = mPreviewImage.getPaddingRight();
+ margins.bottom = mPreviewImage.getPaddingBottom();
+
+ if (mOverlayPosition == OVERLAY_AT_THUMB) {
+ measureViewToSide(v, mThumbImage, margins, out);
} else {
- mThumbW = drawable.getIntrinsicWidth();
- mThumbH = drawable.getIntrinsicHeight();
+ measureFloating(v, margins, out);
}
- mChangedBounds = true;
}
- private void init(Context context) {
- // Get both the scrollbar states drawables
- TypedArray ta = context.getTheme().obtainStyledAttributes(ATTRS);
- useThumbDrawable(context, ta.getDrawable(THUMB_DRAWABLE));
- mTrackDrawable = ta.getDrawable(TRACK_DRAWABLE);
-
- mOverlayDrawableLeft = ta.getDrawable(PREVIEW_BACKGROUND_LEFT);
- mOverlayDrawableRight = ta.getDrawable(PREVIEW_BACKGROUND_RIGHT);
- mOverlayPosition = ta.getInt(OVERLAY_POSITION, OVERLAY_FLOATING);
-
- mScrollCompleted = true;
-
- getSectionsFromIndexer();
-
- mOverlaySize = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.fastscroll_overlay_size);
- mOverlayPos = new RectF();
- mScrollFade = new ScrollFade();
- mPaint = new Paint();
- mPaint.setAntiAlias(true);
- mPaint.setTextAlign(Paint.Align.CENTER);
- mPaint.setTextSize(mOverlaySize / 2);
-
- ColorStateList textColor = ta.getColorStateList(TEXT_COLOR);
- int textColorNormal = textColor.getDefaultColor();
- mPaint.setColor(textColorNormal);
- mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
-
- // to show mOverlayDrawable properly
- if (mList.getWidth() > 0 && mList.getHeight() > 0) {
- onSizeChanged(mList.getWidth(), mList.getHeight(), 0, 0);
+ /**
+ * Measures the bounds for a view that should be laid out against the edge
+ * of an adjacent view. If no adjacent view is provided, lays out against
+ * the list edge.
+ *
+ * @param view The view to measure for layout.
+ * @param adjacent (Optional) The adjacent view, may be null to align to the
+ * list edge.
+ * @param margins Layout margins to apply to the view.
+ * @param out Rectangle into which measured bounds are placed.
+ */
+ private void measureViewToSide(View view, View adjacent, Rect margins, Rect out) {
+ final int marginLeft;
+ final int marginTop;
+ final int marginRight;
+ if (margins == null) {
+ marginLeft = 0;
+ marginTop = 0;
+ marginRight = 0;
+ } else {
+ marginLeft = margins.left;
+ marginTop = margins.top;
+ marginRight = margins.right;
}
-
- mState = STATE_NONE;
- refreshDrawableState();
- ta.recycle();
+ final int listWidth = mList.getWidth();
+ final int maxWidth;
+ if (adjacent == null) {
+ maxWidth = listWidth;
+ } else if (mLayoutFromRight) {
+ maxWidth = adjacent.getLeft();
+ } else {
+ maxWidth = listWidth - adjacent.getRight();
+ }
- mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ final int adjMaxWidth = maxWidth - marginLeft - marginRight;
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ view.measure(widthMeasureSpec, heightMeasureSpec);
- mMatchDragPosition = context.getApplicationInfo().targetSdkVersion >=
- android.os.Build.VERSION_CODES.HONEYCOMB;
+ // Align to the left or right.
+ final int width = view.getMeasuredWidth();
+ final int left;
+ final int right;
+ if (mLayoutFromRight) {
+ right = (adjacent == null ? listWidth : adjacent.getLeft()) - marginRight;
+ left = right - width;
+ } else {
+ left = (adjacent == null ? 0 : adjacent.getRight()) + marginLeft;
+ right = left + width;
+ }
- setScrollbarPosition(mList.getVerticalScrollbarPosition());
+ // Don't adjust the vertical position.
+ final int top = marginTop;
+ final int bottom = top + view.getMeasuredHeight();
+ out.set(left, top, right, bottom);
}
-
- void stop() {
- setState(STATE_NONE);
+
+ private void measureFloating(View preview, Rect margins, Rect out) {
+ final int marginLeft;
+ final int marginTop;
+ final int marginRight;
+ if (margins == null) {
+ marginLeft = 0;
+ marginTop = 0;
+ marginRight = 0;
+ } else {
+ marginLeft = margins.left;
+ marginTop = margins.top;
+ marginRight = margins.right;
+ }
+
+ final View list = mList;
+ final int listWidth = list.getWidth();
+ final int adjMaxWidth = listWidth - marginLeft - marginRight;
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ preview.measure(widthMeasureSpec, heightMeasureSpec);
+
+ // Align at the vertical center, 10% from the top.
+ final int width = preview.getMeasuredWidth();
+ final int top = list.getHeight() / 10 + marginTop;
+ final int bottom = top + preview.getMeasuredHeight();
+ final int left = (listWidth - width) / 2;
+ final int right = left + width;
+ out.set(left, top, right, bottom);
}
-
- boolean isVisible() {
- return !(mState == STATE_NONE);
+
+ /**
+ * Lays out the thumb according to the current scrollbar position.
+ */
+ private void layoutThumb() {
+ final Rect bounds = mTempBounds;
+ measureViewToSide(mThumbImage, null, null, bounds);
+ applyLayout(mThumbImage, bounds);
}
-
- public void draw(Canvas canvas) {
-
- if (mState == STATE_NONE) {
- // No need to draw anything
+
+ /**
+ * Lays out the track centered on the thumb, if available, or against the
+ * edge if no thumb is available. Must be called after {@link #layoutThumb}.
+ */
+ private void layoutTrack() {
+ final View track = mTrackImage;
+ final View thumb = mThumbImage;
+ final View list = mList;
+ final int listWidth = list.getWidth();
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(listWidth, MeasureSpec.AT_MOST);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ track.measure(widthMeasureSpec, heightMeasureSpec);
+
+ final int trackWidth = track.getMeasuredWidth();
+ final int thumbHalfHeight = thumb == null ? 0 : thumb.getHeight() / 2;
+ final int left = thumb == null ? listWidth - trackWidth :
+ thumb.getLeft() + (thumb.getWidth() - trackWidth) / 2;
+ final int right = left + trackWidth;
+ final int top = thumbHalfHeight;
+ final int bottom = list.getHeight() - thumbHalfHeight;
+ track.layout(left, top, right, bottom);
+ }
+
+ private void setState(int state) {
+ mList.removeCallbacks(mDeferHide);
+
+ if (mAlwaysShow && state == STATE_NONE) {
+ state = STATE_VISIBLE;
+ }
+
+ if (state == mState) {
return;
}
- final int y = mThumbY;
- final int viewWidth = mList.getWidth();
- final FastScroller.ScrollFade scrollFade = mScrollFade;
-
- int alpha = -1;
- if (mState == STATE_EXIT) {
- alpha = scrollFade.getAlpha();
- if (alpha < ScrollFade.ALPHA_MAX / 2) {
- mThumbDrawable.setAlpha(alpha * 2);
- }
- int left = 0;
- switch (mPosition) {
- case View.SCROLLBAR_POSITION_RIGHT:
- left = viewWidth - (mThumbW * alpha) / ScrollFade.ALPHA_MAX;
- break;
- case View.SCROLLBAR_POSITION_LEFT:
- left = -mThumbW + (mThumbW * alpha) / ScrollFade.ALPHA_MAX;
- break;
- }
- mThumbDrawable.setBounds(left, 0, left + mThumbW, mThumbH);
- mChangedBounds = true;
- }
-
- if (mTrackDrawable != null) {
- final Rect thumbBounds = mThumbDrawable.getBounds();
- final int left = thumbBounds.left;
- final int halfThumbHeight = (thumbBounds.bottom - thumbBounds.top) / 2;
- final int trackWidth = mTrackDrawable.getIntrinsicWidth();
- final int trackLeft = (left + mThumbW / 2) - trackWidth / 2;
- mTrackDrawable.setBounds(trackLeft, halfThumbHeight,
- trackLeft + trackWidth, mList.getHeight() - halfThumbHeight);
- mTrackDrawable.draw(canvas);
- }
-
- canvas.translate(0, y);
- mThumbDrawable.draw(canvas);
- canvas.translate(0, -y);
-
- // If user is dragging the scroll bar, draw the alphabet overlay
- if (mState == STATE_DRAGGING && mDrawOverlay) {
- if (mOverlayPosition == OVERLAY_AT_THUMB) {
- int left = 0;
- switch (mPosition) {
- default:
- case View.SCROLLBAR_POSITION_RIGHT:
- left = Math.max(0,
- mThumbDrawable.getBounds().left - mThumbW - mOverlaySize);
- break;
- case View.SCROLLBAR_POSITION_LEFT:
- left = Math.min(mThumbDrawable.getBounds().right + mThumbW,
- mList.getWidth() - mOverlaySize);
- break;
- }
-
- int top = Math.max(0,
- Math.min(y + (mThumbH - mOverlaySize) / 2, mList.getHeight() - mOverlaySize));
-
- final RectF pos = mOverlayPos;
- pos.left = left;
- pos.right = pos.left + mOverlaySize;
- pos.top = top;
- pos.bottom = pos.top + mOverlaySize;
- if (mOverlayDrawable != null) {
- mOverlayDrawable.setBounds((int) pos.left, (int) pos.top,
- (int) pos.right, (int) pos.bottom);
- }
- }
- mOverlayDrawable.draw(canvas);
- final Paint paint = mPaint;
- float descent = paint.descent();
- final RectF rectF = mOverlayPos;
- final Rect tmpRect = mTmpRect;
- mOverlayDrawable.getPadding(tmpRect);
- final int hOff = (tmpRect.right - tmpRect.left) / 2;
- final int vOff = (tmpRect.bottom - tmpRect.top) / 2;
- canvas.drawText(mSectionText, (int) (rectF.left + rectF.right) / 2 - hOff,
- (int) (rectF.bottom + rectF.top) / 2 + mOverlaySize / 4 - descent - vOff,
- paint);
- } else if (mState == STATE_EXIT) {
- if (alpha == 0) { // Done with exit
- setState(STATE_NONE);
- } else {
- final int left, right, top, bottom;
- if (mTrackDrawable != null) {
- top = 0;
- bottom = mList.getHeight();
+ switch (state) {
+ case STATE_NONE:
+ transitionToHidden();
+ break;
+ case STATE_VISIBLE:
+ transitionToVisible();
+ break;
+ case STATE_DRAGGING:
+ if (transitionPreviewLayout(mCurrentSection)) {
+ transitionToDragging();
} else {
- top = y;
- bottom = y + mThumbH;
+ transitionToVisible();
}
- switch (mList.getLayoutDirection()) {
- case View.LAYOUT_DIRECTION_RTL:
- left = 0;
- right = mThumbW;
- break;
- case View.LAYOUT_DIRECTION_LTR:
- default:
- left = viewWidth - mThumbW;
- right = viewWidth;
- }
- mList.invalidate(left, top, right, bottom);
- }
+ break;
}
+
+ mState = state;
+
+ refreshDrawablePressedState();
}
- void onSizeChanged(int w, int h, int oldw, int oldh) {
- if (mThumbDrawable != null) {
- switch (mPosition) {
- default:
- case View.SCROLLBAR_POSITION_RIGHT:
- mThumbDrawable.setBounds(w - mThumbW, 0, w, mThumbH);
- break;
- case View.SCROLLBAR_POSITION_LEFT:
- mThumbDrawable.setBounds(0, 0, mThumbW, mThumbH);
- break;
- }
- }
- if (mOverlayPosition == OVERLAY_FLOATING) {
- final RectF pos = mOverlayPos;
- pos.left = (w - mOverlaySize) / 2;
- pos.right = pos.left + mOverlaySize;
- pos.top = h / 10; // 10% from top
- pos.bottom = pos.top + mOverlaySize;
- if (mOverlayDrawable != null) {
- mOverlayDrawable.setBounds((int) pos.left, (int) pos.top,
- (int) pos.right, (int) pos.bottom);
- }
- }
+ private void refreshDrawablePressedState() {
+ final boolean isPressed = mState == STATE_DRAGGING;
+ mThumbImage.setPressed(isPressed);
+ mTrackImage.setPressed(isPressed);
}
- void onItemCountChanged(int oldCount, int newCount) {
- if (mAlwaysShow) {
- mLongList = true;
+ /**
+ * Shows nothing.
+ */
+ private void transitionToHidden() {
+ if (mDecorAnimation != null) {
+ mDecorAnimation.cancel();
}
+
+ final Animator fadeOut = groupAnimatorOfFloat(View.ALPHA, 0f, mThumbImage, mTrackImage,
+ mPreviewImage, mPrimaryText, mSecondaryText).setDuration(DURATION_FADE_OUT);
+
+ // Push the thumb and track outside the list bounds.
+ final float offset = mLayoutFromRight ? mThumbImage.getWidth() : -mThumbImage.getWidth();
+ final Animator slideOut = groupAnimatorOfFloat(
+ View.TRANSLATION_X, offset, mThumbImage, mTrackImage)
+ .setDuration(DURATION_FADE_OUT);
+
+ mDecorAnimation = new AnimatorSet();
+ mDecorAnimation.playTogether(fadeOut, slideOut);
+ mDecorAnimation.start();
}
- void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- // Are there enough pages to require fast scroll? Recompute only if total count changes
+ /**
+ * Shows the thumb and track.
+ */
+ private void transitionToVisible() {
+ if (mDecorAnimation != null) {
+ mDecorAnimation.cancel();
+ }
+
+ final Animator fadeIn = groupAnimatorOfFloat(View.ALPHA, 1f, mThumbImage, mTrackImage)
+ .setDuration(DURATION_FADE_IN);
+ final Animator fadeOut = groupAnimatorOfFloat(
+ View.ALPHA, 0f, mPreviewImage, mPrimaryText, mSecondaryText)
+ .setDuration(DURATION_FADE_OUT);
+ final Animator slideIn = groupAnimatorOfFloat(
+ View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
+
+ mDecorAnimation = new AnimatorSet();
+ mDecorAnimation.playTogether(fadeIn, fadeOut, slideIn);
+ mDecorAnimation.start();
+ }
+
+ /**
+ * Shows the thumb, preview, and track.
+ */
+ private void transitionToDragging() {
+ if (mDecorAnimation != null) {
+ mDecorAnimation.cancel();
+ }
+
+ final Animator fadeIn = groupAnimatorOfFloat(
+ View.ALPHA, 1f, mThumbImage, mTrackImage, mPreviewImage)
+ .setDuration(DURATION_FADE_IN);
+ final Animator slideIn = groupAnimatorOfFloat(
+ View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
+
+ mDecorAnimation = new AnimatorSet();
+ mDecorAnimation.playTogether(fadeIn, slideIn);
+ mDecorAnimation.start();
+ }
+
+ private boolean isLongList(int visibleItemCount, int totalItemCount) {
+ // Are there enough pages to require fast scroll? Recompute only if
+ // total count changes.
if (mItemCount != totalItemCount && visibleItemCount > 0) {
mItemCount = totalItemCount;
mLongList = mItemCount / visibleItemCount >= MIN_PAGES;
}
- if (mAlwaysShow) {
- mLongList = true;
- }
- if (!mLongList) {
- if (mState != STATE_NONE) {
- setState(STATE_NONE);
- }
+
+ return mLongList;
+ }
+
+ public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ if (!mAlwaysShow && !isLongList(visibleItemCount, totalItemCount)) {
+ setState(STATE_NONE);
return;
}
- if (totalItemCount - visibleItemCount > 0 && mState != STATE_DRAGGING) {
- mThumbY = getThumbPositionForListPosition(firstVisibleItem, visibleItemCount,
- totalItemCount);
- if (mChangedBounds) {
- resetThumbPos();
- mChangedBounds = false;
- }
+
+ final boolean hasMoreItems = totalItemCount - visibleItemCount > 0;
+ if (hasMoreItems && mState != STATE_DRAGGING) {
+ setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount));
}
+
mScrollCompleted = true;
- if (firstVisibleItem == mVisibleItem) {
- return;
- }
- mVisibleItem = firstVisibleItem;
- if (mState != STATE_DRAGGING) {
- setState(STATE_VISIBLE);
- if (!mAlwaysShow) {
- mHandler.postDelayed(mScrollFade, FADE_TIMEOUT);
+
+ if (mFirstVisibleItem != firstVisibleItem) {
+ mFirstVisibleItem = firstVisibleItem;
+
+ // Show the thumb, if necessary, and set up auto-fade.
+ if (mState != STATE_DRAGGING) {
+ setState(STATE_VISIBLE);
+ mList.postDelayed(mDeferHide, FADE_TIMEOUT);
}
}
}
- SectionIndexer getSectionIndexer() {
- return mSectionIndexer;
- }
-
- Object[] getSections() {
- if (mListAdapter == null && mList != null) {
- getSectionsFromIndexer();
- }
- return mSections;
- }
-
- void getSectionsFromIndexer() {
- Adapter adapter = mList.getAdapter();
+ private void getSectionsFromIndexer() {
mSectionIndexer = null;
+
+ Adapter adapter = mList.getAdapter();
if (adapter instanceof HeaderViewListAdapter) {
- mListOffset = ((HeaderViewListAdapter)adapter).getHeadersCount();
- adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
+ mHeaderCount = ((HeaderViewListAdapter) adapter).getHeadersCount();
+ adapter = ((HeaderViewListAdapter) adapter).getWrappedAdapter();
}
+
if (adapter instanceof ExpandableListConnector) {
- ExpandableListAdapter expAdapter = ((ExpandableListConnector)adapter).getAdapter();
+ final ExpandableListAdapter expAdapter = ((ExpandableListConnector) adapter)
+ .getAdapter();
if (expAdapter instanceof SectionIndexer) {
mSectionIndexer = (SectionIndexer) expAdapter;
mListAdapter = (BaseAdapter) adapter;
mSections = mSectionIndexer.getSections();
}
+ } else if (adapter instanceof SectionIndexer) {
+ mListAdapter = (BaseAdapter) adapter;
+ mSectionIndexer = (SectionIndexer) adapter;
+ mSections = mSectionIndexer.getSections();
} else {
- if (adapter instanceof SectionIndexer) {
- mListAdapter = (BaseAdapter) adapter;
- mSectionIndexer = (SectionIndexer) adapter;
- mSections = mSectionIndexer.getSections();
- if (mSections == null) {
- mSections = new String[] { " " };
- }
- } else {
- mListAdapter = (BaseAdapter) adapter;
- mSections = new String[] { " " };
- }
+ mListAdapter = (BaseAdapter) adapter;
+ mSections = null;
}
}
@@ -548,21 +750,24 @@
mListAdapter = null;
}
- void scrollTo(float position) {
- int count = mList.getCount();
+ /**
+ * Scrolls to a specific position within the section
+ * @param position
+ */
+ private void scrollTo(float position) {
mScrollCompleted = false;
- float fThreshold = (1.0f / count) / 8;
+
+ final int count = mList.getCount();
final Object[] sections = mSections;
+ final int sectionCount = sections == null ? 0 : sections.length;
int sectionIndex;
- if (sections != null && sections.length > 1) {
- final int nSections = sections.length;
- int section = (int) (position * nSections);
- if (section >= nSections) {
- section = nSections - 1;
- }
- int exactSection = section;
- sectionIndex = section;
- int index = mSectionIndexer.getPositionForSection(section);
+ if (sections != null && sectionCount > 1) {
+ final int exactSection = MathUtils.constrain(
+ (int) (position * sectionCount), 0, sectionCount - 1);
+ int targetSection = exactSection;
+ int targetIndex = mSectionIndexer.getPositionForSection(targetSection);
+ sectionIndex = targetSection;
+
// Given the expected section and index, the following code will
// try to account for missing sections (no names starting with..)
// It will compute the scroll space of surrounding empty sections
@@ -570,25 +775,26 @@
// available space, so that there is always some list movement while
// the user moves the thumb.
int nextIndex = count;
- int prevIndex = index;
- int prevSection = section;
- int nextSection = section + 1;
+ int prevIndex = targetIndex;
+ int prevSection = targetSection;
+ int nextSection = targetSection + 1;
+
// Assume the next section is unique
- if (section < nSections - 1) {
- nextIndex = mSectionIndexer.getPositionForSection(section + 1);
+ if (targetSection < sectionCount - 1) {
+ nextIndex = mSectionIndexer.getPositionForSection(targetSection + 1);
}
-
+
// Find the previous index if we're slicing the previous section
- if (nextIndex == index) {
+ if (nextIndex == targetIndex) {
// Non-existent letter
- while (section > 0) {
- section--;
- prevIndex = mSectionIndexer.getPositionForSection(section);
- if (prevIndex != index) {
- prevSection = section;
- sectionIndex = section;
+ while (targetSection > 0) {
+ targetSection--;
+ prevIndex = mSectionIndexer.getPositionForSection(targetSection);
+ if (prevIndex != targetIndex) {
+ prevSection = targetSection;
+ sectionIndex = targetSection;
break;
- } else if (section == 0) {
+ } else if (targetSection == 0) {
// When section reaches 0 here, sectionIndex must follow it.
// Assuming mSectionIndexer.getPositionForSection(0) == 0.
sectionIndex = 0;
@@ -596,136 +802,301 @@
}
}
}
+
// Find the next index, in case the assumed next index is not
- // unique. For instance, if there is no P, then request for P's
+ // unique. For instance, if there is no P, then request for P's
// position actually returns Q's. So we need to look ahead to make
- // sure that there is really a Q at Q's position. If not, move
+ // sure that there is really a Q at Q's position. If not, move
// further down...
int nextNextSection = nextSection + 1;
- while (nextNextSection < nSections &&
+ while (nextNextSection < sectionCount &&
mSectionIndexer.getPositionForSection(nextNextSection) == nextIndex) {
nextNextSection++;
nextSection++;
}
+
// Compute the beginning and ending scroll range percentage of the
- // currently visible letter. This could be equal to or greater than
- // (1 / nSections).
- float fPrev = (float) prevSection / nSections;
- float fNext = (float) nextSection / nSections;
- if (prevSection == exactSection && position - fPrev < fThreshold) {
- index = prevIndex;
+ // currently visible section. This could be equal to or greater than
+ // (1 / nSections). If the target position is near the previous
+ // position, snap to the previous position.
+ final float prevPosition = (float) prevSection / sectionCount;
+ final float nextPosition = (float) nextSection / sectionCount;
+ final float snapThreshold = (count == 0) ? Float.MAX_VALUE : .125f / count;
+ if (prevSection == exactSection && position - prevPosition < snapThreshold) {
+ targetIndex = prevIndex;
} else {
- index = prevIndex + (int) ((nextIndex - prevIndex) * (position - fPrev)
- / (fNext - fPrev));
+ targetIndex = prevIndex + (int) ((nextIndex - prevIndex) * (position - prevPosition)
+ / (nextPosition - prevPosition));
}
- // Don't overflow
- if (index > count - 1) index = count - 1;
-
+
+ // Clamp to valid positions.
+ targetIndex = MathUtils.constrain(targetIndex, 0, count - 1);
+
if (mList instanceof ExpandableListView) {
- ExpandableListView expList = (ExpandableListView) mList;
+ final ExpandableListView expList = (ExpandableListView) mList;
expList.setSelectionFromTop(expList.getFlatListPosition(
- ExpandableListView.getPackedPositionForGroup(index + mListOffset)), 0);
+ ExpandableListView.getPackedPositionForGroup(targetIndex + mHeaderCount)),
+ 0);
} else if (mList instanceof ListView) {
- ((ListView)mList).setSelectionFromTop(index + mListOffset, 0);
+ ((ListView) mList).setSelectionFromTop(targetIndex + mHeaderCount, 0);
} else {
- mList.setSelection(index + mListOffset);
+ mList.setSelection(targetIndex + mHeaderCount);
}
} else {
- int index = (int) (position * count);
- // Don't overflow
- if (index > count - 1) index = count - 1;
+ final int index = MathUtils.constrain((int) (position * count), 0, count - 1);
if (mList instanceof ExpandableListView) {
ExpandableListView expList = (ExpandableListView) mList;
expList.setSelectionFromTop(expList.getFlatListPosition(
- ExpandableListView.getPackedPositionForGroup(index + mListOffset)), 0);
+ ExpandableListView.getPackedPositionForGroup(index + mHeaderCount)), 0);
} else if (mList instanceof ListView) {
- ((ListView)mList).setSelectionFromTop(index + mListOffset, 0);
+ ((ListView)mList).setSelectionFromTop(index + mHeaderCount, 0);
} else {
- mList.setSelection(index + mListOffset);
+ mList.setSelection(index + mHeaderCount);
}
+
sectionIndex = -1;
}
- if (sectionIndex >= 0) {
- String text = mSectionText = sections[sectionIndex].toString();
- mDrawOverlay = (text.length() != 1 || text.charAt(0) != ' ') &&
- sectionIndex < sections.length;
- } else {
- mDrawOverlay = false;
+ if (mCurrentSection != sectionIndex) {
+ mCurrentSection = sectionIndex;
+
+ if (transitionPreviewLayout(sectionIndex)) {
+ transitionToDragging();
+ } else {
+ transitionToVisible();
+ }
}
}
- private int getThumbPositionForListPosition(int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
+ /**
+ * Transitions the preview text to a new section. Handles animation,
+ * measurement, and layout. If the new preview text is empty, returns false.
+ *
+ * @param sectionIndex The section index to which the preview should
+ * transition.
+ * @return False if the new preview text is empty.
+ */
+ private boolean transitionPreviewLayout(int sectionIndex) {
+ final Object[] sections = mSections;
+ String text = null;
+ if (sections != null && sectionIndex >= 0 && sectionIndex < sections.length) {
+ final Object section = sections[sectionIndex];
+ if (section != null) {
+ text = section.toString();
+ }
+ }
+
+ final Rect bounds = mTempBounds;
+ final ImageView preview = mPreviewImage;
+ final TextView showing;
+ final TextView target;
+ if (mShowingPrimary) {
+ showing = mPrimaryText;
+ target = mSecondaryText;
+ } else {
+ showing = mSecondaryText;
+ target = mPrimaryText;
+ }
+
+ // Set and layout target immediately.
+ target.setText(text);
+ measurePreview(target, bounds);
+ applyLayout(target, bounds);
+
+ if (mPreviewAnimation != null) {
+ mPreviewAnimation.cancel();
+ }
+
+ // Cross-fade preview text.
+ final Animator showTarget = animateAlpha(target, 1f).setDuration(DURATION_CROSS_FADE);
+ final Animator hideShowing = animateAlpha(showing, 0f).setDuration(DURATION_CROSS_FADE);
+ hideShowing.addListener(mSwitchPrimaryListener);
+
+ // Apply preview image padding and animate bounds, if necessary.
+ bounds.left -= mPreviewImage.getPaddingLeft();
+ bounds.top -= mPreviewImage.getPaddingTop();
+ bounds.right += mPreviewImage.getPaddingRight();
+ bounds.bottom += mPreviewImage.getPaddingBottom();
+ final Animator resizePreview = animateBounds(preview, bounds);
+ resizePreview.setDuration(DURATION_RESIZE);
+
+ mPreviewAnimation = new AnimatorSet();
+ final AnimatorSet.Builder builder = mPreviewAnimation.play(hideShowing).with(showTarget);
+ builder.with(resizePreview);
+
+ // The current preview size is unaffected by hidden or showing. It's
+ // used to set starting scales for things that need to be scaled down.
+ final int previewWidth = preview.getWidth() - preview.getPaddingLeft()
+ - preview.getPaddingRight();
+
+ // If target is too large, shrink it immediately to fit and expand to
+ // target size. Otherwise, start at target size.
+ final int targetWidth = target.getWidth();
+ if (targetWidth > previewWidth) {
+ target.setScaleX((float) previewWidth / targetWidth);
+ final Animator scaleAnim = animateScaleX(target, 1f).setDuration(DURATION_RESIZE);
+ builder.with(scaleAnim);
+ } else {
+ target.setScaleX(1f);
+ }
+
+ // If showing is larger than target, shrink to target size.
+ final int showingWidth = showing.getWidth();
+ if (showingWidth > targetWidth) {
+ final float scale = (float) targetWidth / showingWidth;
+ final Animator scaleAnim = animateScaleX(showing, scale).setDuration(DURATION_RESIZE);
+ builder.with(scaleAnim);
+ }
+
+ mPreviewAnimation.start();
+
+ return (text != null && text.length() > 0);
+ }
+
+ /**
+ * Positions the thumb and preview widgets.
+ *
+ * @param position The position, between 0 and 1, along the track at which
+ * to place the thumb.
+ */
+ private void setThumbPos(float position) {
+ final int top = 0;
+ final int bottom = mList.getHeight();
+
+ final float thumbHalfHeight = mThumbImage.getHeight() / 2f;
+ final float min = top + thumbHalfHeight;
+ final float max = bottom - thumbHalfHeight;
+ final float offset = min;
+ final float range = max - min;
+ final float thumbMiddle = position * range + offset;
+ mThumbImage.setTranslationY(thumbMiddle - thumbHalfHeight);
+
+ // Center the preview on the thumb, constrained to the list bounds.
+ final float previewHalfHeight = mPreviewImage.getHeight() / 2f;
+ final float minP = top + previewHalfHeight;
+ final float maxP = bottom - previewHalfHeight;
+ final float previewMiddle = MathUtils.constrain(thumbMiddle, minP, maxP);
+ final float previewTop = previewMiddle - previewHalfHeight;
+
+ mPreviewImage.setTranslationY(previewTop);
+ mPrimaryText.setTranslationY(previewTop);
+ mSecondaryText.setTranslationY(previewTop);
+ }
+
+ private float getPosFromMotionEvent(float y) {
+ final int top = 0;
+ final int bottom = mList.getHeight();
+
+ final float thumbHalfHeight = mThumbImage.getHeight() / 2f;
+ final float min = top + thumbHalfHeight;
+ final float max = bottom - thumbHalfHeight;
+ final float offset = min;
+ final float range = max - min;
+
+ // If the list is the same height as the thumbnail or shorter,
+ // effectively disable scrolling.
+ if (range <= 0) {
+ return 0f;
+ }
+
+ return MathUtils.constrain((y - offset) / range, 0f, 1f);
+ }
+
+ private float getPosFromItemCount(
+ int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mSectionIndexer == null || mListAdapter == null) {
getSectionsFromIndexer();
}
- if (mSectionIndexer == null || !mMatchDragPosition) {
- return ((mList.getHeight() - mThumbH) * firstVisibleItem)
- / (totalItemCount - visibleItemCount);
+
+ final boolean hasSections = mSectionIndexer != null && mSections != null
+ && mSections.length > 0;
+ if (!hasSections || !mMatchDragPosition) {
+ return (float) firstVisibleItem / (totalItemCount - visibleItemCount);
}
- firstVisibleItem -= mListOffset;
+ // Ignore headers.
+ firstVisibleItem -= mHeaderCount;
if (firstVisibleItem < 0) {
return 0;
}
- totalItemCount -= mListOffset;
+ totalItemCount -= mHeaderCount;
- final int trackHeight = mList.getHeight() - mThumbH;
+ // Hidden portion of the first visible row.
+ final View child = mList.getChildAt(0);
+ final float incrementalPos;
+ if (child == null || child.getHeight() == 0) {
+ incrementalPos = 0;
+ } else {
+ incrementalPos = (float) (mList.getPaddingTop() - child.getTop()) / child.getHeight();
+ }
+ // Number of rows in this section.
final int section = mSectionIndexer.getSectionForPosition(firstVisibleItem);
final int sectionPos = mSectionIndexer.getPositionForSection(section);
- final int nextSectionPos;
final int sectionCount = mSections.length;
- if (section + 1 < sectionCount) {
- nextSectionPos = mSectionIndexer.getPositionForSection(section + 1);
+ final int positionsInSection;
+ if (section < sectionCount - 1) {
+ final int nextSectionPos;
+ if (section + 1 < sectionCount) {
+ nextSectionPos = mSectionIndexer.getPositionForSection(section + 1);
+ } else {
+ nextSectionPos = totalItemCount - 1;
+ }
+ positionsInSection = nextSectionPos - sectionPos;
} else {
- nextSectionPos = totalItemCount - 1;
- }
- final int positionsInSection = nextSectionPos - sectionPos;
-
- final View child = mList.getChildAt(0);
- final float incrementalPos = child == null ? 0 : firstVisibleItem +
- (float) (mList.getPaddingTop() - child.getTop()) / child.getHeight();
- final float posWithinSection = (incrementalPos - sectionPos) / positionsInSection;
- int result = (int) ((section + posWithinSection) / sectionCount * trackHeight);
-
- // Fake out the scrollbar for the last item. Since the section indexer won't
- // ever actually move the list in this end space, make scrolling across the last item
- // account for whatever space is remaining.
- if (firstVisibleItem > 0 && firstVisibleItem + visibleItemCount == totalItemCount) {
- final View lastChild = mList.getChildAt(visibleItemCount - 1);
- final float lastItemVisible = (float) (mList.getHeight() - mList.getPaddingBottom()
- - lastChild.getTop()) / lastChild.getHeight();
- result += (trackHeight - result) * lastItemVisible;
+ positionsInSection = totalItemCount - sectionPos;
}
- return result;
+ // Position within this section.
+ final float posWithinSection;
+ if (positionsInSection == 0) {
+ posWithinSection = 0;
+ } else {
+ posWithinSection = (firstVisibleItem + incrementalPos - sectionPos)
+ / positionsInSection;
+ }
+
+ return (section + posWithinSection) / sectionCount;
}
+ /**
+ * Cancels an ongoing fling event by injecting a
+ * {@link MotionEvent#ACTION_CANCEL} into the host view.
+ */
private void cancelFling() {
- // Cancel the list fling
- MotionEvent cancelFling = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
+ final MotionEvent cancelFling = MotionEvent.obtain(
+ 0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
mList.onTouchEvent(cancelFling);
cancelFling.recycle();
}
-
- void cancelPendingDrag() {
+
+ /**
+ * Cancels a pending drag.
+ *
+ * @see #startPendingDrag()
+ */
+ private void cancelPendingDrag() {
mList.removeCallbacks(mDeferStartDrag);
- mPendingDrag = false;
+ mHasPendingDrag = false;
}
- void startPendingDrag() {
- mPendingDrag = true;
- mList.postDelayed(mDeferStartDrag, PENDING_DRAG_DELAY);
+ /**
+ * Delays dragging until after the framework has determined that the user is
+ * scrolling, rather than tapping.
+ */
+ private void startPendingDrag() {
+ mHasPendingDrag = true;
+ mList.postDelayed(mDeferStartDrag, TAP_TIMEOUT);
}
- void beginDrag() {
+ private void beginDrag() {
setState(STATE_DRAGGING);
+
if (mListAdapter == null && mList != null) {
getSectionsFromIndexer();
}
+
if (mList != null) {
mList.requestDisallowInterceptTouchEvent(true);
mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
@@ -734,16 +1105,23 @@
cancelFling();
}
- boolean onInterceptTouchEvent(MotionEvent ev) {
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- if (mState > STATE_NONE && isPointInside(ev.getX(), ev.getY())) {
- if (!mList.isInScrollingContainer()) {
- beginDrag();
- return true;
+ if (isPointInside(ev.getX(), ev.getY())) {
+ // If the parent has requested that its children delay
+ // pressed state (e.g. is a scrolling container) then we
+ // need to allow the parent time to decide whether it wants
+ // to intercept events. If it does, we will receive a CANCEL
+ // event.
+ if (mList.isInScrollingContainer()) {
+ mInitialTouchY = ev.getY();
+ startPendingDrag();
+ return false;
}
- mInitialTouchY = ev.getY();
- startPendingDrag();
+
+ beginDrag();
+ return true;
}
break;
case MotionEvent.ACTION_UP:
@@ -751,70 +1129,56 @@
cancelPendingDrag();
break;
}
+
return false;
}
- boolean onTouchEvent(MotionEvent me) {
- if (mState == STATE_NONE) {
- return false;
- }
-
- final int action = me.getAction();
-
- if (action == MotionEvent.ACTION_DOWN) {
- if (isPointInside(me.getX(), me.getY())) {
- if (!mList.isInScrollingContainer()) {
+ public boolean onTouchEvent(MotionEvent me) {
+ switch (me.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ if (isPointInside(me.getX(), me.getY())) {
beginDrag();
return true;
}
- mInitialTouchY = me.getY();
- startPendingDrag();
- }
- } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here
- if (mPendingDrag) {
- // Allow a tap to scroll.
- beginDrag();
+ } break;
- final int viewHeight = mList.getHeight();
- // Jitter
- int newThumbY = (int) me.getY() - mThumbH + 10;
- if (newThumbY < 0) {
- newThumbY = 0;
- } else if (newThumbY + mThumbH > viewHeight) {
- newThumbY = viewHeight - mThumbH;
- }
- mThumbY = newThumbY;
- scrollTo((float) mThumbY / (viewHeight - mThumbH));
+ case MotionEvent.ACTION_UP: {
+ if (mHasPendingDrag) {
+ // Allow a tap to scroll.
+ beginDrag();
- cancelPendingDrag();
- // Will hit the STATE_DRAGGING check below
- }
- if (mState == STATE_DRAGGING) {
- if (mList != null) {
- // ViewGroup does the right thing already, but there might
- // be other classes that don't properly reset on touch-up,
- // so do this explicitly just in case.
- mList.requestDisallowInterceptTouchEvent(false);
- mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- }
- setState(STATE_VISIBLE);
- final Handler handler = mHandler;
- handler.removeCallbacks(mScrollFade);
- if (!mAlwaysShow) {
- handler.postDelayed(mScrollFade, 1000);
+ final float pos = getPosFromMotionEvent(me.getY());
+ setThumbPos(pos);
+ scrollTo(pos);
+
+ cancelPendingDrag();
+ // Will hit the STATE_DRAGGING check below
}
- mList.invalidate();
- return true;
- }
- } else if (action == MotionEvent.ACTION_MOVE) {
- if (mPendingDrag) {
- final float y = me.getY();
- if (Math.abs(y - mInitialTouchY) > mScaledTouchSlop) {
+ if (mState == STATE_DRAGGING) {
+ if (mList != null) {
+ // ViewGroup does the right thing already, but there might
+ // be other classes that don't properly reset on touch-up,
+ // so do this explicitly just in case.
+ mList.requestDisallowInterceptTouchEvent(false);
+ mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ }
+
+ setState(STATE_VISIBLE);
+ mList.postDelayed(mDeferHide, FADE_TIMEOUT);
+
+ return true;
+ }
+ } break;
+
+ case MotionEvent.ACTION_MOVE: {
+ if (mHasPendingDrag && Math.abs(me.getY() - mInitialTouchY) > mScaledTouchSlop) {
setState(STATE_DRAGGING);
+
if (mListAdapter == null && mList != null) {
getSectionsFromIndexer();
}
+
if (mList != null) {
mList.requestDisallowInterceptTouchEvent(true);
mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
@@ -824,86 +1188,168 @@
cancelPendingDrag();
// Will hit the STATE_DRAGGING check below
}
- }
- if (mState == STATE_DRAGGING) {
- final int viewHeight = mList.getHeight();
- // Jitter
- int newThumbY = (int) me.getY() - mThumbH + 10;
- if (newThumbY < 0) {
- newThumbY = 0;
- } else if (newThumbY + mThumbH > viewHeight) {
- newThumbY = viewHeight - mThumbH;
- }
- if (Math.abs(mThumbY - newThumbY) < 2) {
+
+ if (mState == STATE_DRAGGING) {
+ // TODO: Ignore jitter.
+ final float pos = getPosFromMotionEvent(me.getY());
+ setThumbPos(pos);
+
+ // If the previous scrollTo is still pending
+ if (mScrollCompleted) {
+ scrollTo(pos);
+ }
+
return true;
}
- mThumbY = newThumbY;
- // If the previous scrollTo is still pending
- if (mScrollCompleted) {
- scrollTo((float) mThumbY / (viewHeight - mThumbH));
- }
- return true;
- }
- } else if (action == MotionEvent.ACTION_CANCEL) {
- cancelPendingDrag();
+ } break;
+
+ case MotionEvent.ACTION_CANCEL: {
+ cancelPendingDrag();
+ } break;
}
+
return false;
}
- boolean isPointInside(float x, float y) {
- boolean inTrack = false;
- switch (mPosition) {
- default:
- case View.SCROLLBAR_POSITION_RIGHT:
- inTrack = x > mList.getWidth() - mThumbW;
- break;
- case View.SCROLLBAR_POSITION_LEFT:
- inTrack = x < mThumbW;
- break;
- }
-
- // Allow taps in the track to start moving.
- return inTrack && (mTrackDrawable != null || y >= mThumbY && y <= mThumbY + mThumbH);
+ /**
+ * Returns whether a coordinate is inside the scroller's activation area. If
+ * there is a track image, touching anywhere within the thumb-width of the
+ * track activates scrolling. Otherwise, the user has to touch inside thumb
+ * itself.
+ *
+ * @param x The x-coordinate.
+ * @param y The y-coordinate.
+ * @return Whether the coordinate is inside the scroller's activation area.
+ */
+ private boolean isPointInside(float x, float y) {
+ return isPointInsideX(x) && (mHasTrackImage || isPointInsideY(y));
}
- public class ScrollFade implements Runnable {
-
- long mStartTime;
- long mFadeDuration;
- static final int ALPHA_MAX = 208;
- static final long FADE_DURATION = 200;
-
- void startFade() {
- mFadeDuration = FADE_DURATION;
- mStartTime = SystemClock.uptimeMillis();
- setState(STATE_EXIT);
+ private boolean isPointInsideX(float x) {
+ if (mLayoutFromRight) {
+ return x >= mThumbImage.getLeft();
+ } else {
+ return x <= mThumbImage.getRight();
}
-
- int getAlpha() {
- if (getState() != STATE_EXIT) {
- return ALPHA_MAX;
- }
- int alpha;
- long now = SystemClock.uptimeMillis();
- if (now > mStartTime + mFadeDuration) {
- alpha = 0;
+ }
+
+ private boolean isPointInsideY(float y) {
+ return y >= mThumbImage.getTop() && y <= mThumbImage.getBottom();
+ }
+
+ /**
+ * Constructs an animator for the specified property on a group of views.
+ * See {@link ObjectAnimator#ofFloat(Object, String, float...)} for
+ * implementation details.
+ *
+ * @param property The property being animated.
+ * @param value The value to which that property should animate.
+ * @param views The target views to animate.
+ * @return An animator for all the specified views.
+ */
+ private static Animator groupAnimatorOfFloat(
+ Property<View, Float> property, float value, View... views) {
+ AnimatorSet animSet = new AnimatorSet();
+ AnimatorSet.Builder builder = null;
+
+ for (int i = views.length - 1; i >= 0; i--) {
+ final Animator anim = ObjectAnimator.ofFloat(views[i], property, value);
+ if (builder == null) {
+ builder = animSet.play(anim);
} else {
- alpha = (int) (ALPHA_MAX - ((now - mStartTime) * ALPHA_MAX) / mFadeDuration);
- }
- return alpha;
- }
-
- public void run() {
- if (getState() != STATE_EXIT) {
- startFade();
- return;
- }
-
- if (getAlpha() > 0) {
- mList.invalidate();
- } else {
- setState(STATE_NONE);
+ builder.with(anim);
}
}
+
+ return animSet;
+ }
+
+ /**
+ * Returns an animator for the view's scaleX value.
+ */
+ private static Animator animateScaleX(View v, float target) {
+ return ObjectAnimator.ofFloat(v, View.SCALE_X, target);
+ }
+
+ /**
+ * Returns an animator for the view's alpha value.
+ */
+ private static Animator animateAlpha(View v, float alpha) {
+ return ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
+ }
+
+ /**
+ * A Property wrapper around the <code>left</code> functionality handled by the
+ * {@link View#setLeft(int)} and {@link View#getLeft()} methods.
+ */
+ private static Property<View, Integer> LEFT = new IntProperty<View>("left") {
+ @Override
+ public void setValue(View object, int value) {
+ object.setLeft(value);
+ }
+
+ @Override
+ public Integer get(View object) {
+ return object.getLeft();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>top</code> functionality handled by the
+ * {@link View#setTop(int)} and {@link View#getTop()} methods.
+ */
+ private static Property<View, Integer> TOP = new IntProperty<View>("top") {
+ @Override
+ public void setValue(View object, int value) {
+ object.setTop(value);
+ }
+
+ @Override
+ public Integer get(View object) {
+ return object.getTop();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>right</code> functionality handled by the
+ * {@link View#setRight(int)} and {@link View#getRight()} methods.
+ */
+ private static Property<View, Integer> RIGHT = new IntProperty<View>("right") {
+ @Override
+ public void setValue(View object, int value) {
+ object.setRight(value);
+ }
+
+ @Override
+ public Integer get(View object) {
+ return object.getRight();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>bottom</code> functionality handled by the
+ * {@link View#setBottom(int)} and {@link View#getBottom()} methods.
+ */
+ private static Property<View, Integer> BOTTOM = new IntProperty<View>("bottom") {
+ @Override
+ public void setValue(View object, int value) {
+ object.setBottom(value);
+ }
+
+ @Override
+ public Integer get(View object) {
+ return object.getBottom();
+ }
+ };
+
+ /**
+ * Returns an animator for the view's bounds.
+ */
+ private static Animator animateBounds(View v, Rect bounds) {
+ final PropertyValuesHolder left = PropertyValuesHolder.ofInt(LEFT, bounds.left);
+ final PropertyValuesHolder top = PropertyValuesHolder.ofInt(TOP, bounds.top);
+ final PropertyValuesHolder right = PropertyValuesHolder.ofInt(RIGHT, bounds.right);
+ final PropertyValuesHolder bottom = PropertyValuesHolder.ofInt(BOTTOM, bounds.bottom);
+ return ObjectAnimator.ofPropertyValuesHolder(v, left, top, right, bottom);
}
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 738f63b..691c941 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -267,12 +267,12 @@
return mForeground;
}
- private int getPaddingLeftWithForeground() {
+ int getPaddingLeftWithForeground() {
return mForegroundInPadding ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
mPaddingLeft + mForegroundPaddingLeft;
}
- private int getPaddingRightWithForeground() {
+ int getPaddingRightWithForeground() {
return mForegroundInPadding ? Math.max(mPaddingRight, mForegroundPaddingRight) :
mPaddingRight + mForegroundPaddingRight;
}
@@ -385,6 +385,11 @@
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ layoutChildren(left, top, right, bottom, false /* no force left gravity */);
+ }
+
+ void layoutChildren(int left, int top, int right, int bottom,
+ boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
@@ -416,16 +421,16 @@
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- childLeft = parentLeft + lp.leftMargin;
- break;
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
- childLeft = parentRight - width - lp.rightMargin;
- break;
+ if (!forceLeftGravity) {
+ childLeft = parentRight - width - lp.rightMargin;
+ break;
+ }
+ case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 2309001..b0ab70d 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -24,7 +24,9 @@
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.LogPrinter;
import android.util.Pair;
+import android.util.Printer;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -124,6 +126,17 @@
* GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
* and <em>bottom</em> edges.
*
+ * <h4>Interpretation of GONE</h4>
+ *
+ * For layout purposes, GridLayout treats views whose visibility status is
+ * {@link View#GONE GONE}, as having zero width and height. This is subtly different from
+ * the policy of ignoring views that are marked as GONE outright. If, for example, a gone-marked
+ * view was alone in a column, that column would itself collapse to zero width if and only if
+ * no gravity was defined on the view. If gravity was defined, then the gone-marked
+ * view has no effect on the layout and the container should be laid out as if the view
+ * 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
@@ -208,10 +221,15 @@
// Misc constants
- static final String TAG = GridLayout.class.getName();
static final int MAX_SIZE = 100000;
static final int DEFAULT_CONTAINER_MARGIN = 0;
static final int UNINITIALIZED_HASH = 0;
+ static final Printer LOG_PRINTER = new LogPrinter(Log.DEBUG, GridLayout.class.getName());
+ static final Printer NO_PRINTER = new Printer() {
+ @Override
+ public void println(String x) {
+ }
+ };
// Defaults
@@ -240,6 +258,7 @@
int alignmentMode = DEFAULT_ALIGNMENT_MODE;
int defaultGap;
int lastLayoutParamsHashCode = UNINITIALIZED_HASH;
+ Printer printer = LOG_PRINTER;
// Constructors
@@ -556,6 +575,29 @@
requestLayout();
}
+ /**
+ * Return the printer that will log diagnostics from this layout.
+ *
+ * @see #setPrinter(android.util.Printer)
+ *
+ * @return the printer associated with this view
+ */
+ public Printer getPrinter() {
+ return printer;
+ }
+
+ /**
+ * Set the printer that will log diagnostics from this layout.
+ * The default value is created by {@link android.util.LogPrinter}.
+ *
+ * @param printer the printer associated with this layout
+ *
+ * @see #getPrinter()
+ */
+ public void setPrinter(Printer printer) {
+ this.printer = (printer == null) ? NO_PRINTER : printer;
+ }
+
// Static utility methods
static int max2(int[] a, int valueIfEmpty) {
@@ -915,7 +957,7 @@
protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
if (oldVisibility == GONE || newVisibility == GONE) {
- invalidateStructure();
+ invalidateStructure();
}
}
@@ -935,8 +977,8 @@
validateLayoutParams();
lastLayoutParamsHashCode = computeLayoutParamsHashCode();
} else if (lastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
- Log.w(TAG, "The fields of some layout parameters were modified in between layout " +
- "operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
+ printer.println("The fields of some layout parameters were modified in between "
+ + "layout operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
invalidateStructure();
consistencyCheck();
}
@@ -1246,6 +1288,7 @@
Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
for (int i = 0, N = getChildCount(); i < N; i++) {
View c = getChildAt(i);
+ // we must include views that are GONE here, see introductory javadoc
LayoutParams lp = getLayoutParams(c);
Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
Bounds bounds = getAlignment(spec.alignment, horizontal).getBounds();
@@ -1261,6 +1304,7 @@
}
for (int i = 0, N = getChildCount(); i < N; i++) {
View c = getChildAt(i);
+ // 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);
@@ -1527,8 +1571,8 @@
removed.add(arc);
}
}
- Log.d(TAG, axisName + " constraints: " + arcsToString(culprits) + " are inconsistent; "
- + "permanently removing: " + arcsToString(removed) + ". ");
+ printer.println(axisName + " constraints: " + arcsToString(culprits) +
+ " are inconsistent; permanently removing: " + arcsToString(removed) + ". ");
}
/*
@@ -2666,6 +2710,9 @@
@Override
public int getAlignmentValue(View view, int viewSize, int mode) {
+ if (view.getVisibility() == GONE) {
+ return 0;
+ }
int baseline = view.getBaseline();
return baseline == -1 ? UNDEFINED : baseline;
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index ff0579c..d114b76 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -20,7 +20,10 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.FocusFinder;
@@ -133,6 +136,8 @@
*/
private static final int INVALID_POINTER = -1;
+ private SavedState mSavedState;
+
public HorizontalScrollView(Context context) {
this(context, null);
}
@@ -595,12 +600,13 @@
final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+ // Calling overScrollBy will call onOverScrolled, which
+ // calls onScrollChanged if applicable.
if (overScrollBy(deltaX, 0, mScrollX, 0, range, 0,
mOverscrollDistance, 0, true)) {
// Break our velocity if we hit a scroll barrier.
mVelocityTracker.clear();
}
- onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (canOverscroll) {
final int pulledToX = oldX + deltaX;
@@ -732,9 +738,12 @@
boolean clampedX, boolean clampedY) {
// Treat animating scrolls differently; see #computeScroll() for why.
if (!mScroller.isFinished()) {
+ final int oldX = mScrollX;
+ final int oldY = mScrollY;
mScrollX = scrollX;
mScrollY = scrollY;
invalidateParentIfNeeded();
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (clampedX) {
mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0);
}
@@ -1448,14 +1457,52 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
+ int childWidth = 0;
+ int childMargins = 0;
+
+ if (getChildCount() > 0) {
+ childWidth = getChildAt(0).getMeasuredWidth();
+ LayoutParams childParams = (LayoutParams) getChildAt(0).getLayoutParams();
+ childMargins = childParams.leftMargin + childParams.rightMargin;
+ }
+
+ final int available = r - l - getPaddingLeftWithForeground() -
+ getPaddingRightWithForeground() - childMargins;
+
+ final boolean forceLeftGravity = (childWidth > available);
+
+ layoutChildren(l, t, r, b, forceLeftGravity);
+
mIsLayoutDirty = false;
// Give a child focus if it needs it
if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
- scrollToChild(mChildToScrollTo);
+ scrollToChild(mChildToScrollTo);
}
mChildToScrollTo = null;
+ if (!hasLayout()) {
+ final int scrollRange = Math.max(0,
+ childWidth - (r - l - mPaddingLeft - mPaddingRight));
+ if (mSavedState != null) {
+ if (isLayoutRtl() == mSavedState.isLayoutRtl) {
+ mScrollX = mSavedState.scrollPosition;
+ } else {
+ mScrollX = scrollRange - mSavedState.scrollPosition;
+ }
+ mSavedState = null;
+ } else {
+ if (isLayoutRtl()) {
+ mScrollX = scrollRange - mScrollX;
+ } // mScrollX default value is "0" for LTR
+ }
+ // Don't forget to clamp
+ if (mScrollX > scrollRange) {
+ mScrollX = scrollRange;
+ } else if (mScrollX < 0) {
+ mScrollX = 0;
+ }
+ }
+
// Calling this with the present values causes it to re-claim them
scrollTo(mScrollX, mScrollY);
}
@@ -1600,4 +1647,73 @@
}
return n;
}
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ // Some old apps reused IDs in ways they shouldn't have.
+ // Don't break them, but they don't get scroll state restoration.
+ super.onRestoreInstanceState(state);
+ return;
+ }
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mSavedState = ss;
+ requestLayout();
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ // Some old apps reused IDs in ways they shouldn't have.
+ // Don't break them, but they don't get scroll state restoration.
+ return super.onSaveInstanceState();
+ }
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.scrollPosition = mScrollX;
+ ss.isLayoutRtl = isLayoutRtl();
+ return ss;
+ }
+
+ static class SavedState extends BaseSavedState {
+ public int scrollPosition;
+ public boolean isLayoutRtl;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public SavedState(Parcel source) {
+ super(source);
+ scrollPosition = source.readInt();
+ isLayoutRtl = (source.readInt() == 0) ? true : false;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(scrollPosition);
+ dest.writeInt(isLayoutRtl ? 1 : 0);
+ }
+
+ @Override
+ public String toString() {
+ return "HorizontalScrollView.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " scrollPosition=" + scrollPosition
+ + " isLayoutRtl=" + isLayoutRtl + "}";
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index f42999d..d990a20 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -240,35 +240,39 @@
}
/**
- * Add a fixed view to appear at the top of the list. If addHeaderView is
+ * Add a fixed view to appear at the top of the list. If this method is
* called more than once, the views will appear in the order they were
* added. Views added using this call can take focus if they want.
* <p>
- * NOTE: Call this before calling setAdapter. This is so ListView can wrap
- * the supplied cursor with one that will also account for header and footer
- * views.
+ * Note: When first introduced, this method could only be called before
+ * setting the adapter with {@link #setAdapter(ListAdapter)}. Starting with
+ * {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, this method may be
+ * called at any time. If the ListView's adapter does not extend
+ * {@link HeaderViewListAdapter}, it will be wrapped with a supporting
+ * instance of {@link WrapperListAdapter}.
*
* @param v The view to add.
* @param data Data to associate with this view
* @param isSelectable whether the item is selectable
*/
public void addHeaderView(View v, Object data, boolean isSelectable) {
-
- if (mAdapter != null && ! (mAdapter instanceof HeaderViewListAdapter)) {
- throw new IllegalStateException(
- "Cannot add header view to list -- setAdapter has already been called.");
- }
-
- FixedViewInfo info = new FixedViewInfo();
+ final FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mHeaderViewInfos.add(info);
- // in the case of re-adding a header view, or adding one later on,
- // we need to notify the observer
- if (mAdapter != null && mDataSetObserver != null) {
- mDataSetObserver.onChanged();
+ // Wrap the adapter if it wasn't already wrapped.
+ if (mAdapter != null) {
+ if (!(mAdapter instanceof HeaderViewListAdapter)) {
+ mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
+ }
+
+ // In the case of re-adding a header view, or adding one later on,
+ // we need to notify the observer.
+ if (mDataSetObserver != null) {
+ mDataSetObserver.onChanged();
+ }
}
}
@@ -277,9 +281,12 @@
* called more than once, the views will appear in the order they were
* added. Views added using this call can take focus if they want.
* <p>
- * NOTE: Call this before calling setAdapter. This is so ListView can wrap
- * the supplied cursor with one that will also account for header and footer
- * views.
+ * Note: When first introduced, this method could only be called before
+ * setting the adapter with {@link #setAdapter(ListAdapter)}. Starting with
+ * {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, this method may be
+ * called at any time. If the ListView's adapter does not extend
+ * {@link HeaderViewListAdapter}, it will be wrapped with a supporting
+ * instance of {@link WrapperListAdapter}.
*
* @param v The view to add.
*/
@@ -330,41 +337,49 @@
* called more than once, the views will appear in the order they were
* added. Views added using this call can take focus if they want.
* <p>
- * NOTE: Call this before calling setAdapter. This is so ListView can wrap
- * the supplied cursor with one that will also account for header and footer
- * views.
+ * Note: When first introduced, this method could only be called before
+ * setting the adapter with {@link #setAdapter(ListAdapter)}. Starting with
+ * {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, this method may be
+ * called at any time. If the ListView's adapter does not extend
+ * {@link HeaderViewListAdapter}, it will be wrapped with a supporting
+ * instance of {@link WrapperListAdapter}.
*
* @param v The view to add.
* @param data Data to associate with this view
* @param isSelectable true if the footer view can be selected
*/
public void addFooterView(View v, Object data, boolean isSelectable) {
-
- // NOTE: do not enforce the adapter being null here, since unlike in
- // addHeaderView, it was never enforced here, and so existing apps are
- // relying on being able to add a footer and then calling setAdapter to
- // force creation of the HeaderViewListAdapter wrapper
-
- FixedViewInfo info = new FixedViewInfo();
+ final FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mFooterViewInfos.add(info);
- // in the case of re-adding a footer view, or adding one later on,
- // we need to notify the observer
- if (mAdapter != null && mDataSetObserver != null) {
- mDataSetObserver.onChanged();
+ // Wrap the adapter if it wasn't already wrapped.
+ if (mAdapter != null) {
+ if (!(mAdapter instanceof HeaderViewListAdapter)) {
+ mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
+ }
+
+ // In the case of re-adding a footer view, or adding one later on,
+ // we need to notify the observer.
+ if (mDataSetObserver != null) {
+ mDataSetObserver.onChanged();
+ }
}
}
/**
- * Add a fixed view to appear at the bottom of the list. If addFooterView is called more
- * than once, the views will appear in the order they were added. Views added using
- * this call can take focus if they want.
- * <p>NOTE: Call this before calling setAdapter. This is so ListView can wrap the supplied
- * cursor with one that will also account for header and footer views.
- *
+ * Add a fixed view to appear at the bottom of the list. If addFooterView is
+ * called more than once, the views will appear in the order they were
+ * added. Views added using this call can take focus if they want.
+ * <p>
+ * Note: When first introduced, this method could only be called before
+ * setting the adapter with {@link #setAdapter(ListAdapter)}. Starting with
+ * {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, this method may be
+ * called at any time. If the ListView's adapter does not extend
+ * {@link HeaderViewListAdapter}, it will be wrapped with a supporting
+ * instance of {@link WrapperListAdapter}.
*
* @param v The view to add.
*/
@@ -2058,17 +2073,61 @@
position--;
}
}
-
- if (position < 0 || position >= count) {
- return INVALID_POSITION;
- }
- return position;
- } else {
- if (position < 0 || position >= count) {
- return INVALID_POSITION;
- }
- return position;
}
+
+ if (position < 0 || position >= count) {
+ return INVALID_POSITION;
+ }
+
+ return position;
+ }
+
+ /**
+ * Find a position that can be selected (i.e., is not a separator). If there
+ * are no selectable positions in the specified direction from the starting
+ * position, searches in the opposite direction from the starting position
+ * to the current position.
+ *
+ * @param current the current position
+ * @param position the starting position
+ * @param lookDown whether to look down for other positions
+ * @return the next selectable position, or {@link #INVALID_POSITION} if
+ * nothing can be found
+ */
+ int lookForSelectablePositionAfter(int current, int position, boolean lookDown) {
+ final ListAdapter adapter = mAdapter;
+ if (adapter == null || isInTouchMode()) {
+ return INVALID_POSITION;
+ }
+
+ // First check after the starting position in the specified direction.
+ final int after = lookForSelectablePosition(position, lookDown);
+ if (after != INVALID_POSITION) {
+ return after;
+ }
+
+ // Then check between the starting position and the current position.
+ final int count = adapter.getCount();
+ current = MathUtils.constrain(current, -1, count - 1);
+ if (lookDown) {
+ position = Math.min(position - 1, count - 1);
+ while ((position > current) && !adapter.isEnabled(position)) {
+ position--;
+ }
+ if (position <= current) {
+ return INVALID_POSITION;
+ }
+ } else {
+ position = Math.max(0, position + 1);
+ while ((position < current) && !adapter.isEnabled(position)) {
+ position++;
+ }
+ if (position >= current) {
+ return INVALID_POSITION;
+ }
+ }
+
+ return position;
}
/**
@@ -2281,27 +2340,30 @@
* @return whether selection was moved
*/
boolean pageScroll(int direction) {
- int nextPage = -1;
- boolean down = false;
+ final int nextPage;
+ final boolean down;
if (direction == FOCUS_UP) {
nextPage = Math.max(0, mSelectedPosition - getChildCount() - 1);
+ down = false;
} else if (direction == FOCUS_DOWN) {
nextPage = Math.min(mItemCount - 1, mSelectedPosition + getChildCount() - 1);
down = true;
+ } else {
+ return false;
}
if (nextPage >= 0) {
- int position = lookForSelectablePosition(nextPage, down);
+ final int position = lookForSelectablePositionAfter(mSelectedPosition, nextPage, down);
if (position >= 0) {
mLayoutMode = LAYOUT_SPECIFIC;
mSpecificTop = mPaddingTop + getVerticalFadingEdgeLength();
- if (down && position > mItemCount - getChildCount()) {
+ if (down && (position > (mItemCount - getChildCount()))) {
mLayoutMode = LAYOUT_FORCE_BOTTOM;
}
- if (!down && position < getChildCount()) {
+ if (!down && (position < getChildCount())) {
mLayoutMode = LAYOUT_FORCE_TOP;
}
@@ -2319,18 +2381,18 @@
}
/**
- * Go to the last or first item if possible (not worrying about panning across or navigating
- * within the internal focus of the currently selected item.)
+ * Go to the last or first item if possible (not worrying about panning
+ * across or navigating within the internal focus of the currently selected
+ * item.)
*
* @param direction either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN}
- *
* @return whether selection was moved
*/
boolean fullScroll(int direction) {
boolean moved = false;
if (direction == FOCUS_UP) {
if (mSelectedPosition != 0) {
- int position = lookForSelectablePosition(0, true);
+ final int position = lookForSelectablePositionAfter(mSelectedPosition, 0, true);
if (position >= 0) {
mLayoutMode = LAYOUT_FORCE_TOP;
setSelectionInt(position);
@@ -2339,8 +2401,10 @@
moved = true;
}
} else if (direction == FOCUS_DOWN) {
- if (mSelectedPosition < mItemCount - 1) {
- int position = lookForSelectablePosition(mItemCount - 1, true);
+ final int lastItem = (mItemCount - 1);
+ if (mSelectedPosition < lastItem) {
+ final int position = lookForSelectablePositionAfter(
+ mSelectedPosition, lastItem, false);
if (position >= 0) {
mLayoutMode = LAYOUT_FORCE_BOTTOM;
setSelectionInt(position);
@@ -2425,24 +2489,37 @@
/**
* Used by {@link #arrowScrollImpl(int)} to help determine the next selected position
- * to move to. This can return a position currently not represented by a view on screen
- * but only in the direction given.
+ * to move to. This return a position in the direction given if the selected item
+ * is fully visible.
*
+ * @param selectedView Current selected view to move from
* @param selectedPos Current selected position to move from
* @param direction Direction to move in
* @return Desired selected position after moving in the given direction
*/
- private final int nextSelectedPositionForDirection(int selectedPos, int direction) {
+ private final int nextSelectedPositionForDirection(
+ View selectedView, int selectedPos, int direction) {
int nextSelected;
+
if (direction == View.FOCUS_DOWN) {
- nextSelected = selectedPos != INVALID_POSITION && selectedPos >= mFirstPosition ?
- selectedPos + 1 :
- mFirstPosition;
+ final int listBottom = getHeight() - mListPadding.bottom;
+ if (selectedView != null && selectedView.getBottom() <= listBottom) {
+ nextSelected = selectedPos != INVALID_POSITION && selectedPos >= mFirstPosition ?
+ selectedPos + 1 :
+ mFirstPosition;
+ } else {
+ return INVALID_POSITION;
+ }
} else {
- final int lastPos = mFirstPosition + getChildCount() - 1;
- nextSelected = selectedPos != INVALID_POSITION && selectedPos <= lastPos ?
- selectedPos - 1 :
- lastPos;
+ final int listTop = mListPadding.top;
+ if (selectedView != null && selectedView.getTop() >= listTop) {
+ final int lastPos = mFirstPosition + getChildCount() - 1;
+ nextSelected = selectedPos != INVALID_POSITION && selectedPos <= lastPos ?
+ selectedPos - 1 :
+ lastPos;
+ } else {
+ return INVALID_POSITION;
+ }
}
if (nextSelected < 0 || nextSelected >= mAdapter.getCount()) {
@@ -2466,7 +2543,7 @@
View selectedView = getSelectedView();
int selectedPos = mSelectedPosition;
- int nextSelectedPosition = nextSelectedPositionForDirection(selectedPos, direction);
+ int nextSelectedPosition = nextSelectedPositionForDirection(selectedView, selectedPos, direction);
int amountToScroll = amountToScroll(direction, nextSelectedPosition);
// if we are moving focus, we may OVERRIDE the default behavior
@@ -3195,7 +3272,7 @@
final int count = getChildCount();
final int headerCount = mHeaderViewInfos.size();
final int itemCount = mItemCount;
- final int footerLimit = itemCount - mFooterViewInfos.size() - 1;
+ final int footerLimit = (itemCount - mFooterViewInfos.size());
final boolean headerDividers = mHeaderDividersEnabled;
final boolean footerDividers = mFooterDividersEnabled;
final int first = mFirstPosition;
@@ -3239,17 +3316,25 @@
}
for (int i = 0; i < count; i++) {
- if ((headerDividers || first + i >= headerCount) &&
- (footerDividers || first + i < footerLimit)) {
- View child = getChildAt(i);
+ final int itemIndex = (first + i);
+ final boolean isHeader = (itemIndex < headerCount);
+ final boolean isFooter = (itemIndex >= footerLimit);
+ if ((headerDividers || !isHeader) && (footerDividers || !isFooter)) {
+ final View child = getChildAt(i);
bottom = child.getBottom();
- // Don't draw dividers next to items that are not enabled
+ final boolean isLastItem = (i == (count - 1));
- if (drawDividers &&
- (bottom < listBottom && !(drawOverscrollFooter && i == count - 1))) {
- if ((areAllItemsSelectable ||
- (adapter.isEnabled(first + i) && (i == count - 1 ||
- adapter.isEnabled(first + i + 1))))) {
+ if (drawDividers && (bottom < listBottom)
+ && !(drawOverscrollFooter && isLastItem)) {
+ final int nextIndex = (itemIndex + 1);
+ // Draw dividers between enabled items, headers and/or
+ // footers when enabled, and the end of the list.
+ if (areAllItemsSelectable || ((adapter.isEnabled(itemIndex)
+ || (headerDividers && isHeader)
+ || (footerDividers && isFooter)) && (isLastItem
+ || adapter.isEnabled(nextIndex)
+ || (headerDividers && (nextIndex < headerCount))
+ || (footerDividers && (nextIndex >= footerLimit))))) {
bounds.top = bottom;
bounds.bottom = bottom + dividerHeight;
drawDivider(canvas, bounds, i);
@@ -3282,20 +3367,28 @@
final int start = drawOverscrollHeader ? 1 : 0;
for (int i = start; i < count; i++) {
- if ((headerDividers || first + i >= headerCount) &&
- (footerDividers || first + i < footerLimit)) {
- View child = getChildAt(i);
+ final int itemIndex = (first + i);
+ final boolean isHeader = (itemIndex < headerCount);
+ final boolean isFooter = (itemIndex >= footerLimit);
+ if ((headerDividers || !isHeader) && (footerDividers || !isFooter)) {
+ final View child = getChildAt(i);
top = child.getTop();
- // Don't draw dividers next to items that are not enabled
- if (top > effectivePaddingTop) {
- if ((areAllItemsSelectable ||
- (adapter.isEnabled(first + i) && (i == count - 1 ||
- adapter.isEnabled(first + i + 1))))) {
+ if (drawDividers && (top > effectivePaddingTop)) {
+ final boolean isFirstItem = (i == start);
+ final int previousIndex = (itemIndex - 1);
+ // Draw dividers between enabled items, headers and/or
+ // footers when enabled, and the end of the list.
+ if (areAllItemsSelectable || ((adapter.isEnabled(itemIndex)
+ || (headerDividers && isHeader)
+ || (footerDividers && isFooter)) && (isFirstItem
+ || adapter.isEnabled(previousIndex)
+ || (headerDividers && (previousIndex < headerCount))
+ || (footerDividers && (previousIndex >= footerLimit))))) {
bounds.top = top - dividerHeight;
bounds.bottom = top;
- // Give the method the child ABOVE the divider, so we
- // subtract one from our child
- // position. Give -1 when there is no child above the
+ // Give the method the child ABOVE the divider,
+ // so we subtract one from our child position.
+ // Give -1 when there is no child above the
// divider.
drawDivider(canvas, bounds, i - 1);
} else if (fillForMissingDividers) {
@@ -3404,6 +3497,7 @@
* @param headerDividersEnabled True to draw the headers, false otherwise.
*
* @see #setFooterDividersEnabled(boolean)
+ * @see #areHeaderDividersEnabled()
* @see #addHeaderView(android.view.View)
*/
public void setHeaderDividersEnabled(boolean headerDividersEnabled) {
@@ -3412,17 +3506,36 @@
}
/**
+ * @return Whether the drawing of the divider for header views is enabled
+ *
+ * @see #setHeaderDividersEnabled(boolean)
+ */
+ public boolean areHeaderDividersEnabled() {
+ return mHeaderDividersEnabled;
+ }
+
+ /**
* Enables or disables the drawing of the divider for footer views.
*
* @param footerDividersEnabled True to draw the footers, false otherwise.
*
* @see #setHeaderDividersEnabled(boolean)
+ * @see #areFooterDividersEnabled()
* @see #addFooterView(android.view.View)
*/
public void setFooterDividersEnabled(boolean footerDividersEnabled) {
mFooterDividersEnabled = footerDividersEnabled;
invalidate();
}
+
+ /**
+ * @return Whether the drawing of the divider for footer views is enabled
+ *
+ * @see #setFooterDividersEnabled(boolean)
+ */
+ public boolean areFooterDividersEnabled() {
+ return mFooterDividersEnabled;
+ }
/**
* Sets the drawable that will be drawn above all other list content.
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index f940226..fd3dc03 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -16,22 +16,21 @@
package android.widget;
+import android.util.ArrayMap;
import com.android.internal.R;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.SortedSet;
import java.util.TreeSet;
import android.content.Context;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
-import android.util.Pools.SimplePool;
+import android.util.Pools.SynchronizedPool;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.View;
@@ -42,7 +41,6 @@
import android.widget.RemoteViews.RemoteView;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-import static android.util.Log.d;
/**
* A Layout where the positions of the children can be described in relation to each other or to the
@@ -83,10 +81,6 @@
*/
@RemoteView
public class RelativeLayout extends ViewGroup {
- private static final String LOG_TAG = "RelativeLayout";
-
- private static final boolean DEBUG_GRAPH = false;
-
public static final int TRUE = -1;
/**
@@ -212,8 +206,8 @@
private SortedSet<View> mTopToBottomLeftToRightSet = null;
private boolean mDirtyHierarchy;
- private View[] mSortedHorizontalChildren = new View[0];
- private View[] mSortedVerticalChildren = new View[0];
+ private View[] mSortedHorizontalChildren;
+ private View[] mSortedVerticalChildren;
private final DependencyGraph mGraph = new DependencyGraph();
// Compatibility hack. Old versions of the platform had problems
@@ -360,42 +354,26 @@
}
private void sortChildren() {
- int count = getChildCount();
- if (mSortedVerticalChildren.length != count) mSortedVerticalChildren = new View[count];
- if (mSortedHorizontalChildren.length != count) mSortedHorizontalChildren = new View[count];
+ final int count = getChildCount();
+ if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
+ mSortedVerticalChildren = new View[count];
+ }
+
+ if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
+ mSortedHorizontalChildren = new View[count];
+ }
final DependencyGraph graph = mGraph;
graph.clear();
for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- graph.add(child);
- }
-
- if (DEBUG_GRAPH) {
- d(LOG_TAG, "=== Sorted vertical children");
- graph.log(getResources(), RULES_VERTICAL);
- d(LOG_TAG, "=== Sorted horizontal children");
- graph.log(getResources(), RULES_HORIZONTAL);
+ graph.add(getChildAt(i));
}
graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
-
- if (DEBUG_GRAPH) {
- d(LOG_TAG, "=== Ordered list of vertical children");
- for (View view : mSortedVerticalChildren) {
- DependencyGraph.printViewId(getResources(), view);
- }
- d(LOG_TAG, "=== Ordered list of horizontal children");
- for (View view : mSortedHorizontalChildren) {
- DependencyGraph.printViewId(getResources(), view);
- }
- }
}
- // TODO: we need to find another way to implement RelativeLayout
- // This implementation cannot handle every case
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mDirtyHierarchy) {
@@ -900,8 +878,6 @@
} else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
- } else {
- // FIXME uh oh...
}
}
@@ -926,8 +902,6 @@
} else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
- } else {
- // FIXME uh oh...
}
}
@@ -938,8 +912,6 @@
if (0 != rules[ALIGN_PARENT_RIGHT]) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
- } else {
- // FIXME uh oh...
}
}
}
@@ -958,8 +930,6 @@
} else if (childParams.alignWithParent && rules[ABOVE] != 0) {
if (myHeight >= 0) {
childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
- } else {
- // FIXME uh oh...
}
}
@@ -984,8 +954,6 @@
} else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
if (myHeight >= 0) {
childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
- } else {
- // FIXME uh oh...
}
}
@@ -996,8 +964,6 @@
if (0 != rules[ALIGN_PARENT_BOTTOM]) {
if (myHeight >= 0) {
childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
- } else {
- // FIXME uh oh...
}
}
@@ -1677,8 +1643,10 @@
sorted[index++] = view;
- final HashMap<Node, DependencyGraph> dependents = node.dependents;
- for (Node dependent : dependents.keySet()) {
+ final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
+ final int count = dependents.size();
+ for (int i = 0; i < count; i++) {
+ final Node dependent = dependents.keyAt(i);
final SparseArray<Node> dependencies = dependent.dependencies;
dependencies.remove(key);
@@ -1756,61 +1724,6 @@
}
/**
- * Prints the dependency graph for the specified rules.
- *
- * @param resources The context's resources to print the ids.
- * @param rules The list of rules to take into account.
- */
- void log(Resources resources, int... rules) {
- final ArrayDeque<Node> roots = findRoots(rules);
- for (Node node : roots) {
- printNode(resources, node);
- }
- }
-
- static void printViewId(Resources resources, View view) {
- if (view.getId() != View.NO_ID) {
- d(LOG_TAG, resources.getResourceEntryName(view.getId()));
- } else {
- d(LOG_TAG, "NO_ID");
- }
- }
-
- private static void appendViewId(Resources resources, Node node, StringBuilder buffer) {
- if (node.view.getId() != View.NO_ID) {
- buffer.append(resources.getResourceEntryName(node.view.getId()));
- } else {
- buffer.append("NO_ID");
- }
- }
-
- private static void printNode(Resources resources, Node node) {
- if (node.dependents.size() == 0) {
- printViewId(resources, node.view);
- } else {
- for (Node dependent : node.dependents.keySet()) {
- StringBuilder buffer = new StringBuilder();
- appendViewId(resources, node, buffer);
- printdependents(resources, dependent, buffer);
- }
- }
- }
-
- private static void printdependents(Resources resources, Node node, StringBuilder buffer) {
- buffer.append(" -> ");
- appendViewId(resources, node, buffer);
-
- if (node.dependents.size() == 0) {
- d(LOG_TAG, buffer.toString());
- } else {
- for (Node dependent : node.dependents.keySet()) {
- StringBuilder subBuffer = new StringBuilder(buffer);
- printdependents(resources, dependent, subBuffer);
- }
- }
- }
-
- /**
* A node in the dependency graph. A node is a view, its list of dependencies
* and its list of dependents.
*
@@ -1826,7 +1739,8 @@
* The list of dependents for this node; a dependent is a node
* that needs this node to be processed first.
*/
- final HashMap<Node, DependencyGraph> dependents = new HashMap<Node, DependencyGraph>();
+ final ArrayMap<Node, DependencyGraph> dependents =
+ new ArrayMap<Node, DependencyGraph>();
/**
* The list of dependencies for this node.
@@ -1839,7 +1753,8 @@
// The pool is static, so all nodes instances are shared across
// activities, that's why we give it a rather high limit
private static final int POOL_LIMIT = 100;
- private static final SimplePool<Node> sPool = new SimplePool<Node>(POOL_LIMIT);
+ private static final SynchronizedPool<Node> sPool =
+ new SynchronizedPool<Node>(POOL_LIMIT);
static Node acquire(View view) {
Node node = sPool.acquire();
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index aeee111..3ff0cee 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -22,9 +22,11 @@
import java.util.HashSet;
import java.util.LinkedList;
+import android.Manifest;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -34,6 +36,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
+import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.MeasureSpec;
@@ -50,9 +53,11 @@
*/
/** @hide */
public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback {
+ private static final String MULTI_USER_PERM = Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
private static final String TAG = "RemoteViewsAdapter";
- // The max number of items in the cache
+ // The max number of items in the cache
private static final int sDefaultCacheSize = 40;
// The delay (in millis) to wait until attempting to unbind from a service after a request.
// This ensures that we don't stay continually bound to the service and that it can be destroyed
@@ -63,7 +68,7 @@
private static final int sDefaultLoadingViewHeight = 50;
// Type defs for controlling different messages across the main and worker message queues
- private static final int sDefaultMessageType = 0;
+ private static final int sDefaultMessageType = 0;
private static final int sUnbindServiceMessageType = 1;
private final Context mContext;
@@ -90,7 +95,7 @@
private Handler mMainQueue;
// We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
- // structures;
+ // structures;
private static final HashMap<RemoteViewsCacheKey,
FixedSizeRemoteViewsCache> sCachedRemoteViewsCaches
= new HashMap<RemoteViewsCacheKey,
@@ -155,13 +160,12 @@
try {
RemoteViewsAdapter adapter;
final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
- if (Process.myUid() == Process.SYSTEM_UID
- && (adapter = mAdapter.get()) != null) {
+ if ((adapter = mAdapter.get()) != null) {
+ checkInteractAcrossUsersPermission(context, adapter.mUserId);
mgr.bindRemoteViewsService(appWidgetId, intent, asBinder(),
new UserHandle(adapter.mUserId));
} else {
- mgr.bindRemoteViewsService(appWidgetId, intent, asBinder(),
- Process.myUserHandle());
+ Slog.w(TAG, "bind: adapter was null");
}
mIsConnecting = true;
} catch (Exception e) {
@@ -176,12 +180,12 @@
try {
RemoteViewsAdapter adapter;
final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
- if (Process.myUid() == Process.SYSTEM_UID
- && (adapter = mAdapter.get()) != null) {
+ if ((adapter = mAdapter.get()) != null) {
+ checkInteractAcrossUsersPermission(context, adapter.mUserId);
mgr.unbindRemoteViewsService(appWidgetId, intent,
new UserHandle(adapter.mUserId));
} else {
- mgr.unbindRemoteViewsService(appWidgetId, intent, Process.myUserHandle());
+ Slog.w(TAG, "unbind: adapter was null");
}
mIsConnecting = false;
} catch (Exception e) {
@@ -263,7 +267,7 @@
// Clear the main/worker queues
final RemoteViewsAdapter adapter = mAdapter.get();
if (adapter == null) return;
-
+
adapter.mMainQueue.post(new Runnable() {
@Override
public void run() {
@@ -828,11 +832,9 @@
}
mRequestedViews = new RemoteViewsFrameLayoutRefSet();
- if (Process.myUid() == Process.SYSTEM_UID) {
- mUserId = new LockPatternUtils(context).getCurrentUser();
- } else {
- mUserId = UserHandle.myUserId();
- }
+ checkInteractAcrossUsersPermission(context, UserHandle.myUserId());
+ mUserId = context.getUserId();
+
// Strip the previously injected app widget id from service intent
if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
@@ -876,6 +878,15 @@
}
}
+ private static void checkInteractAcrossUsersPermission(Context context, int userId) {
+ if (context.getUserId() != userId
+ && context.checkCallingOrSelfPermission(MULTI_USER_PERM)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must have permission " + MULTI_USER_PERM
+ + " to inflate another user's widget");
+ }
+ }
+
@Override
protected void finalize() throws Throwable {
try {
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index 93a1179..9886bc3 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -226,6 +226,12 @@
}
@Override
+ public int getAlpha() {
+ // All elements should have same alpha, just return one of them
+ return mVerticalThumb.getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
if (mVerticalTrack != null) {
mVerticalTrack.setColorFilter(cf);
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index bc41931..3d361f1 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -16,6 +16,9 @@
package android.widget;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
import com.android.internal.R;
import android.content.Context;
@@ -149,6 +152,8 @@
*/
private static final int INVALID_POINTER = -1;
+ private SavedState mSavedState;
+
public ScrollView(Context context) {
this(context, null);
}
@@ -631,12 +636,13 @@
final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+ // Calling overScrollBy will call onOverScrolled, which
+ // calls onScrollChanged if applicable.
if (overScrollBy(0, deltaY, 0, mScrollY,
0, range, 0, mOverscrollDistance, true)) {
// Break our velocity if we hit a scroll barrier.
mVelocityTracker.clear();
}
- onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (canOverscroll) {
final int pulledToY = oldY + deltaY;
@@ -753,9 +759,12 @@
boolean clampedX, boolean clampedY) {
// Treat animating scrolls differently; see #computeScroll() for why.
if (!mScroller.isFinished()) {
+ final int oldX = mScrollX;
+ final int oldY = mScrollY;
mScrollX = scrollX;
mScrollY = scrollY;
invalidateParentIfNeeded();
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (clampedY) {
mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange());
}
@@ -1464,6 +1473,24 @@
}
mChildToScrollTo = null;
+ if (!hasLayout()) {
+ if (mSavedState != null) {
+ mScrollY = mSavedState.scrollPosition;
+ mSavedState = null;
+ } // mScrollY default value is "0"
+
+ final int childHeight = (getChildCount() > 0) ? getChildAt(0).getMeasuredHeight() : 0;
+ final int scrollRange = Math.max(0,
+ childHeight - (b - t - mPaddingBottom - mPaddingTop));
+
+ // Don't forget to clamp
+ if (mScrollY > scrollRange) {
+ mScrollY = scrollRange;
+ } else if (mScrollY < 0) {
+ mScrollY = 0;
+ }
+ }
+
// Calling this with the present values causes it to re-claim them
scrollTo(mScrollX, mScrollY);
}
@@ -1633,4 +1660,69 @@
}
return n;
}
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ // Some old apps reused IDs in ways they shouldn't have.
+ // Don't break them, but they don't get scroll state restoration.
+ super.onRestoreInstanceState(state);
+ return;
+ }
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mSavedState = ss;
+ requestLayout();
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ // Some old apps reused IDs in ways they shouldn't have.
+ // Don't break them, but they don't get scroll state restoration.
+ return super.onSaveInstanceState();
+ }
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.scrollPosition = mScrollY;
+ return ss;
+ }
+
+ static class SavedState extends BaseSavedState {
+ public int scrollPosition;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public SavedState(Parcel source) {
+ super(source);
+ scrollPosition = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(scrollPosition);
+ }
+
+ @Override
+ public String toString() {
+ return "HorizontalScrollView.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " scrollPosition=" + scrollPosition + "}";
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
}
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 9e7f97e..b204dfd 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -753,4 +753,39 @@
}
}
}
+
+ public static boolean haveWordBoundariesChanged(final Editable editable, final int start,
+ final int end, final int spanStart, final int spanEnd) {
+ final boolean haveWordBoundariesChanged;
+ if (spanEnd != start && spanStart != end) {
+ haveWordBoundariesChanged = true;
+ if (DBG) {
+ Log.d(TAG, "(1) Text inside the span has been modified. Remove.");
+ }
+ } else if (spanEnd == start && start < editable.length()) {
+ final int codePoint = Character.codePointAt(editable, start);
+ haveWordBoundariesChanged = Character.isLetterOrDigit(codePoint);
+ if (DBG) {
+ Log.d(TAG, "(2) Characters have been appended to the spanned text. "
+ + (haveWordBoundariesChanged ? "Remove.<" : "Keep. <") + (char)(codePoint)
+ + ">, " + editable + ", " + editable.subSequence(spanStart, spanEnd) + ", "
+ + start);
+ }
+ } else if (spanStart == end && end > 0) {
+ final int codePoint = Character.codePointBefore(editable, end);
+ haveWordBoundariesChanged = Character.isLetterOrDigit(codePoint);
+ if (DBG) {
+ Log.d(TAG, "(3) Characters have been prepended to the spanned text. "
+ + (haveWordBoundariesChanged ? "Remove.<" : "Keep.<") + (char)(codePoint)
+ + ">, " + editable + ", " + editable.subSequence(spanStart, spanEnd) + ", "
+ + end);
+ }
+ } else {
+ if (DBG) {
+ Log.d(TAG, "(4) Characters adjacent to the spanned text were deleted. Keep.");
+ }
+ haveWordBoundariesChanged = false;
+ }
+ return haveWordBoundariesChanged;
+ }
}
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index a564c96..b3b95d9 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -95,6 +95,7 @@
*
* @see #setFormat12Hour(CharSequence)
* @see #getFormat12Hour()
+ *
* @deprecated Let the system use locale-appropriate defaults instead.
*/
public static final CharSequence DEFAULT_FORMAT_12_HOUR = "h:mm a";
@@ -108,6 +109,7 @@
*
* @see #setFormat24Hour(CharSequence)
* @see #getFormat24Hour()
+ *
* @deprecated Let the system use locale-appropriate defaults instead.
*/
public static final CharSequence DEFAULT_FORMAT_24_HOUR = "H:mm";
@@ -162,9 +164,7 @@
};
/**
- * Creates a new clock using the default patterns
- * {@link #DEFAULT_FORMAT_24_HOUR} and {@link #DEFAULT_FORMAT_12_HOUR}
- * respectively for the 24-hour and 12-hour modes.
+ * Creates a new clock using the default patterns for the current locale.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
@@ -258,20 +258,26 @@
}
/**
- * Specifies the formatting pattern used to display the date and/or time
+ * <p>Specifies the formatting pattern used to display the date and/or time
* in 12-hour mode. The formatting pattern syntax is described in
- * {@link DateFormat}.
+ * {@link DateFormat}.</p>
*
- * If this pattern is set to null, {@link #getFormat24Hour()} will be used
+ * <p>If this pattern is set to null, {@link #getFormat24Hour()} will be used
* even in 12-hour mode. If both 24-hour and 12-hour formatting patterns
- * are set to null, {@link #DEFAULT_FORMAT_24_HOUR} and
- * {@link #DEFAULT_FORMAT_12_HOUR} will be used instead.
+ * are set to null, the default pattern for the current locale will be used
+ * instead.</p>
+ *
+ * <p><strong>Note:</strong> if styling is not needed, it is highly recommended
+ * you supply a format string generated by
+ * {@link DateFormat#getBestDateTimePattern(java.util.Locale, String)}. This method
+ * takes care of generating a format string adapted to the desired locale.</p>
+ *
*
* @param format A date/time formatting pattern as described in {@link DateFormat}
*
* @see #getFormat12Hour()
* @see #is24HourModeEnabled()
- * @see #DEFAULT_FORMAT_12_HOUR
+ * @see DateFormat#getBestDateTimePattern(java.util.Locale, String)
* @see DateFormat
*
* @attr ref android.R.styleable#TextClock_format12Hour
@@ -300,20 +306,25 @@
}
/**
- * Specifies the formatting pattern used to display the date and/or time
+ * <p>Specifies the formatting pattern used to display the date and/or time
* in 24-hour mode. The formatting pattern syntax is described in
- * {@link DateFormat}.
+ * {@link DateFormat}.</p>
*
- * If this pattern is set to null, {@link #getFormat12Hour()} will be used
- * even in 24-hour mode. If both 24-hour and 12-hour formatting patterns
- * are set to null, {@link #DEFAULT_FORMAT_24_HOUR} and
- * {@link #DEFAULT_FORMAT_12_HOUR} will be used instead.
+ * <p>If this pattern is set to null, {@link #getFormat24Hour()} will be used
+ * even in 12-hour mode. If both 24-hour and 12-hour formatting patterns
+ * are set to null, the default pattern for the current locale will be used
+ * instead.</p>
+ *
+ * <p><strong>Note:</strong> if styling is not needed, it is highly recommended
+ * you supply a format string generated by
+ * {@link DateFormat#getBestDateTimePattern(java.util.Locale, String)}. This method
+ * takes care of generating a format string adapted to the desired locale.</p>
*
* @param format A date/time formatting pattern as described in {@link DateFormat}
*
* @see #getFormat24Hour()
* @see #is24HourModeEnabled()
- * @see #DEFAULT_FORMAT_24_HOUR
+ * @see DateFormat#getBestDateTimePattern(java.util.Locale, String)
* @see DateFormat
*
* @attr ref android.R.styleable#TextClock_format24Hour
@@ -334,8 +345,7 @@
* returned by {@link #getFormat12Hour()} is used instead.
*
* If either one of the formats is null, the other format is used. If
- * both formats are null, the default values {@link #DEFAULT_FORMAT_12_HOUR}
- * and {@link #DEFAULT_FORMAT_24_HOUR} are used instead.
+ * both formats are null, the default formats for the current locale are used.
*
* @return true if time should be displayed in 24-hour format, false if it
* should be displayed in 12-hour format.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 04c4070..816bb18 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -20,6 +20,7 @@
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
+import android.content.UndoManager;
import android.content.res.ColorStateList;
import android.content.res.CompatibilityInfo;
import android.content.res.Resources;
@@ -291,6 +292,13 @@
private boolean mPreDrawRegistered;
+ // A flag to prevent repeated movements from escaping the enclosing text view. The idea here is
+ // that if a user is holding down a movement key to traverse text, we shouldn't also traverse
+ // the view hierarchy. On the other hand, if the user is using the movement key to traverse views
+ // (i.e. the first movement was to traverse out of this view, or this view was traversed into by
+ // the user holding the movement key down) then we shouldn't prevent the focus from changing.
+ private boolean mPreventDefaultMovement;
+
private TextUtils.TruncateAt mEllipsize;
static class Drawables {
@@ -547,7 +555,6 @@
private InputFilter[] mFilters = NO_FILTERS;
private volatile Locale mCurrentSpellCheckerLocaleCache;
- private final ReentrantLock mCurrentTextServicesLocaleLock = new ReentrantLock();
// It is possible to have a selection even when mEditor is null (programmatically set, like when
// a link is pressed). These highlight-related fields do not go in mEditor.
@@ -1510,6 +1517,47 @@
}
/**
+ * Retrieve the {@link android.content.UndoManager} that is currently associated
+ * with this TextView. By default there is no associated UndoManager, so null
+ * is returned. One can be associated with the TextView through
+ * {@link #setUndoManager(android.content.UndoManager, String)}
+ */
+ public final UndoManager getUndoManager() {
+ return mEditor == null ? null : mEditor.mUndoManager;
+ }
+
+ /**
+ * Associate an {@link android.content.UndoManager} with this TextView. Once
+ * done, all edit operations on the TextView will result in appropriate
+ * {@link android.content.UndoOperation} objects pushed on the given UndoManager's
+ * stack.
+ *
+ * @param undoManager The {@link android.content.UndoManager} to associate with
+ * this TextView, or null to clear any existing association.
+ * @param tag String tag identifying this particular TextView owner in the
+ * UndoManager. This is used to keep the correct association with the
+ * {@link android.content.UndoOwner} of any operations inside of the UndoManager.
+ */
+ public final void setUndoManager(UndoManager undoManager, String tag) {
+ if (undoManager != null) {
+ createEditorIfNeeded();
+ mEditor.mUndoManager = undoManager;
+ mEditor.mUndoOwner = undoManager.getOwner(tag, this);
+ mEditor.mUndoInputFilter = new Editor.UndoInputFilter(mEditor);
+ if (!(mText instanceof Editable)) {
+ setText(mText, BufferType.EDITABLE);
+ }
+
+ setFilters((Editable) mText, mFilters);
+ } else if (mEditor != null) {
+ // XXX need to destroy all associated state.
+ mEditor.mUndoManager = null;
+ mEditor.mUndoOwner = null;
+ mEditor.mUndoInputFilter = null;
+ }
+ }
+
+ /**
* @return the current key listener for this TextView.
* This will frequently be null for non-EditText TextViews.
*
@@ -1673,7 +1721,7 @@
setText(mText);
if (hasPasswordTransformationMethod()) {
- notifyAccessibilityStateChanged();
+ notifyViewAccessibilityStateChangedIfNeeded();
}
}
@@ -4401,16 +4449,30 @@
* and includes mInput in the list if it is an InputFilter.
*/
private void setFilters(Editable e, InputFilter[] filters) {
- if (mEditor != null && mEditor.mKeyListener instanceof InputFilter) {
- InputFilter[] nf = new InputFilter[filters.length + 1];
+ if (mEditor != null) {
+ final boolean undoFilter = mEditor.mUndoInputFilter != null;
+ final boolean keyFilter = mEditor.mKeyListener instanceof InputFilter;
+ int num = 0;
+ if (undoFilter) num++;
+ if (keyFilter) num++;
+ if (num > 0) {
+ InputFilter[] nf = new InputFilter[filters.length + num];
- System.arraycopy(filters, 0, nf, 0, filters.length);
- nf[filters.length] = (InputFilter) mEditor.mKeyListener;
+ System.arraycopy(filters, 0, nf, 0, filters.length);
+ num = 0;
+ if (undoFilter) {
+ nf[filters.length] = mEditor.mUndoInputFilter;
+ num++;
+ }
+ if (keyFilter) {
+ nf[filters.length + num] = (InputFilter) mEditor.mKeyListener;
+ }
- e.setFilters(nf);
- } else {
- e.setFilters(filters);
+ e.setFilters(nf);
+ return;
+ }
}
+ e.setFilters(filters);
}
/**
@@ -5282,7 +5344,6 @@
public boolean onKeyDown(int keyCode, KeyEvent event) {
int which = doKeyDown(keyCode, event, null);
if (which == 0) {
- // Go through default dispatching.
return super.onKeyDown(keyCode, event);
}
@@ -5380,6 +5441,15 @@
return 0;
}
+ // If this is the initial keydown, we don't want to prevent a movement away from this view.
+ // While this shouldn't be necessary because any time we're preventing default movement we
+ // should be restricting the focus to remain within this view, thus we'll also receive
+ // the key up event, occasionally key up events will get dropped and we don't want to
+ // prevent the user from traversing out of this on the next key down.
+ if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
+ mPreventDefaultMovement = false;
+ }
+
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (event.hasNoModifiers()) {
@@ -5488,12 +5558,16 @@
}
}
if (doDown) {
- if (mMovement.onKeyDown(this, (Spannable)mText, keyCode, event))
+ if (mMovement.onKeyDown(this, (Spannable)mText, keyCode, event)) {
+ if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
+ mPreventDefaultMovement = true;
+ }
return 2;
+ }
}
}
- return 0;
+ return mPreventDefaultMovement && !KeyEvent.isModifierKey(keyCode) ? -1 : 0;
}
/**
@@ -5526,6 +5600,10 @@
return super.onKeyUp(keyCode, event);
}
+ if (!KeyEvent.isModifierKey(keyCode)) {
+ mPreventDefaultMovement = false;
+ }
+
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
if (event.hasNoModifiers()) {
@@ -7240,7 +7318,6 @@
*/
protected void onSelectionChanged(int selStart, int selEnd) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
- notifyAccessibilityStateChanged();
}
/**
@@ -7285,27 +7362,42 @@
}
// The spans that are inside or intersect the modified region no longer make sense
- removeIntersectingSpans(start, start + before, SpellCheckSpan.class);
- removeIntersectingSpans(start, start + before, SuggestionSpan.class);
+ removeIntersectingNonAdjacentSpans(start, start + before, SpellCheckSpan.class);
+ removeIntersectingNonAdjacentSpans(start, start + before, SuggestionSpan.class);
}
// Removes all spans that are inside or actually overlap the start..end range
- private <T> void removeIntersectingSpans(int start, int end, Class<T> type) {
+ private <T> void removeIntersectingNonAdjacentSpans(int start, int end, Class<T> type) {
if (!(mText instanceof Editable)) return;
Editable text = (Editable) mText;
T[] spans = text.getSpans(start, end, type);
final int length = spans.length;
for (int i = 0; i < length; i++) {
- final int s = text.getSpanStart(spans[i]);
- final int e = text.getSpanEnd(spans[i]);
- // Spans that are adjacent to the edited region will be handled in
- // updateSpellCheckSpans. Result depends on what will be added (space or text)
- if (e == start || s == end) break;
+ final int spanStart = text.getSpanStart(spans[i]);
+ final int spanEnd = text.getSpanEnd(spans[i]);
+ if (spanEnd == start || spanStart == end) break;
text.removeSpan(spans[i]);
}
}
+ void removeAdjacentSuggestionSpans(final int pos) {
+ if (!(mText instanceof Editable)) return;
+ final Editable text = (Editable) mText;
+
+ final SuggestionSpan[] spans = text.getSpans(pos, pos, SuggestionSpan.class);
+ final int length = spans.length;
+ for (int i = 0; i < length; i++) {
+ final int spanStart = text.getSpanStart(spans[i]);
+ final int spanEnd = text.getSpanEnd(spans[i]);
+ if (spanEnd == pos || spanStart == pos) {
+ if (SpellChecker.haveWordBoundariesChanged(text, pos, pos, spanStart, spanEnd)) {
+ text.removeSpan(spans[i]);
+ }
+ }
+ }
+ }
+
/**
* Not private so it can be called from an inner class without going
* through a thunk.
@@ -7958,16 +8050,13 @@
}
private void updateTextServicesLocaleAsync() {
+ // AsyncTask.execute() uses a serial executor which means we don't have
+ // to lock around updateTextServicesLocaleLocked() to prevent it from
+ // being executed n times in parallel.
AsyncTask.execute(new Runnable() {
@Override
public void run() {
- if (mCurrentTextServicesLocaleLock.tryLock()) {
- try {
- updateTextServicesLocaleLocked();
- } finally {
- mCurrentTextServicesLocaleLock.unlock();
- }
- }
+ updateTextServicesLocaleLocked();
}
});
}
@@ -8056,6 +8145,10 @@
info.setEditable(true);
}
+ if (mEditor != null) {
+ info.setInputType(mEditor.mInputType);
+ }
+
if (!TextUtils.isEmpty(mText)) {
info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
@@ -8088,7 +8181,6 @@
case AccessibilityNodeInfo.ACTION_COPY: {
if (isFocused() && canCopy()) {
if (onTextContextMenuItem(ID_COPY)) {
- notifyAccessibilityStateChanged();
return true;
}
}
@@ -8096,7 +8188,6 @@
case AccessibilityNodeInfo.ACTION_PASTE: {
if (isFocused() && canPaste()) {
if (onTextContextMenuItem(ID_PASTE)) {
- notifyAccessibilityStateChanged();
return true;
}
}
@@ -8104,7 +8195,6 @@
case AccessibilityNodeInfo.ACTION_CUT: {
if (isFocused() && canCut()) {
if (onTextContextMenuItem(ID_CUT)) {
- notifyAccessibilityStateChanged();
return true;
}
}
@@ -8123,7 +8213,6 @@
// No arguments clears the selection.
if (start == end && end == -1) {
Selection.removeSelection((Spannable) text);
- notifyAccessibilityStateChanged();
return true;
}
if (start >= 0 && start <= end && end <= text.length()) {
@@ -8132,7 +8221,6 @@
if (mEditor != null) {
mEditor.startSelectionActionMode();
}
- notifyAccessibilityStateChanged();
return true;
}
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 1d85126..aaa1adaa 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -29,6 +29,7 @@
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -74,6 +75,9 @@
*/
public static final int LENGTH_LONG = 1;
+ /** @hide */
+ public static final int LENGTH_INFINITE = 2;
+
final Context mContext;
final TN mTN;
int mDuration;
@@ -288,6 +292,61 @@
tv.setText(s);
}
+ /** @hide */
+ public static Toast makeBar(Context context, int resId, int duration) {
+ return makeBar(context, context.getResources().getText(resId), duration);
+ }
+
+ /** @hide */
+ public static Toast makeBar(Context context, CharSequence text, int duration) {
+ Toast result = new Toast(context);
+
+ LayoutInflater inflate = (LayoutInflater)
+ context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflate.inflate(com.android.internal.R.layout.toast_bar, null);
+ ((TextView)v.findViewById(android.R.id.message)).setText(text);
+ v.findViewById(android.R.id.button1).setVisibility(View.GONE);
+
+ result.mNextView = v;
+ result.mDuration = duration;
+ result.mTN.mParams.alpha = 0.9f;
+ result.mTN.mParams.windowAnimations = com.android.internal.R.style.Animation_ToastBar;
+
+ return result;
+ }
+
+ /** @hide */
+ public Toast setAction(int resId, Runnable action) {
+ return setAction(mContext.getResources().getText(resId), action);
+ }
+
+ /** @hide */
+ public Toast setAction(CharSequence actionText, final Runnable action) {
+ if (mNextView != null) {
+ TextView text1 = (TextView)mNextView.findViewById(android.R.id.text1);
+ View button1 = mNextView.findViewById(android.R.id.button1);
+ if (text1 != null && button1 != null) {
+ text1.setText(actionText);
+ button1.setVisibility(View.VISIBLE);
+ button1.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (action != null) {
+ action.run();
+ }
+ }});
+ return setInteractive(true);
+ }
+ }
+ throw new RuntimeException("This Toast was not created with Toast.makeBar()");
+ }
+
+ /** @hide */
+ public Toast setInteractive(boolean interactive) {
+ mTN.setInteractive(interactive);
+ return this;
+ }
+
// =======================================================================================
// All the gunk below is the interaction with the Notification Service, which handles
// the proper ordering of these system-wide.
@@ -340,13 +399,20 @@
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
- params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
+ setInteractive(false);
+ }
+
+ private void setInteractive(boolean interactive) {
+ mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | (interactive
+ ? (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH)
+ : WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
/**
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index acbb2b1..e092fff 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -1210,6 +1210,10 @@
mActionView.setIcon(icon);
}
+ public boolean hasIcon() {
+ return mActionView.hasIcon();
+ }
+
@Override
public void setLogo(int resId) {
mActionView.setLogo(resId);
@@ -1220,6 +1224,10 @@
mActionView.setLogo(logo);
}
+ public boolean hasLogo() {
+ return mActionView.hasLogo();
+ }
+
public void setDefaultDisplayHomeAsUpEnabled(boolean enable) {
if (!mDisplayHomeAsUpSet) {
setDisplayHomeAsUpEnabled(enable);
diff --git a/core/java/com/android/internal/app/AlertActivity.java b/core/java/com/android/internal/app/AlertActivity.java
index 7251256..7456def 100644
--- a/core/java/com/android/internal/app/AlertActivity.java
+++ b/core/java/com/android/internal/app/AlertActivity.java
@@ -36,18 +36,18 @@
* @see #mAlertParams
*/
protected AlertController mAlert;
-
+
/**
* The parameters for the alert.
*/
protected AlertController.AlertParams mAlertParams;
-
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+
mAlert = new AlertController(this, this, getWindow());
- mAlertParams = new AlertController.AlertParams(this);
+ mAlertParams = new AlertController.AlertParams(this);
}
public void cancel() {
@@ -65,7 +65,7 @@
/**
* Sets up the alert, including applying the parameters to the alert model,
* and installing the alert's content.
- *
+ *
* @see #mAlert
* @see #mAlertParams
*/
@@ -73,7 +73,7 @@
mAlertParams.apply(mAlert);
mAlert.installContent();
}
-
+
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (mAlert.onKeyDown(keyCode, event)) return true;
@@ -85,6 +85,4 @@
if (mAlert.onKeyUp(keyCode, event)) return true;
return super.onKeyUp(keyCode, event);
}
-
-
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 823e19f..525517c 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -71,6 +71,7 @@
void noteWifiMulticastEnabledFromSource(in WorkSource ws);
void noteWifiMulticastDisabledFromSource(in WorkSource ws);
void noteNetworkInterfaceType(String iface, int type);
+ void noteNetworkStatsEnabled();
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt);
long getAwakeTimeBattery();
long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index c22cd26..aa94728 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -16,6 +16,7 @@
package com.android.internal.app;
+import android.os.AsyncTask;
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
@@ -326,6 +327,17 @@
// Look through the resolved filter to determine which part
// of it matched the original Intent.
+ Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
+ if (pIt != null) {
+ String ssp = data.getSchemeSpecificPart();
+ while (ssp != null && pIt.hasNext()) {
+ PatternMatcher p = pIt.next();
+ if (p.match(ssp)) {
+ filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
+ break;
+ }
+ }
+ }
Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
if (aIt != null) {
while (aIt.hasNext()) {
@@ -338,7 +350,7 @@
}
}
}
- Iterator<PatternMatcher> pIt = ri.filter.pathsIterator();
+ pIt = ri.filter.pathsIterator();
if (pIt != null) {
String path = data.getPath();
while (path != null && pIt.hasNext()) {
@@ -621,9 +633,11 @@
view = mInflater.inflate(
com.android.internal.R.layout.resolve_list_item, parent, false);
+ final ViewHolder holder = new ViewHolder(view);
+ view.setTag(holder);
+
// Fix the icon size even if we have different sized resources
- ImageView icon = (ImageView)view.findViewById(R.id.icon);
- ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) icon.getLayoutParams();
+ ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
lp.width = lp.height = mIconSize;
} else {
view = convertView;
@@ -633,20 +647,30 @@
}
private final void bindView(View view, DisplayResolveInfo info) {
- TextView text = (TextView)view.findViewById(com.android.internal.R.id.text1);
- TextView text2 = (TextView)view.findViewById(com.android.internal.R.id.text2);
- ImageView icon = (ImageView)view.findViewById(R.id.icon);
- text.setText(info.displayLabel);
+ final ViewHolder holder = (ViewHolder) view.getTag();
+ holder.text.setText(info.displayLabel);
if (mShowExtended) {
- text2.setVisibility(View.VISIBLE);
- text2.setText(info.extendedInfo);
+ holder.text2.setVisibility(View.VISIBLE);
+ holder.text2.setText(info.extendedInfo);
} else {
- text2.setVisibility(View.GONE);
+ holder.text2.setVisibility(View.GONE);
}
if (info.displayIcon == null) {
- info.displayIcon = loadIconForResolveInfo(info.ri);
+ new LoadIconTask().execute(info);
}
- icon.setImageDrawable(info.displayIcon);
+ holder.icon.setImageDrawable(info.displayIcon);
+ }
+ }
+
+ static class ViewHolder {
+ public TextView text;
+ public TextView text2;
+ public ImageView icon;
+
+ public ViewHolder(View view) {
+ text = (TextView) view.findViewById(com.android.internal.R.id.text1);
+ text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
+ icon = (ImageView) view.findViewById(R.id.icon);
}
}
@@ -660,5 +684,21 @@
}
}
+
+ class LoadIconTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
+ @Override
+ protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
+ final DisplayResolveInfo info = params[0];
+ if (info.displayIcon == null) {
+ info.displayIcon = loadIconForResolveInfo(info.ri);
+ }
+ return info;
+ }
+
+ @Override
+ protected void onPostExecute(DisplayResolveInfo info) {
+ mAdapter.notifyDataSetChanged();
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/RestrictionsPinActivity.java b/core/java/com/android/internal/app/RestrictionsPinActivity.java
new file mode 100644
index 0000000..57436f7
--- /dev/null
+++ b/core/java/com/android/internal/app/RestrictionsPinActivity.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.R;
+
+/**
+ * This activity is launched by Settings and other apps to either create a new PIN or
+ * challenge for an existing PIN. The PIN is maintained by UserManager.
+ */
+public class RestrictionsPinActivity extends AlertActivity
+ implements DialogInterface.OnClickListener, TextWatcher, OnEditorActionListener {
+
+ private UserManager mUserManager;
+
+ private EditText mPin1Text;
+ private EditText mPin2Text;
+ private TextView mPinErrorMessage;
+ private TextView mPinMessage;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ AlertController.AlertParams ap = mAlertParams;
+ ap.mTitle = getString(R.string.restr_pin_enter_pin);
+ ap.mPositiveButtonText = getString(R.string.ok);
+ ap.mNegativeButtonText = getString(R.string.cancel);
+ ap.mPositiveButtonListener = this;
+ ap.mNegativeButtonListener = this;
+ LayoutInflater inflater =
+ (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ ap.mView = inflater.inflate(R.layout.pin_challenge, null);
+
+ mPinMessage = (TextView) ap.mView.findViewById(R.id.pin_message);
+ mPin1Text = (EditText) ap.mView.findViewById(R.id.pin1_text);
+ mPin2Text = (EditText) ap.mView.findViewById(R.id.pin2_text);
+ mPinErrorMessage = (TextView) ap.mView.findViewById(R.id.pin_error_message);
+ mPin1Text.addTextChangedListener(this);
+ mPin2Text.addTextChangedListener(this);
+
+ mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+
+ setupAlert();
+ }
+
+ protected boolean verifyingPin() {
+ return true;
+ }
+
+ public void onResume() {
+ super.onResume();
+
+ setPositiveButtonState(false);
+ boolean hasPin = mUserManager.hasRestrictionsPin();
+ if (verifyingPin()) {
+ if (hasPin) {
+ mPinMessage.setVisibility(View.GONE);
+ mPinErrorMessage.setVisibility(View.GONE);
+ mPin2Text.setVisibility(View.GONE);
+ mPin1Text.setOnEditorActionListener(this);
+ updatePinTimer(-1);
+ } else {
+ setResult(RESULT_OK);
+ finish();
+ }
+ } else if (hasPin) {
+ // Shouldn't really be in this state, exit
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+
+ private void setPositiveButtonState(boolean enabled) {
+ mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(enabled);
+ }
+
+ private void updatePinTimer(int pinTimerMs) {
+ if (pinTimerMs < 0) {
+ pinTimerMs = mUserManager.checkRestrictionsPin(null);
+ }
+ if (pinTimerMs >= 200) {
+ final int seconds = (pinTimerMs + 200) / 1000;
+ final String formatString = getResources().getQuantityString(
+ R.plurals.restr_pin_countdown,
+ seconds);
+ mPinErrorMessage.setText(String.format(formatString, seconds));
+ mPinErrorMessage.setVisibility(View.VISIBLE);
+ mPin1Text.setEnabled(false);
+ mPin1Text.setText("");
+ setPositiveButtonState(false);
+ mPin1Text.postDelayed(mCountdownRunnable, Math.min(1000, pinTimerMs));
+ } else {
+ mPinErrorMessage.setVisibility(View.INVISIBLE);
+ mPin1Text.setEnabled(true);
+ mPin1Text.setText("");
+ }
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ setResult(RESULT_CANCELED);
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ performPositiveButtonAction();
+ } else if (which == AlertDialog.BUTTON_NEGATIVE) {
+ finish();
+ }
+ }
+
+ private void performPositiveButtonAction() {
+ if (verifyingPin()) {
+ int result = mUserManager.checkRestrictionsPin(mPin1Text.getText().toString());
+ if (result == UserManager.PIN_VERIFICATION_SUCCESS) {
+ setResult(RESULT_OK);
+ finish();
+ } else if (result >= 0) {
+ updatePinTimer(result);
+ }
+ } else {
+ if (mUserManager.changeRestrictionsPin(mPin1Text.getText().toString())) {
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ CharSequence pin1 = mPin1Text.getText();
+ if (!verifyingPin()) {
+ CharSequence pin2 = mPin2Text.getText();
+ boolean match = pin1 != null && pin2 != null && pin1.length() >= 4
+ && pin1.toString().equals(pin2.toString());
+ setPositiveButtonState(match);
+ mPinErrorMessage.setVisibility(match ? View.INVISIBLE : View.VISIBLE);
+ } else {
+ setPositiveButtonState(pin1 != null && pin1.length() >= 4);
+ }
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ performPositiveButtonAction();
+ return true;
+ }
+
+ private Runnable mCountdownRunnable = new Runnable() {
+ public void run() {
+ updatePinTimer(-1);
+ }
+ };
+}
diff --git a/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java b/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
new file mode 100644
index 0000000..35f2967
--- /dev/null
+++ b/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+/**
+ * This activity is launched by Settings and other apps to either create a new PIN or
+ * challenge for an existing PIN. The PIN is maintained by UserManager.
+ */
+public class RestrictionsPinSetupActivity extends RestrictionsPinActivity {
+
+ protected boolean verifyingPin() {
+ return false;
+ }
+}
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 424c19b..ab871fb 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -25,6 +25,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
+import com.android.internal.os.BackgroundThread;
import java.util.HashSet;
@@ -37,10 +38,6 @@
static final IntentFilter sNonDataFilt = new IntentFilter();
static final IntentFilter sExternalFilt = new IntentFilter();
- static final Object sLock = new Object();
- static HandlerThread sBackgroundThread;
- static Handler sBackgroundHandler;
-
static {
sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -79,15 +76,7 @@
}
mRegisteredContext = context;
if (thread == null) {
- synchronized (sLock) {
- if (sBackgroundThread == null) {
- sBackgroundThread = new HandlerThread("PackageMonitor",
- android.os.Process.THREAD_PRIORITY_BACKGROUND);
- sBackgroundThread.start();
- sBackgroundHandler = new Handler(sBackgroundThread.getLooper());
- }
- mRegisteredHandler = sBackgroundHandler;
- }
+ mRegisteredHandler = BackgroundThread.getHandler();
} else {
mRegisteredHandler = new Handler(thread);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 4e21324..78389c5 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -432,6 +432,17 @@
}
}
+ public static CharSequence getImeAndSubtypeDisplayName(Context context, InputMethodInfo imi,
+ InputMethodSubtype subtype) {
+ final CharSequence imiLabel = imi.loadLabel(context.getPackageManager());
+ return subtype != null
+ ? TextUtils.concat(subtype.getDisplayName(context,
+ imi.getPackageName(), imi.getServiceInfo().applicationInfo),
+ (TextUtils.isEmpty(imiLabel) ?
+ "" : " - " + imiLabel))
+ : imiLabel;
+ }
+
/**
* Utility class for putting and getting settings for InputMethod
* TODO: Move all putters and getters of settings to this class.
diff --git a/core/java/com/android/internal/logging/AndroidHandler.java b/core/java/com/android/internal/logging/AndroidHandler.java
index 12f6a4f..f55a31f 100644
--- a/core/java/com/android/internal/logging/AndroidHandler.java
+++ b/core/java/com/android/internal/logging/AndroidHandler.java
@@ -17,6 +17,7 @@
package com.android.internal.logging;
import android.util.Log;
+import com.android.internal.util.FastPrintWriter;
import dalvik.system.DalvikLogging;
import dalvik.system.DalvikLogHandler;
@@ -91,7 +92,7 @@
Throwable thrown = r.getThrown();
if (thrown != null) {
StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
+ PrintWriter pw = new FastPrintWriter(sw, false, 256);
sw.write(r.getMessage());
sw.write("\n");
thrown.printStackTrace(pw);
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
new file mode 100644
index 0000000..d6f7b20
--- /dev/null
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+/**
+ * Shared singleton background thread for each process.
+ */
+public final class BackgroundThread extends HandlerThread {
+ private static BackgroundThread sInstance;
+ private static Handler sHandler;
+
+ private BackgroundThread() {
+ super("android.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new BackgroundThread();
+ sInstance.start();
+ sHandler = new Handler(sInstance.getLooper());
+ }
+ }
+
+ public static BackgroundThread get() {
+ synchronized (BackgroundThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (BackgroundThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6375dbe..e0a154c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,7 +16,6 @@
package com.android.internal.os;
-import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
import android.bluetooth.BluetoothDevice;
@@ -46,7 +45,9 @@
import android.util.SparseArray;
import android.util.TimeUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.JournaledFile;
import com.google.android.collect.Sets;
@@ -83,7 +84,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 64 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 66 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -231,6 +232,9 @@
final StopwatchTimer[] mPhoneDataConnectionsTimer =
new StopwatchTimer[NUM_DATA_CONNECTION_TYPES];
+ final LongSamplingCounter[] mNetworkActivityCounters =
+ new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+
boolean mWifiOn;
StopwatchTimer mWifiOnTimer;
int mWifiOnUid = -1;
@@ -275,12 +279,6 @@
long mLastWriteTime = 0; // Milliseconds
- // Mobile data transferred while on battery
- private long[] mMobileDataTx = new long[4];
- private long[] mMobileDataRx = new long[4];
- private long[] mTotalDataTx = new long[4];
- private long[] mTotalDataRx = new long[4];
-
private long mRadioDataUptime;
private long mRadioDataStart;
@@ -304,7 +302,8 @@
private static int sKernelWakelockUpdateVersion = 0;
private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
- Process.PROC_TAB_TERM|Process.PROC_OUT_STRING, // 0: name
+ Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name
+ Process.PROC_QUOTES,
Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 1: count
Process.PROC_TAB_TERM,
Process.PROC_TAB_TERM,
@@ -337,9 +336,12 @@
private HashMap<String, Integer> mUidCache = new HashMap<String, Integer>();
private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
+ private NetworkStats mLastSnapshot;
- /** Network ifaces that {@link ConnectivityManager} has claimed as mobile. */
+ @GuardedBy("this")
private HashSet<String> mMobileIfaces = Sets.newHashSet();
+ @GuardedBy("this")
+ private HashSet<String> mWifiIfaces = Sets.newHashSet();
// For debugging
public BatteryStatsImpl() {
@@ -466,7 +468,6 @@
}
public static class SamplingCounter extends Counter {
-
SamplingCounter(ArrayList<Unpluggable> unpluggables, Parcel in) {
super(unpluggables, in);
}
@@ -480,6 +481,93 @@
}
}
+ public static class LongSamplingCounter implements Unpluggable {
+ final ArrayList<Unpluggable> mUnpluggables;
+ long mCount;
+ long mLoadedCount;
+ long mLastCount;
+ long mUnpluggedCount;
+ long mPluggedCount;
+
+ LongSamplingCounter(ArrayList<Unpluggable> unpluggables, Parcel in) {
+ mUnpluggables = unpluggables;
+ mPluggedCount = in.readLong();
+ mCount = mPluggedCount;
+ mLoadedCount = in.readLong();
+ mLastCount = 0;
+ mUnpluggedCount = in.readLong();
+ unpluggables.add(this);
+ }
+
+ LongSamplingCounter(ArrayList<Unpluggable> unpluggables) {
+ mUnpluggables = unpluggables;
+ unpluggables.add(this);
+ }
+
+ public void writeToParcel(Parcel out) {
+ out.writeLong(mCount);
+ out.writeLong(mLoadedCount);
+ out.writeLong(mUnpluggedCount);
+ }
+
+ @Override
+ public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ mUnpluggedCount = mPluggedCount;
+ mCount = mPluggedCount;
+ }
+
+ @Override
+ public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ mPluggedCount = mCount;
+ }
+
+ public long getCountLocked(int which) {
+ long val;
+ if (which == STATS_LAST) {
+ val = mLastCount;
+ } else {
+ val = mCount;
+ if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedCount;
+ } else if (which != STATS_SINCE_CHARGED) {
+ val -= mLoadedCount;
+ }
+ }
+
+ return val;
+ }
+
+ void addCountLocked(long count) {
+ mCount += count;
+ }
+
+ /**
+ * Clear state of this counter.
+ */
+ void reset(boolean detachIfReset) {
+ mCount = 0;
+ mLoadedCount = mLastCount = mPluggedCount = mUnpluggedCount = 0;
+ if (detachIfReset) {
+ detach();
+ }
+ }
+
+ void detach() {
+ mUnpluggables.remove(this);
+ }
+
+ void writeSummaryFromParcelLocked(Parcel out) {
+ out.writeLong(mCount);
+ }
+
+ void readSummaryFromParcelLocked(Parcel in) {
+ mLoadedCount = in.readLong();
+ mCount = mLoadedCount;
+ mLastCount = 0;
+ mUnpluggedCount = mPluggedCount = mLoadedCount;
+ }
+ }
+
/**
* State for keeping track of timing information.
*/
@@ -525,7 +613,6 @@
* Constructs from a parcel.
* @param type
* @param unpluggables
- * @param powerType
* @param in
*/
Timer(int type, ArrayList<Unpluggable> unpluggables, Parcel in) {
@@ -659,7 +746,7 @@
}
public void logState(Printer pw, String prefix) {
- pw.println(prefix + " mCount=" + mCount
+ pw.println(prefix + "mCount=" + mCount
+ " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
+ " mUnpluggedCount=" + mUnpluggedCount);
pw.println(prefix + "mTotalTime=" + mTotalTime
@@ -1044,7 +1131,7 @@
public void logState(Printer pw, String prefix) {
super.logState(pw, prefix);
- pw.println(prefix + "mNesting=" + mNesting + "mUpdateTime=" + mUpdateTime
+ pw.println(prefix + "mNesting=" + mNesting + " mUpdateTime=" + mUpdateTime
+ " mAcquireTime=" + mAcquireTime);
}
@@ -1316,15 +1403,6 @@
return kwlt;
}
- private void doDataPlug(long[] dataTransfer, long currentBytes) {
- dataTransfer[STATS_LAST] = dataTransfer[STATS_SINCE_UNPLUGGED];
- dataTransfer[STATS_SINCE_UNPLUGGED] = -1;
- }
-
- private void doDataUnplug(long[] dataTransfer, long currentBytes) {
- dataTransfer[STATS_SINCE_UNPLUGGED] = currentBytes;
- }
-
/**
* Radio uptime in microseconds when transferring data. This value is very approximate.
* @return
@@ -1571,34 +1649,10 @@
}
public void doUnplugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
- NetworkStats.Entry entry = null;
-
- // Track UID data usage
- final NetworkStats uidStats = getNetworkStatsDetailGroupedByUid();
- final int size = uidStats.size();
- for (int i = 0; i < size; i++) {
- entry = uidStats.getValues(i, entry);
-
- final Uid u = getUidStatsLocked(entry.uid);
- u.mStartedTcpBytesReceived = entry.rxBytes;
- u.mStartedTcpBytesSent = entry.txBytes;
- u.mTcpBytesReceivedAtLastUnplug = u.mCurrentTcpBytesReceived;
- u.mTcpBytesSentAtLastUnplug = u.mCurrentTcpBytesSent;
- }
-
for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
mUnpluggables.get(i).unplug(elapsedRealtime, batteryUptime, batteryRealtime);
}
- // Track both mobile and total overall data
- final NetworkStats ifaceStats = getNetworkStatsSummary();
- entry = ifaceStats.getTotal(entry, mMobileIfaces);
- doDataUnplug(mMobileDataRx, entry.rxBytes);
- doDataUnplug(mMobileDataTx, entry.txBytes);
- entry = ifaceStats.getTotal(entry);
- doDataUnplug(mTotalDataRx, entry.rxBytes);
- doDataUnplug(mTotalDataTx, entry.txBytes);
-
// Track radio awake time
mRadioDataStart = getCurrentRadioDataUptime();
mRadioDataUptime = 0;
@@ -1609,32 +1663,10 @@
}
public void doPlugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
- NetworkStats.Entry entry = null;
-
- for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
- Uid u = mUidStats.valueAt(iu);
- if (u.mStartedTcpBytesReceived >= 0) {
- u.mCurrentTcpBytesReceived = u.computeCurrentTcpBytesReceived();
- u.mStartedTcpBytesReceived = -1;
- }
- if (u.mStartedTcpBytesSent >= 0) {
- u.mCurrentTcpBytesSent = u.computeCurrentTcpBytesSent();
- u.mStartedTcpBytesSent = -1;
- }
- }
for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
mUnpluggables.get(i).plug(elapsedRealtime, batteryUptime, batteryRealtime);
}
- // Track both mobile and total overall data
- final NetworkStats ifaceStats = getNetworkStatsSummary();
- entry = ifaceStats.getTotal(entry, mMobileIfaces);
- doDataPlug(mMobileDataRx, entry.rxBytes);
- doDataPlug(mMobileDataTx, entry.txBytes);
- entry = ifaceStats.getTotal(entry);
- doDataPlug(mTotalDataRx, entry.rxBytes);
- doDataPlug(mTotalDataTx, entry.txBytes);
-
// Track radio awake time
mRadioDataUptime = getRadioDataUptime();
mRadioDataStart = -1;
@@ -2234,6 +2266,14 @@
getUidStatsLocked(uid).noteVideoTurnedOffLocked();
}
+ public void noteActivityResumedLocked(int uid) {
+ getUidStatsLocked(uid).noteActivityResumedLocked();
+ }
+
+ public void noteActivityPausedLocked(int uid) {
+ getUidStatsLocked(uid).noteActivityPausedLocked();
+ }
+
public void noteVibratorOnLocked(int uid, long durationMillis) {
getUidStatsLocked(uid).noteVibratorOnLocked(durationMillis);
}
@@ -2433,6 +2473,18 @@
} else {
mMobileIfaces.remove(iface);
}
+ if (ConnectivityManager.isNetworkTypeWifi(networkType)) {
+ mWifiIfaces.add(iface);
+ } else {
+ mWifiIfaces.remove(iface);
+ }
+ }
+
+ public void noteNetworkStatsEnabledLocked() {
+ // During device boot, qtaguid isn't enabled until after the inital
+ // loading of battery stats. Now that they're enabled, take our initial
+ // snapshot for future delta calculation.
+ updateNetworkActivityLocked();
}
@Override public long getScreenOnTime(long batteryRealtime, int which) {
@@ -2491,6 +2543,15 @@
return mBluetoothOnTimer.getTotalTimeLocked(batteryRealtime, which);
}
+ @Override
+ public long getNetworkActivityCount(int type, int which) {
+ if (type >= 0 && type < mNetworkActivityCounters.length) {
+ return mNetworkActivityCounters[type].getCountLocked(which);
+ } else {
+ return 0;
+ }
+ }
+
@Override public boolean getIsOnBattery() {
return mOnBattery;
}
@@ -2505,17 +2566,6 @@
public final class Uid extends BatteryStats.Uid {
final int mUid;
- long mLoadedTcpBytesReceived;
- long mLoadedTcpBytesSent;
- long mCurrentTcpBytesReceived;
- long mCurrentTcpBytesSent;
- long mTcpBytesReceivedAtLastUnplug;
- long mTcpBytesSentAtLastUnplug;
-
- // These are not saved/restored when parcelling, since we want
- // to return from the parcel with a snapshot of the state.
- long mStartedTcpBytesReceived = -1;
- long mStartedTcpBytesSent = -1;
boolean mWifiRunning;
StopwatchTimer mWifiRunningTimer;
@@ -2535,10 +2585,14 @@
boolean mVideoTurnedOn;
StopwatchTimer mVideoTurnedOnTimer;
+ StopwatchTimer mForegroundActivityTimer;
+
BatchTimer mVibratorOnTimer;
Counter[] mUserActivityCounters;
+ LongSamplingCounter[] mNetworkActivityCounters;
+
/**
* The statistics we have collected for this uid's wake locks.
*/
@@ -2602,43 +2656,6 @@
}
@Override
- public long getTcpBytesReceived(int which) {
- if (which == STATS_LAST) {
- return mLoadedTcpBytesReceived;
- } else {
- long current = computeCurrentTcpBytesReceived();
- if (which == STATS_SINCE_UNPLUGGED) {
- current -= mTcpBytesReceivedAtLastUnplug;
- } else if (which == STATS_SINCE_CHARGED) {
- current += mLoadedTcpBytesReceived;
- }
- return current;
- }
- }
-
- public long computeCurrentTcpBytesReceived() {
- final long uidRxBytes = getNetworkStatsDetailGroupedByUid().getTotal(
- null, mUid).rxBytes;
- return mCurrentTcpBytesReceived + (mStartedTcpBytesReceived >= 0
- ? (uidRxBytes - mStartedTcpBytesReceived) : 0);
- }
-
- @Override
- public long getTcpBytesSent(int which) {
- if (which == STATS_LAST) {
- return mLoadedTcpBytesSent;
- } else {
- long current = computeCurrentTcpBytesSent();
- if (which == STATS_SINCE_UNPLUGGED) {
- current -= mTcpBytesSentAtLastUnplug;
- } else if (which == STATS_SINCE_CHARGED) {
- current += mLoadedTcpBytesSent;
- }
- return current;
- }
- }
-
- @Override
public void noteWifiRunningLocked() {
if (!mWifiRunning) {
mWifiRunning = true;
@@ -2770,6 +2787,27 @@
}
}
+ public StopwatchTimer createForegroundActivityTimerLocked() {
+ if (mForegroundActivityTimer == null) {
+ mForegroundActivityTimer = new StopwatchTimer(
+ Uid.this, FOREGROUND_ACTIVITY, null, mUnpluggables);
+ }
+ return mForegroundActivityTimer;
+ }
+
+ @Override
+ public void noteActivityResumedLocked() {
+ // We always start, since we want multiple foreground PIDs to nest
+ createForegroundActivityTimerLocked().startRunningLocked(BatteryStatsImpl.this);
+ }
+
+ @Override
+ public void noteActivityPausedLocked() {
+ if (mForegroundActivityTimer != null) {
+ mForegroundActivityTimer.stopRunningLocked(BatteryStatsImpl.this);
+ }
+ }
+
public BatchTimer createVibratorOnTimerLocked() {
if (mVibratorOnTimer == null) {
mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
@@ -2838,6 +2876,11 @@
}
@Override
+ public Timer getForegroundActivityTimer() {
+ return mForegroundActivityTimer;
+ }
+
+ @Override
public Timer getVibratorOnTimer() {
return mVibratorOnTimer;
}
@@ -2875,11 +2918,38 @@
}
}
- public long computeCurrentTcpBytesSent() {
- final long uidTxBytes = getNetworkStatsDetailGroupedByUid().getTotal(
- null, mUid).txBytes;
- return mCurrentTcpBytesSent + (mStartedTcpBytesSent >= 0
- ? (uidTxBytes - mStartedTcpBytesSent) : 0);
+ void noteNetworkActivityLocked(int type, long delta) {
+ if (mNetworkActivityCounters == null) {
+ initNetworkActivityLocked();
+ }
+ if (type >= 0 && type < NUM_NETWORK_ACTIVITY_TYPES) {
+ mNetworkActivityCounters[type].addCountLocked(delta);
+ } else {
+ Slog.w(TAG, "Unknown network activity type " + type + " was specified.",
+ new Throwable());
+ }
+ }
+
+ @Override
+ public boolean hasNetworkActivity() {
+ return mNetworkActivityCounters != null;
+ }
+
+ @Override
+ public long getNetworkActivityCount(int type, int which) {
+ if (mNetworkActivityCounters != null && type >= 0
+ && type < mNetworkActivityCounters.length) {
+ return mNetworkActivityCounters[type].getCountLocked(which);
+ } else {
+ return 0;
+ }
+ }
+
+ void initNetworkActivityLocked() {
+ mNetworkActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables);
+ }
}
/**
@@ -2913,6 +2983,9 @@
active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mVideoTurnedOn;
}
+ if (mForegroundActivityTimer != null) {
+ active |= !mForegroundActivityTimer.reset(BatteryStatsImpl.this, false);
+ }
if (mVibratorOnTimer != null) {
if (mVibratorOnTimer.reset(BatteryStatsImpl.this, false)) {
mVibratorOnTimer.detach();
@@ -2922,15 +2995,18 @@
}
}
- mLoadedTcpBytesReceived = mLoadedTcpBytesSent = 0;
- mCurrentTcpBytesReceived = mCurrentTcpBytesSent = 0;
-
if (mUserActivityCounters != null) {
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
mUserActivityCounters[i].reset(false);
}
}
+ if (mNetworkActivityCounters != null) {
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i].reset(false);
+ }
+ }
+
if (mWakelockStats.size() > 0) {
Iterator<Map.Entry<String, Wakelock>> it = mWakelockStats.entrySet().iterator();
while (it.hasNext()) {
@@ -3012,11 +3088,20 @@
mVideoTurnedOnTimer.detach();
mVideoTurnedOnTimer = null;
}
+ if (mForegroundActivityTimer != null) {
+ mForegroundActivityTimer.detach();
+ mForegroundActivityTimer = null;
+ }
if (mUserActivityCounters != null) {
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
mUserActivityCounters[i].detach();
}
}
+ if (mNetworkActivityCounters != null) {
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i].detach();
+ }
+ }
}
return !active;
@@ -3051,12 +3136,6 @@
pkg.writeToParcelLocked(out);
}
- out.writeLong(mLoadedTcpBytesReceived);
- out.writeLong(mLoadedTcpBytesSent);
- out.writeLong(computeCurrentTcpBytesReceived());
- out.writeLong(computeCurrentTcpBytesSent());
- out.writeLong(mTcpBytesReceivedAtLastUnplug);
- out.writeLong(mTcpBytesSentAtLastUnplug);
if (mWifiRunningTimer != null) {
out.writeInt(1);
mWifiRunningTimer.writeToParcel(out, batteryRealtime);
@@ -3093,6 +3172,12 @@
} else {
out.writeInt(0);
}
+ if (mForegroundActivityTimer != null) {
+ out.writeInt(1);
+ mForegroundActivityTimer.writeToParcel(out, batteryRealtime);
+ } else {
+ out.writeInt(0);
+ }
if (mVibratorOnTimer != null) {
out.writeInt(1);
mVibratorOnTimer.writeToParcel(out, batteryRealtime);
@@ -3107,6 +3192,14 @@
} else {
out.writeInt(0);
}
+ if (mNetworkActivityCounters != null) {
+ out.writeInt(1);
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i].writeToParcel(out);
+ }
+ } else {
+ out.writeInt(0);
+ }
}
void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
@@ -3149,12 +3242,6 @@
mPackageStats.put(packageName, pkg);
}
- mLoadedTcpBytesReceived = in.readLong();
- mLoadedTcpBytesSent = in.readLong();
- mCurrentTcpBytesReceived = in.readLong();
- mCurrentTcpBytesSent = in.readLong();
- mTcpBytesReceivedAtLastUnplug = in.readLong();
- mTcpBytesSentAtLastUnplug = in.readLong();
mWifiRunning = false;
if (in.readInt() != 0) {
mWifiRunningTimer = new StopwatchTimer(Uid.this, WIFI_RUNNING,
@@ -3198,6 +3285,12 @@
mVideoTurnedOnTimer = null;
}
if (in.readInt() != 0) {
+ mForegroundActivityTimer = new StopwatchTimer(
+ Uid.this, FOREGROUND_ACTIVITY, null, mUnpluggables, in);
+ } else {
+ mForegroundActivityTimer = null;
+ }
+ if (in.readInt() != 0) {
mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal, in);
} else {
@@ -3211,6 +3304,14 @@
} else {
mUserActivityCounters = null;
}
+ if (in.readInt() != 0) {
+ mNetworkActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables, in);
+ }
+ } else {
+ mNetworkActivityCounters = null;
+ }
}
/**
@@ -3366,16 +3467,16 @@
long mSystemTime;
/**
- * Number of times the process has been started.
- */
- int mStarts;
-
- /**
* Amount of time the process was running in the foreground.
*/
long mForegroundTime;
/**
+ * Number of times the process has been started.
+ */
+ int mStarts;
+
+ /**
* The amount of user time loaded from a previous save.
*/
long mLoadedUserTime;
@@ -3386,16 +3487,16 @@
long mLoadedSystemTime;
/**
- * The number of times the process has started from a previous save.
- */
- int mLoadedStarts;
-
- /**
* The amount of foreground time loaded from a previous save.
*/
long mLoadedForegroundTime;
/**
+ * The number of times the process has started from a previous save.
+ */
+ int mLoadedStarts;
+
+ /**
* The amount of user time loaded from the previous run.
*/
long mLastUserTime;
@@ -3406,16 +3507,16 @@
long mLastSystemTime;
/**
- * The number of times the process has started from the previous run.
- */
- int mLastStarts;
-
- /**
* The amount of foreground time loaded from the previous run
*/
long mLastForegroundTime;
/**
+ * The number of times the process has started from the previous run.
+ */
+ int mLastStarts;
+
+ /**
* The amount of user time when last unplugged.
*/
long mUnpluggedUserTime;
@@ -3426,15 +3527,15 @@
long mUnpluggedSystemTime;
/**
- * The number of times the process has started before unplugged.
- */
- int mUnpluggedStarts;
-
- /**
* The amount of foreground time since unplugged.
*/
long mUnpluggedForegroundTime;
+ /**
+ * The number of times the process has started before unplugged.
+ */
+ int mUnpluggedStarts;
+
SamplingCounter[] mSpeedBins;
ArrayList<ExcessivePower> mExcessivePower;
@@ -3447,8 +3548,8 @@
public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
mUnpluggedUserTime = mUserTime;
mUnpluggedSystemTime = mSystemTime;
- mUnpluggedStarts = mStarts;
mUnpluggedForegroundTime = mForegroundTime;
+ mUnpluggedStarts = mStarts;
}
public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
@@ -4285,6 +4386,9 @@
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i, null, mUnpluggables);
}
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables);
+ }
mWifiOnTimer = new StopwatchTimer(null, -3, null, mUnpluggables);
mGlobalWifiRunningTimer = new StopwatchTimer(null, -4, null, mUnpluggables);
mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mUnpluggables);
@@ -4358,12 +4462,13 @@
Slog.w(TAG, "New history ends before old history!");
} else if (!out.same(mHistoryReadTmp)) {
long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
- PrintWriter pw = new PrintWriter(new LogWriter(android.util.Log.WARN, TAG));
+ PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG));
pw.println("Histories differ!");
pw.println("Old history:");
(new HistoryPrinter()).printNextItem(pw, out, now);
pw.println("New history:");
(new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now);
+ pw.flush();
}
}
return true;
@@ -4460,6 +4565,9 @@
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].reset(this, false);
}
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i].reset(false);
+ }
mWifiOnTimer.reset(this, false);
mGlobalWifiRunningTimer.reset(this, false);
mBluetoothOnTimer.reset(this, false);
@@ -4535,6 +4643,7 @@
mDischargeStartLevel = level;
}
updateKernelWakelocksLocked();
+ updateNetworkActivityLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
@@ -4557,6 +4666,7 @@
doUnplugLocked(realtime, mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
} else {
updateKernelWakelocksLocked();
+ updateNetworkActivityLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
@@ -4689,6 +4799,52 @@
}
}
+ private void updateNetworkActivityLocked() {
+ if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return;
+
+ final NetworkStats snapshot;
+ try {
+ snapshot = mNetworkStatsFactory.readNetworkStatsDetail();
+ } catch (IOException e) {
+ Log.wtf(TAG, "Failed to read network stats", e);
+ return;
+ }
+
+ if (mLastSnapshot == null) {
+ mLastSnapshot = snapshot;
+ return;
+ }
+
+ final NetworkStats delta = snapshot.subtract(mLastSnapshot);
+ mLastSnapshot = snapshot;
+
+ NetworkStats.Entry entry = null;
+ final int size = delta.size();
+ for (int i = 0; i < size; i++) {
+ entry = delta.getValues(i, entry);
+
+ if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
+ if (entry.tag != NetworkStats.TAG_NONE) continue;
+
+ final Uid u = getUidStatsLocked(entry.uid);
+
+ if (mMobileIfaces.contains(entry.iface)) {
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_BYTES, entry.rxBytes);
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_BYTES, entry.txBytes);
+
+ mNetworkActivityCounters[NETWORK_MOBILE_RX_BYTES].addCountLocked(entry.rxBytes);
+ mNetworkActivityCounters[NETWORK_MOBILE_TX_BYTES].addCountLocked(entry.txBytes);
+
+ } else if (mWifiIfaces.contains(entry.iface)) {
+ u.noteNetworkActivityLocked(NETWORK_WIFI_RX_BYTES, entry.rxBytes);
+ u.noteNetworkActivityLocked(NETWORK_WIFI_TX_BYTES, entry.txBytes);
+
+ mNetworkActivityCounters[NETWORK_WIFI_RX_BYTES].addCountLocked(entry.rxBytes);
+ mNetworkActivityCounters[NETWORK_WIFI_TX_BYTES].addCountLocked(entry.txBytes);
+ }
+ }
+ }
+
public long getAwakeTimeBattery() {
return computeBatteryUptime(getBatteryUptimeLocked(), STATS_CURRENT);
}
@@ -4779,47 +4935,6 @@
return getBatteryRealtimeLocked(curTime);
}
- private long getTcpBytes(long current, long[] dataBytes, int which) {
- if (which == STATS_LAST) {
- return dataBytes[STATS_LAST];
- } else {
- if (which == STATS_SINCE_UNPLUGGED) {
- if (dataBytes[STATS_SINCE_UNPLUGGED] < 0) {
- return dataBytes[STATS_LAST];
- } else {
- return current - dataBytes[STATS_SINCE_UNPLUGGED];
- }
- } else if (which == STATS_SINCE_CHARGED) {
- return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_SINCE_CHARGED];
- }
- return current - dataBytes[STATS_CURRENT];
- }
- }
-
- /** Only STATS_UNPLUGGED works properly */
- public long getMobileTcpBytesSent(int which) {
- final long mobileTxBytes = getNetworkStatsSummary().getTotal(null, mMobileIfaces).txBytes;
- return getTcpBytes(mobileTxBytes, mMobileDataTx, which);
- }
-
- /** Only STATS_UNPLUGGED works properly */
- public long getMobileTcpBytesReceived(int which) {
- final long mobileRxBytes = getNetworkStatsSummary().getTotal(null, mMobileIfaces).rxBytes;
- return getTcpBytes(mobileRxBytes, mMobileDataRx, which);
- }
-
- /** Only STATS_UNPLUGGED works properly */
- public long getTotalTcpBytesSent(int which) {
- final long totalTxBytes = getNetworkStatsSummary().getTotal(null).txBytes;
- return getTcpBytes(totalTxBytes, mTotalDataTx, which);
- }
-
- /** Only STATS_UNPLUGGED works properly */
- public long getTotalTcpBytesReceived(int which) {
- final long totalRxBytes = getNetworkStatsSummary().getTotal(null).rxBytes;
- return getTcpBytes(totalRxBytes, mTotalDataRx, which);
- }
-
@Override
public int getDischargeStartLevel() {
synchronized(this) {
@@ -5299,6 +5414,9 @@
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].readSummaryFromParcelLocked(in);
}
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i].readSummaryFromParcelLocked(in);
+ }
mWifiOn = false;
mWifiOnTimer.readSummaryFromParcelLocked(in);
mGlobalWifiRunning = false;
@@ -5355,6 +5473,9 @@
u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
+ u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in);
+ }
+ if (in.readInt() != 0) {
u.createVibratorOnTimerLocked().readSummaryFromParcelLocked(in);
}
@@ -5367,6 +5488,15 @@
}
}
+ if (in.readInt() != 0) {
+ if (u.mNetworkActivityCounters == null) {
+ u.initNetworkActivityLocked();
+ }
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ u.mNetworkActivityCounters[i].readSummaryFromParcelLocked(in);
+ }
+ }
+
int NW = in.readInt();
if (NW > 100) {
Slog.w(TAG, "File corrupt: too many wake locks " + NW);
@@ -5408,6 +5538,7 @@
Uid.Proc p = u.getProcessStatsLocked(procName);
p.mUserTime = p.mLoadedUserTime = in.readLong();
p.mSystemTime = p.mLoadedSystemTime = in.readLong();
+ p.mForegroundTime = p.mLoadedForegroundTime = in.readLong();
p.mStarts = p.mLoadedStarts = in.readInt();
int NSB = in.readInt();
if (NSB > 100) {
@@ -5448,9 +5579,6 @@
s.mLaunches = s.mLoadedLaunches = in.readInt();
}
}
-
- u.mLoadedTcpBytesReceived = in.readLong();
- u.mLoadedTcpBytesSent = in.readLong();
}
}
@@ -5463,6 +5591,7 @@
public void writeSummaryToParcel(Parcel out) {
// Need to update with current kernel wake lock counts.
updateKernelWakelocksLocked();
+ updateNetworkActivityLocked();
final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
@@ -5498,6 +5627,9 @@
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL);
}
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i].writeSummaryFromParcelLocked(out);
+ }
mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
mGlobalWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL);
mBluetoothOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
@@ -5557,6 +5689,12 @@
} else {
out.writeInt(0);
}
+ if (u.mForegroundActivityTimer != null) {
+ out.writeInt(1);
+ u.mForegroundActivityTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ } else {
+ out.writeInt(0);
+ }
if (u.mVibratorOnTimer != null) {
out.writeInt(1);
u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
@@ -5573,6 +5711,15 @@
}
}
+ if (u.mNetworkActivityCounters == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(1);
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ u.mNetworkActivityCounters[i].writeSummaryFromParcelLocked(out);
+ }
+ }
+
int NW = u.mWakelockStats.size();
out.writeInt(NW);
if (NW > 0) {
@@ -5626,6 +5773,7 @@
Uid.Proc ps = ent.getValue();
out.writeLong(ps.mUserTime);
out.writeLong(ps.mSystemTime);
+ out.writeLong(ps.mForegroundTime);
out.writeInt(ps.mStarts);
final int N = ps.mSpeedBins.length;
out.writeInt(N);
@@ -5664,9 +5812,6 @@
}
}
}
-
- out.writeLong(u.getTcpBytesReceived(STATS_SINCE_CHARGED));
- out.writeLong(u.getTcpBytesSent(STATS_SINCE_CHARGED));
}
}
@@ -5705,6 +5850,9 @@
mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i,
null, mUnpluggables, in);
}
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables, in);
+ }
mWifiOn = false;
mWifiOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
mGlobalWifiRunning = false;
@@ -5735,15 +5883,6 @@
mDischargeAmountScreenOffSinceCharge = in.readInt();
mLastWriteTime = in.readLong();
- mMobileDataRx[STATS_LAST] = in.readLong();
- mMobileDataRx[STATS_SINCE_UNPLUGGED] = -1;
- mMobileDataTx[STATS_LAST] = in.readLong();
- mMobileDataTx[STATS_SINCE_UNPLUGGED] = -1;
- mTotalDataRx[STATS_LAST] = in.readLong();
- mTotalDataRx[STATS_SINCE_UNPLUGGED] = -1;
- mTotalDataTx[STATS_LAST] = in.readLong();
- mTotalDataTx[STATS_SINCE_UNPLUGGED] = -1;
-
mRadioDataUptime = in.readLong();
mRadioDataStart = -1;
@@ -5793,6 +5932,7 @@
void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
// Need to update with current kernel wake lock counts.
updateKernelWakelocksLocked();
+ updateNetworkActivityLocked();
final long uSecUptime = SystemClock.uptimeMillis() * 1000;
final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
@@ -5819,6 +5959,9 @@
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].writeToParcel(out, batteryRealtime);
}
+ for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+ mNetworkActivityCounters[i].writeToParcel(out);
+ }
mWifiOnTimer.writeToParcel(out, batteryRealtime);
mGlobalWifiRunningTimer.writeToParcel(out, batteryRealtime);
mBluetoothOnTimer.writeToParcel(out, batteryRealtime);
@@ -5843,11 +5986,6 @@
out.writeInt(mDischargeAmountScreenOffSinceCharge);
out.writeLong(mLastWriteTime);
- out.writeLong(getMobileTcpBytesReceived(STATS_SINCE_UNPLUGGED));
- out.writeLong(getMobileTcpBytesSent(STATS_SINCE_UNPLUGGED));
- out.writeLong(getTotalTcpBytesReceived(STATS_SINCE_UNPLUGGED));
- out.writeLong(getTotalTcpBytesSent(STATS_SINCE_UNPLUGGED));
-
// Write radio uptime for data
out.writeLong(getRadioDataUptime());
@@ -5899,9 +6037,10 @@
public void prepareForDumpLocked() {
// Need to retrieve current kernel wake lock stats before printing.
updateKernelWakelocksLocked();
+ updateNetworkActivityLocked();
}
- public void dumpLocked(PrintWriter pw) {
+ public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly, int reqUid) {
if (DEBUG) {
Printer pr = new PrintWriterPrinter(pw);
pr.println("*** Screen timer:");
@@ -5930,59 +6069,7 @@
mGlobalWifiRunningTimer.logState(pr, " ");
pr.println("*** Bluetooth timer:");
mBluetoothOnTimer.logState(pr, " ");
- pr.println("*** Mobile ifaces:");
- pr.println(mMobileIfaces.toString());
}
- super.dumpLocked(pw);
- }
-
- private NetworkStats mNetworkSummaryCache;
- private NetworkStats mNetworkDetailCache;
-
- private NetworkStats getNetworkStatsSummary() {
- // NOTE: calls from BatteryStatsService already hold this lock
- synchronized (this) {
- if (mNetworkSummaryCache == null
- || mNetworkSummaryCache.getElapsedRealtimeAge() > SECOND_IN_MILLIS) {
- mNetworkSummaryCache = null;
-
- if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
- try {
- mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummaryDev();
- } catch (IOException e) {
- Log.wtf(TAG, "problem reading network stats", e);
- }
- }
-
- if (mNetworkSummaryCache == null) {
- mNetworkSummaryCache = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- }
- }
- return mNetworkSummaryCache;
- }
- }
-
- private NetworkStats getNetworkStatsDetailGroupedByUid() {
- // NOTE: calls from BatteryStatsService already hold this lock
- synchronized (this) {
- if (mNetworkDetailCache == null
- || mNetworkDetailCache.getElapsedRealtimeAge() > SECOND_IN_MILLIS) {
- mNetworkDetailCache = null;
-
- if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
- try {
- mNetworkDetailCache = mNetworkStatsFactory
- .readNetworkStatsDetail().groupedByUid();
- } catch (IOException e) {
- Log.wtf(TAG, "problem reading network stats", e);
- }
- }
-
- if (mNetworkDetailCache == null) {
- mNetworkDetailCache = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- }
- }
- return mNetworkDetailCache;
- }
+ super.dumpLocked(pw, isUnpluggedOnly, reqUid);
}
}
diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java
index b1bb8c1..874bc0e 100644
--- a/core/java/com/android/internal/os/ProcessStats.java
+++ b/core/java/com/android/internal/os/ProcessStats.java
@@ -18,10 +18,12 @@
import static android.os.Process.*;
+import android.os.FileUtils;
import android.os.Process;
import android.os.StrictMode;
import android.os.SystemClock;
import android.util.Slog;
+import com.android.internal.util.FastPrintWriter;
import java.io.File;
import java.io.FileInputStream;
@@ -173,12 +175,15 @@
public static class Stats {
public final int pid;
+ public final int uid;
final String statFile;
final String cmdlineFile;
final String threadsDir;
final ArrayList<Stats> threadStats;
final ArrayList<Stats> workingThreads;
-
+
+ public BatteryStatsImpl.Uid.Proc batteryStats;
+
public boolean interesting;
public String baseName;
@@ -228,6 +233,7 @@
threadStats = null;
workingThreads = null;
}
+ uid = FileUtils.getUid(statFile.toString());
}
}
@@ -518,6 +524,10 @@
return pids;
}
+ /**
+ * Returns the total time (in clock ticks, or 1/100 sec) spent executing in
+ * both user and system code.
+ */
public long getCpuTimeForPid(int pid) {
final String statFile = "/proc/" + pid + "/stat";
final long[] statsData = mSinglePidStatsData;
@@ -531,9 +541,9 @@
}
/**
- * Returns the times spent at each CPU speed, since the last call to this method. If this
- * is the first time, it will return 1 for each value.
- * @return relative times spent at different speed steps.
+ * Returns the delta time (in clock ticks, or 1/100 sec) spent at each CPU
+ * speed, since the last call to this method. If this is the first call, it
+ * will return 1 for each value.
*/
public long[] getLastCpuSpeedTimes() {
if (mCpuSpeedTimes == null) {
@@ -671,13 +681,14 @@
final public String printCurrentLoad() {
StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
+ PrintWriter pw = new FastPrintWriter(sw, false, 128);
pw.print("Load: ");
pw.print(mLoad1);
pw.print(" / ");
pw.print(mLoad5);
pw.print(" / ");
pw.println(mLoad15);
+ pw.flush();
return sw.toString();
}
@@ -685,7 +696,7 @@
buildWorkingProcs();
StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
+ PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.print("CPU usage from ");
if (now > mLastSampleTime) {
@@ -736,7 +747,8 @@
printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0);
-
+
+ pw.flush();
return sw.toString();
}
diff --git a/core/java/com/android/internal/os/TransferPipe.java b/core/java/com/android/internal/os/TransferPipe.java
new file mode 100644
index 0000000..068d914
--- /dev/null
+++ b/core/java/com/android/internal/os/TransferPipe.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+
+/**
+ * Helper for transferring data through a pipe from a client app.
+ */
+public final class TransferPipe implements Runnable {
+ static final String TAG = "TransferPipe";
+ static final boolean DEBUG = false;
+
+ static final long DEFAULT_TIMEOUT = 5000; // 5 seconds
+
+ final Thread mThread;;
+ final ParcelFileDescriptor[] mFds;
+
+ FileDescriptor mOutFd;
+ long mEndTime;
+ String mFailure;
+ boolean mComplete;
+
+ String mBufferPrefix;
+
+ interface Caller {
+ void go(IInterface iface, FileDescriptor fd, String prefix,
+ String[] args) throws RemoteException;
+ }
+
+ public TransferPipe() throws IOException {
+ mThread = new Thread(this, "TransferPipe");
+ mFds = ParcelFileDescriptor.createPipe();
+ }
+
+ ParcelFileDescriptor getReadFd() {
+ return mFds[0];
+ }
+
+ public ParcelFileDescriptor getWriteFd() {
+ return mFds[1];
+ }
+
+ public void setBufferPrefix(String prefix) {
+ mBufferPrefix = prefix;
+ }
+
+ static void go(Caller caller, IInterface iface, FileDescriptor out,
+ String prefix, String[] args) throws IOException, RemoteException {
+ go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT);
+ }
+
+ static void go(Caller caller, IInterface iface, FileDescriptor out,
+ String prefix, String[] args, long timeout) throws IOException, RemoteException {
+ if ((iface.asBinder()) instanceof Binder) {
+ // This is a local object... just call it directly.
+ try {
+ caller.go(iface, out, prefix, args);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+
+ TransferPipe tp = new TransferPipe();
+ try {
+ caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args);
+ tp.go(out, timeout);
+ } finally {
+ tp.kill();
+ }
+ }
+
+ static void goDump(IBinder binder, FileDescriptor out,
+ String[] args) throws IOException, RemoteException {
+ goDump(binder, out, args, DEFAULT_TIMEOUT);
+ }
+
+ static void goDump(IBinder binder, FileDescriptor out,
+ String[] args, long timeout) throws IOException, RemoteException {
+ if (binder instanceof Binder) {
+ // This is a local object... just call it directly.
+ try {
+ binder.dump(out, args);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+
+ TransferPipe tp = new TransferPipe();
+ try {
+ binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args);
+ tp.go(out, timeout);
+ } finally {
+ tp.kill();
+ }
+ }
+
+ public void go(FileDescriptor out) throws IOException {
+ go(out, DEFAULT_TIMEOUT);
+ }
+
+ public void go(FileDescriptor out, long timeout) throws IOException {
+ try {
+ synchronized (this) {
+ mOutFd = out;
+ mEndTime = SystemClock.uptimeMillis() + timeout;
+
+ if (DEBUG) Slog.i(TAG, "read=" + getReadFd() + " write=" + getWriteFd()
+ + " out=" + out);
+
+ // Close the write fd, so we know when the other side is done.
+ closeFd(1);
+
+ mThread.start();
+
+ while (mFailure == null && !mComplete) {
+ long waitTime = mEndTime - SystemClock.uptimeMillis();
+ if (waitTime <= 0) {
+ if (DEBUG) Slog.i(TAG, "TIMEOUT!");
+ mThread.interrupt();
+ throw new IOException("Timeout");
+ }
+
+ try {
+ wait(waitTime);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ if (DEBUG) Slog.i(TAG, "Finished: " + mFailure);
+ if (mFailure != null) {
+ throw new IOException(mFailure);
+ }
+ }
+ } finally {
+ kill();
+ }
+ }
+
+ void closeFd(int num) {
+ if (mFds[num] != null) {
+ if (DEBUG) Slog.i(TAG, "Closing: " + mFds[num]);
+ try {
+ mFds[num].close();
+ } catch (IOException e) {
+ }
+ mFds[num] = null;
+ }
+ }
+
+ public void kill() {
+ closeFd(0);
+ closeFd(1);
+ }
+
+ @Override
+ public void run() {
+ final byte[] buffer = new byte[1024];
+ final FileInputStream fis = new FileInputStream(getReadFd().getFileDescriptor());
+ final FileOutputStream fos = new FileOutputStream(mOutFd);
+
+ if (DEBUG) Slog.i(TAG, "Ready to read pipe...");
+ byte[] bufferPrefix = null;
+ boolean needPrefix = true;
+ if (mBufferPrefix != null) {
+ bufferPrefix = mBufferPrefix.getBytes();
+ }
+
+ int size;
+ try {
+ while ((size=fis.read(buffer)) > 0) {
+ if (DEBUG) Slog.i(TAG, "Got " + size + " bytes");
+ if (bufferPrefix == null) {
+ fos.write(buffer, 0, size);
+ } else {
+ int start = 0;
+ for (int i=0; i<size; i++) {
+ if (buffer[i] != '\n') {
+ if (i > start) {
+ fos.write(buffer, start, i-start);
+ }
+ start = i;
+ if (needPrefix) {
+ fos.write(bufferPrefix);
+ needPrefix = false;
+ }
+ do {
+ i++;
+ } while (i<size && buffer[i] != '\n');
+ if (i < size) {
+ needPrefix = true;
+ }
+ }
+ }
+ if (size > start) {
+ fos.write(buffer, start, size-start);
+ }
+ }
+ }
+ if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size);
+ if (mThread.isInterrupted()) {
+ if (DEBUG) Slog.i(TAG, "Interrupted!");
+ }
+ } catch (IOException e) {
+ synchronized (this) {
+ mFailure = e.toString();
+ notifyAll();
+ return;
+ }
+ }
+
+ synchronized (this) {
+ mComplete = true;
+ notifyAll();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index fb22df7..ccc0089 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -22,9 +22,11 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.net.LocalServerSocket;
+import android.opengl.EGL14;
import android.os.Debug;
import android.os.Process;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.Trace;
import android.util.EventLog;
import android.util.Log;
@@ -59,9 +61,10 @@
* @hide
*/
public class ZygoteInit {
-
private static final String TAG = "Zygote";
+ private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
+
private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
@@ -227,6 +230,13 @@
static void preload() {
preloadClasses();
preloadResources();
+ preloadOpenGL();
+ }
+
+ private static void preloadOpenGL() {
+ if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
+ EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+ }
}
/**
@@ -479,7 +489,6 @@
OsConstants.CAP_NET_BIND_SERVICE,
OsConstants.CAP_NET_BROADCAST,
OsConstants.CAP_NET_RAW,
- OsConstants.CAP_SYS_BOOT,
OsConstants.CAP_SYS_MODULE,
OsConstants.CAP_SYS_NICE,
OsConstants.CAP_SYS_RESOURCE,
diff --git a/core/java/com/android/internal/policy/IKeyguardExitCallback.aidl b/core/java/com/android/internal/policy/IKeyguardExitCallback.aidl
new file mode 100644
index 0000000..3702712
--- /dev/null
+++ b/core/java/com/android/internal/policy/IKeyguardExitCallback.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy;
+
+oneway interface IKeyguardExitCallback {
+ void onKeyguardExitResult(boolean success);
+}
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
new file mode 100644
index 0000000..d1f7fa3
--- /dev/null
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy;
+
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.policy.IKeyguardExitCallback;
+
+import android.os.Bundle;
+
+interface IKeyguardService {
+ boolean isShowing();
+ boolean isSecure();
+ boolean isShowingAndNotHidden();
+ boolean isInputRestricted();
+ boolean isDismissable();
+ oneway void verifyUnlock(IKeyguardExitCallback callback);
+ oneway void keyguardDone(boolean authenticated, boolean wakeup);
+ oneway void setHidden(boolean isHidden);
+ oneway void dismiss();
+ oneway void onDreamingStarted();
+ oneway void onDreamingStopped();
+ oneway void onScreenTurnedOff(int reason);
+ oneway void onScreenTurnedOn(IKeyguardShowCallback callback);
+ oneway void setKeyguardEnabled(boolean enabled);
+ oneway void onSystemReady();
+ oneway void doKeyguardTimeout(in Bundle options);
+ oneway void setCurrentUser(int userId);
+ oneway void showAssistant();
+}
diff --git a/core/java/com/android/internal/policy/IKeyguardShowCallback.aidl b/core/java/com/android/internal/policy/IKeyguardShowCallback.aidl
new file mode 100644
index 0000000..a2784d9
--- /dev/null
+++ b/core/java/com/android/internal/policy/IKeyguardShowCallback.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy;
+
+oneway interface IKeyguardShowCallback {
+ void onShown(IBinder windowToken);
+}
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index 7b8c582..65b56ec 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -40,7 +40,7 @@
if (handler.runWithScissors(new Runnable() {
@Override
public void run() {
- PrintWriter lpw = new PrintWriter(sw);
+ PrintWriter lpw = new FastPrintWriter(sw);
dump.dump(lpw);
lpw.close();
}
diff --git a/core/java/com/android/internal/util/FastPrintWriter.java b/core/java/com/android/internal/util/FastPrintWriter.java
new file mode 100644
index 0000000..9bec10e
--- /dev/null
+++ b/core/java/com/android/internal/util/FastPrintWriter.java
@@ -0,0 +1,600 @@
+package com.android.internal.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+public class FastPrintWriter extends PrintWriter {
+ private static Writer sDummyWriter = new Writer() {
+ @Override
+ public void close() throws IOException {
+ UnsupportedOperationException ex
+ = new UnsupportedOperationException("Shouldn't be here");
+ throw ex;
+ }
+
+ @Override
+ public void flush() throws IOException {
+ close();
+ }
+
+ @Override
+ public void write(char[] buf, int offset, int count) throws IOException {
+ close();
+ }
+ };
+
+ private final int mBufferLen;
+ private final char[] mText;
+ private int mPos;
+
+ final private OutputStream mOutputStream;
+ final private boolean mAutoFlush;
+ final private String mSeparator;
+
+ final private Writer mWriter;
+
+ private CharsetEncoder mCharset;
+ final private ByteBuffer mBytes;
+
+ private boolean mIoError;
+
+ /**
+ * Constructs a new {@code PrintWriter} with {@code out} as its target
+ * stream. By default, the new print writer does not automatically flush its
+ * contents to the target stream when a newline is encountered.
+ *
+ * @param out
+ * the target output stream.
+ * @throws NullPointerException
+ * if {@code out} is {@code null}.
+ */
+ public FastPrintWriter(OutputStream out) {
+ this(out, false, 8192);
+ }
+
+ /**
+ * Constructs a new {@code PrintWriter} with {@code out} as its target
+ * stream. The parameter {@code autoFlush} determines if the print writer
+ * automatically flushes its contents to the target stream when a newline is
+ * encountered.
+ *
+ * @param out
+ * the target output stream.
+ * @param autoFlush
+ * indicates whether contents are flushed upon encountering a
+ * newline sequence.
+ * @throws NullPointerException
+ * if {@code out} is {@code null}.
+ */
+ public FastPrintWriter(OutputStream out, boolean autoFlush) {
+ this(out, autoFlush, 8192);
+ }
+
+ /**
+ * Constructs a new {@code PrintWriter} with {@code out} as its target
+ * stream and a custom buffer size. The parameter {@code autoFlush} determines
+ * if the print writer automatically flushes its contents to the target stream
+ * when a newline is encountered.
+ *
+ * @param out
+ * the target output stream.
+ * @param autoFlush
+ * indicates whether contents are flushed upon encountering a
+ * newline sequence.
+ * @param bufferLen
+ * specifies the size of the FastPrintWriter's internal buffer; the
+ * default is 8192.
+ * @throws NullPointerException
+ * if {@code out} is {@code null}.
+ */
+ public FastPrintWriter(OutputStream out, boolean autoFlush, int bufferLen) {
+ super(sDummyWriter, autoFlush);
+ if (out == null) {
+ throw new NullPointerException("out is null");
+ }
+ mBufferLen = bufferLen;
+ mText = new char[bufferLen];
+ mBytes = ByteBuffer.allocate(mBufferLen);
+ mOutputStream = out;
+ mWriter = null;
+ mAutoFlush = autoFlush;
+ mSeparator = System.lineSeparator();
+ initDefaultEncoder();
+ }
+
+ /**
+ * Constructs a new {@code PrintWriter} with {@code wr} as its target
+ * writer. By default, the new print writer does not automatically flush its
+ * contents to the target writer when a newline is encountered.
+ *
+ * <p>NOTE: Unlike PrintWriter, this version will still do buffering inside of
+ * FastPrintWriter before sending data to the Writer. This means you must call
+ * flush() before retrieving any data from the Writer.</p>
+ *
+ * @param wr
+ * the target writer.
+ * @throws NullPointerException
+ * if {@code wr} is {@code null}.
+ */
+ public FastPrintWriter(Writer wr) {
+ this(wr, false, 8192);
+ }
+
+ /**
+ * Constructs a new {@code PrintWriter} with {@code out} as its target
+ * writer. The parameter {@code autoFlush} determines if the print writer
+ * automatically flushes its contents to the target writer when a newline is
+ * encountered.
+ *
+ * @param wr
+ * the target writer.
+ * @param autoFlush
+ * indicates whether to flush contents upon encountering a
+ * newline sequence.
+ * @throws NullPointerException
+ * if {@code out} is {@code null}.
+ */
+ public FastPrintWriter(Writer wr, boolean autoFlush) {
+ this(wr, autoFlush, 8192);
+ }
+
+ /**
+ * Constructs a new {@code PrintWriter} with {@code out} as its target
+ * writer and a custom buffer size. The parameter {@code autoFlush} determines
+ * if the print writer automatically flushes its contents to the target writer
+ * when a newline is encountered.
+ *
+ * @param wr
+ * the target writer.
+ * @param autoFlush
+ * indicates whether to flush contents upon encountering a
+ * newline sequence.
+ * @param bufferLen
+ * specifies the size of the FastPrintWriter's internal buffer; the
+ * default is 8192.
+ * @throws NullPointerException
+ * if {@code wr} is {@code null}.
+ */
+ public FastPrintWriter(Writer wr, boolean autoFlush, int bufferLen) {
+ super(sDummyWriter, autoFlush);
+ if (wr == null) {
+ throw new NullPointerException("wr is null");
+ }
+ mBufferLen = bufferLen;
+ mText = new char[bufferLen];
+ mBytes = null;
+ mOutputStream = null;
+ mWriter = wr;
+ mAutoFlush = autoFlush;
+ mSeparator = System.lineSeparator();
+ initDefaultEncoder();
+ }
+
+ private final void initEncoder(String csn) throws UnsupportedEncodingException {
+ try {
+ mCharset = Charset.forName(csn).newEncoder();
+ } catch (Exception e) {
+ throw new UnsupportedEncodingException(csn);
+ }
+ mCharset.onMalformedInput(CodingErrorAction.REPLACE);
+ mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
+ }
+
+ /**
+ * Flushes this writer and returns the value of the error flag.
+ *
+ * @return {@code true} if either an {@code IOException} has been thrown
+ * previously or if {@code setError()} has been called;
+ * {@code false} otherwise.
+ * @see #setError()
+ */
+ public boolean checkError() {
+ flush();
+ synchronized (lock) {
+ return mIoError;
+ }
+ }
+
+ /**
+ * Sets the error state of the stream to false.
+ * @since 1.6
+ */
+ protected void clearError() {
+ synchronized (lock) {
+ mIoError = false;
+ }
+ }
+
+ /**
+ * Sets the error flag of this writer to true.
+ */
+ protected void setError() {
+ synchronized (lock) {
+ mIoError = true;
+ }
+ }
+
+ private final void initDefaultEncoder() {
+ mCharset = Charset.defaultCharset().newEncoder();
+ mCharset.onMalformedInput(CodingErrorAction.REPLACE);
+ mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
+ }
+
+ private void appendLocked(char c) throws IOException {
+ int pos = mPos;
+ if (pos >= (mBufferLen-1)) {
+ flushLocked();
+ pos = mPos;
+ }
+ mText[pos] = c;
+ mPos = pos+1;
+ }
+
+ private void appendLocked(String str, int i, final int length) throws IOException {
+ final int BUFFER_LEN = mBufferLen;
+ if (length > BUFFER_LEN) {
+ final int end = i + length;
+ while (i < end) {
+ int next = i + BUFFER_LEN;
+ appendLocked(str, i, next < end ? BUFFER_LEN : (end - i));
+ i = next;
+ }
+ return;
+ }
+ int pos = mPos;
+ if ((pos+length) > BUFFER_LEN) {
+ flushLocked();
+ pos = mPos;
+ }
+ str.getChars(i, i + length, mText, pos);
+ mPos = pos + length;
+ }
+
+ private void appendLocked(char[] buf, int i, final int length) throws IOException {
+ final int BUFFER_LEN = mBufferLen;
+ if (length > BUFFER_LEN) {
+ final int end = i + length;
+ while (i < end) {
+ int next = i + BUFFER_LEN;
+ appendLocked(buf, i, next < end ? BUFFER_LEN : (end - i));
+ i = next;
+ }
+ return;
+ }
+ int pos = mPos;
+ if ((pos+length) > BUFFER_LEN) {
+ flushLocked();
+ pos = mPos;
+ }
+ System.arraycopy(buf, i, mText, pos, length);
+ mPos = pos + length;
+ }
+
+ private void flushBytesLocked() throws IOException {
+ int position;
+ if ((position = mBytes.position()) > 0) {
+ mBytes.flip();
+ mOutputStream.write(mBytes.array(), 0, position);
+ mBytes.clear();
+ }
+ }
+
+ private void flushLocked() throws IOException {
+ //Log.i("PackageManager", "flush mPos=" + mPos);
+ if (mPos > 0) {
+ if (mOutputStream != null) {
+ CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
+ CoderResult result = mCharset.encode(charBuffer, mBytes, true);
+ while (true) {
+ if (result.isError()) {
+ throw new IOException(result.toString());
+ } else if (result.isOverflow()) {
+ flushBytesLocked();
+ result = mCharset.encode(charBuffer, mBytes, true);
+ continue;
+ }
+ break;
+ }
+ flushBytesLocked();
+ mOutputStream.flush();
+ } else {
+ mWriter.write(mText, 0, mPos);
+ mWriter.flush();
+ }
+ mPos = 0;
+ }
+ }
+
+ /**
+ * Ensures that all pending data is sent out to the target. It also
+ * flushes the target. If an I/O error occurs, this writer's error
+ * state is set to {@code true}.
+ */
+ @Override
+ public void flush() {
+ synchronized (lock) {
+ try {
+ flushLocked();
+ if (mOutputStream != null) {
+ mOutputStream.flush();
+ } else {
+ mWriter.flush();
+ }
+ } catch (IOException e) {
+ setError();
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ synchronized (lock) {
+ try {
+ flushLocked();
+ if (mOutputStream != null) {
+ mOutputStream.close();
+ } else {
+ mWriter.close();
+ }
+ } catch (IOException e) {
+ setError();
+ }
+ }
+ }
+
+ /**
+ * Prints the string representation of the specified character array
+ * to the target.
+ *
+ * @param charArray
+ * the character array to print to the target.
+ * @see #print(String)
+ */
+ public void print(char[] charArray) {
+ synchronized (lock) {
+ try {
+ appendLocked(charArray, 0, charArray.length);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Prints the string representation of the specified character to the
+ * target.
+ *
+ * @param ch
+ * the character to print to the target.
+ * @see #print(String)
+ */
+ public void print(char ch) {
+ synchronized (lock) {
+ try {
+ appendLocked(ch);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Prints a string to the target. The string is converted to an array of
+ * bytes using the encoding chosen during the construction of this writer.
+ * The bytes are then written to the target with {@code write(int)}.
+ * <p>
+ * If an I/O error occurs, this writer's error flag is set to {@code true}.
+ *
+ * @param str
+ * the string to print to the target.
+ * @see #write(int)
+ */
+ public void print(String str) {
+ if (str == null) {
+ str = String.valueOf((Object) null);
+ }
+ synchronized (lock) {
+ try {
+ appendLocked(str, 0, str.length());
+ } catch (IOException e) {
+ setError();
+ }
+ }
+ }
+
+
+ @Override
+ public void print(int inum) {
+ if (inum == 0) {
+ print("0");
+ } else {
+ super.print(inum);
+ }
+ }
+
+ @Override
+ public void print(long lnum) {
+ if (lnum == 0) {
+ print("0");
+ } else {
+ super.print(lnum);
+ }
+ }
+
+ /**
+ * Prints a newline. Flushes this writer if the autoFlush flag is set to {@code true}.
+ */
+ public void println() {
+ synchronized (lock) {
+ try {
+ appendLocked(mSeparator, 0, mSeparator.length());
+ if (mAutoFlush) {
+ flushLocked();
+ }
+ } catch (IOException e) {
+ setError();
+ }
+ }
+ }
+
+ @Override
+ public void println(int inum) {
+ if (inum == 0) {
+ println("0");
+ } else {
+ super.println(inum);
+ }
+ }
+
+ @Override
+ public void println(long lnum) {
+ if (lnum == 0) {
+ println("0");
+ } else {
+ super.println(lnum);
+ }
+ }
+
+ /**
+ * Prints the string representation of the character array {@code chars} followed by a newline.
+ * Flushes this writer if the autoFlush flag is set to {@code true}.
+ */
+ public void println(char[] chars) {
+ print(chars);
+ println();
+ }
+
+ /**
+ * Prints the string representation of the char {@code c} followed by a newline.
+ * Flushes this writer if the autoFlush flag is set to {@code true}.
+ */
+ public void println(char c) {
+ print(c);
+ println();
+ }
+
+ /**
+ * Writes {@code count} characters from {@code buffer} starting at {@code
+ * offset} to the target.
+ * <p>
+ * This writer's error flag is set to {@code true} if this writer is closed
+ * or an I/O error occurs.
+ *
+ * @param buf
+ * the buffer to write to the target.
+ * @param offset
+ * the index of the first character in {@code buffer} to write.
+ * @param count
+ * the number of characters in {@code buffer} to write.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0} or {@code count < 0}, or if {@code
+ * offset + count} is greater than the length of {@code buf}.
+ */
+ @Override
+ public void write(char[] buf, int offset, int count) {
+ synchronized (lock) {
+ try {
+ appendLocked(buf, offset, count);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Writes one character to the target. Only the two least significant bytes
+ * of the integer {@code oneChar} are written.
+ * <p>
+ * This writer's error flag is set to {@code true} if this writer is closed
+ * or an I/O error occurs.
+ *
+ * @param oneChar
+ * the character to write to the target.
+ */
+ @Override
+ public void write(int oneChar) {
+ synchronized (lock) {
+ try {
+ appendLocked((char) oneChar);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Writes the characters from the specified string to the target.
+ *
+ * @param str
+ * the non-null string containing the characters to write.
+ */
+ @Override
+ public void write(String str) {
+ synchronized (lock) {
+ try {
+ appendLocked(str, 0, str.length());
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Writes {@code count} characters from {@code str} starting at {@code
+ * offset} to the target.
+ *
+ * @param str
+ * the non-null string containing the characters to write.
+ * @param offset
+ * the index of the first character in {@code str} to write.
+ * @param count
+ * the number of characters from {@code str} to write.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0} or {@code count < 0}, or if {@code
+ * offset + count} is greater than the length of {@code str}.
+ */
+ @Override
+ public void write(String str, int offset, int count) {
+ synchronized (lock) {
+ try {
+ appendLocked(str, offset, count);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Appends a subsequence of the character sequence {@code csq} to the
+ * target. This method works the same way as {@code
+ * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code
+ * csq} is {@code null}, then the specified subsequence of the string "null"
+ * will be written to the target.
+ *
+ * @param csq
+ * the character sequence appended to the target.
+ * @param start
+ * the index of the first char in the character sequence appended
+ * to the target.
+ * @param end
+ * the index of the character following the last character of the
+ * subsequence appended to the target.
+ * @return this writer.
+ * @throws StringIndexOutOfBoundsException
+ * if {@code start > end}, {@code start < 0}, {@code end < 0} or
+ * either {@code start} or {@code end} are greater or equal than
+ * the length of {@code csq}.
+ */
+ @Override
+ public PrintWriter append(CharSequence csq, int start, int end) {
+ if (csq == null) {
+ csq = "null";
+ }
+ String output = csq.subSequence(start, end).toString();
+ write(output, 0, output.length());
+ return this;
+ }
+}
diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java
index d01a817..6fddd09 100644
--- a/core/java/com/android/internal/util/IndentingPrintWriter.java
+++ b/core/java/com/android/internal/util/IndentingPrintWriter.java
@@ -68,6 +68,10 @@
print(key + "=" + String.valueOf(value) + " ");
}
+ public void printHexPair(String key, int value) {
+ print(key + "=0x" + Integer.toHexString(value) + " ");
+ }
+
@Override
public void write(char[] buf, int offset, int count) {
final int indentLength = mIndentBuilder.length();
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index 95130c8..70e2bfc 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -17,12 +17,13 @@
package com.android.internal.view;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -40,6 +41,21 @@
}
/**
+ * Gets whether the device supports rotation. In general such a
+ * device has an accelerometer and has the portrait and landscape
+ * features.
+ *
+ * @param context Context for accessing system resources.
+ * @return Whether the device supports rotation.
+ */
+ public static boolean isRotationSupported(Context context) {
+ PackageManager pm = context.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_ACCELEROMETER)
+ && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT)
+ && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE);
+ }
+
+ /**
* Returns true if the device supports the rotation-lock toggle feature
* in the system UI or system bar.
*
@@ -48,7 +64,8 @@
* settings.
*/
public static boolean isRotationLockToggleSupported(Context context) {
- return context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
+ return isRotationSupported(context)
+ && context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
/**
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index df579c6..35b76dd 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -26,48 +26,41 @@
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.RadioButton;
+import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* The item view for each item in the ListView-based MenuViews.
*/
-public class ListMenuItemView extends LinearLayout implements MenuView.ItemView {
- private static final String TAG = "ListMenuItemView";
- private MenuItemImpl mItemData;
-
+public class ListMenuItemView extends RelativeLayout implements MenuView.ItemView {
+ private final Drawable mBackground;
+ private final int mTextAppearance;
+
+ private MenuItemImpl mItemData;
+
private ImageView mIconView;
private RadioButton mRadioButton;
private TextView mTitleView;
private CheckBox mCheckBox;
private TextView mShortcutView;
-
- private Drawable mBackground;
- private int mTextAppearance;
- private Context mTextAppearanceContext;
- private boolean mPreserveIconSpacing;
-
- private int mMenuType;
-
+
private LayoutInflater mInflater;
+ private boolean mPreserveIconSpacing;
private boolean mForceShowIcon;
public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
-
- TypedArray a =
- context.obtainStyledAttributes(
+
+ final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.MenuView, defStyle, 0);
-
mBackground = a.getDrawable(com.android.internal.R.styleable.MenuView_itemBackground);
- mTextAppearance = a.getResourceId(com.android.internal.R.styleable.
- MenuView_itemTextAppearance, -1);
+ mTextAppearance = a.getResourceId(
+ com.android.internal.R.styleable.MenuView_itemTextAppearance, 0);
mPreserveIconSpacing = a.getBoolean(
com.android.internal.R.styleable.MenuView_preserveIconSpacing, false);
- mTextAppearanceContext = context;
-
+
a.recycle();
}
@@ -78,24 +71,22 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
-
- setBackgroundDrawable(mBackground);
-
+
+ setBackground(mBackground);
+
mTitleView = (TextView) findViewById(com.android.internal.R.id.title);
- if (mTextAppearance != -1) {
- mTitleView.setTextAppearance(mTextAppearanceContext,
- mTextAppearance);
+ if (mTextAppearance != 0) {
+ mTitleView.setTextAppearance(mContext, mTextAppearance);
}
-
+
mShortcutView = (TextView) findViewById(com.android.internal.R.id.shortcut);
}
+ @Override
public void initialize(MenuItemImpl itemData, int menuType) {
mItemData = itemData;
- mMenuType = menuType;
setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
-
setTitle(itemData.getTitleForItemView(this));
setCheckable(itemData.isCheckable());
setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
@@ -107,29 +98,36 @@
mPreserveIconSpacing = mForceShowIcon = forceShow;
}
+ @Override
public void setTitle(CharSequence title) {
if (title != null) {
mTitleView.setText(title);
-
- if (mTitleView.getVisibility() != VISIBLE) mTitleView.setVisibility(VISIBLE);
+
+ if (mTitleView.getVisibility() != VISIBLE) {
+ mTitleView.setVisibility(VISIBLE);
+ }
} else {
- if (mTitleView.getVisibility() != GONE) mTitleView.setVisibility(GONE);
+ if (mTitleView.getVisibility() != GONE) {
+ mTitleView.setVisibility(GONE);
+ }
}
}
-
+
+ @Override
public MenuItemImpl getItemData() {
return mItemData;
}
+ @Override
public void setCheckable(boolean checkable) {
if (!checkable && mRadioButton == null && mCheckBox == null) {
return;
}
-
+
// Depending on whether its exclusive check or not, the checkbox or
// radio button will be the one in use (and the other will be otherCompoundButton)
final CompoundButton compoundButton;
- final CompoundButton otherCompoundButton;
+ final CompoundButton otherCompoundButton;
if (mItemData.isExclusiveCheckable()) {
if (mRadioButton == null) {
@@ -144,28 +142,36 @@
compoundButton = mCheckBox;
otherCompoundButton = mRadioButton;
}
-
+
if (checkable) {
compoundButton.setChecked(mItemData.isChecked());
-
+
final int newVisibility = checkable ? VISIBLE : GONE;
if (compoundButton.getVisibility() != newVisibility) {
compoundButton.setVisibility(newVisibility);
}
-
+
+ // Align text to the start of the visible compound button.
+ alignTextToStartOf(compoundButton);
+
// Make sure the other compound button isn't visible
if (otherCompoundButton != null && otherCompoundButton.getVisibility() != GONE) {
otherCompoundButton.setVisibility(GONE);
}
} else {
- if (mCheckBox != null) mCheckBox.setVisibility(GONE);
- if (mRadioButton != null) mRadioButton.setVisibility(GONE);
+ if (mCheckBox != null) {
+ mCheckBox.setVisibility(GONE);
+ }
+ if (mRadioButton != null) {
+ mRadioButton.setVisibility(GONE);
+ }
}
}
-
+
+ @Override
public void setChecked(boolean checked) {
CompoundButton compoundButton;
-
+
if (mItemData.isExclusiveCheckable()) {
if (mRadioButton == null) {
insertRadioButton();
@@ -177,13 +183,13 @@
}
compoundButton = mCheckBox;
}
-
+
compoundButton.setChecked(checked);
}
+ @Override
public void setShortcut(boolean showShortcut, char shortcutKey) {
- final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
- ? VISIBLE : GONE;
+ final int newVisibility = (showShortcut && mItemData.shouldShowShortcut()) ? VISIBLE : GONE;
if (newVisibility == VISIBLE) {
mShortcutView.setText(mItemData.getShortcutLabel());
@@ -193,21 +199,22 @@
mShortcutView.setVisibility(newVisibility);
}
}
-
+
+ @Override
public void setIcon(Drawable icon) {
final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
if (!showIcon && !mPreserveIconSpacing) {
return;
}
-
+
if (mIconView == null && icon == null && !mPreserveIconSpacing) {
return;
}
-
+
if (mIconView == null) {
insertIconView();
}
-
+
if (icon != null || mPreserveIconSpacing) {
mIconView.setImageDrawable(showIcon ? icon : null);
@@ -218,51 +225,55 @@
mIconView.setVisibility(GONE);
}
}
-
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mIconView != null && mPreserveIconSpacing) {
// Enforce minimum icon spacing
- ViewGroup.LayoutParams lp = getLayoutParams();
- LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+ final ViewGroup.LayoutParams lp = getLayoutParams();
+ final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
if (lp.height > 0 && iconLp.width <= 0) {
iconLp.width = lp.height;
}
}
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void insertIconView() {
- LayoutInflater inflater = getInflater();
- mIconView = (ImageView) inflater.inflate(com.android.internal.R.layout.list_menu_item_icon,
- this, false);
- addView(mIconView, 0);
+ mIconView = (ImageView) getInflater()
+ .inflate(com.android.internal.R.layout.list_menu_item_icon, this, false);
+ addView(mIconView);
}
-
+
private void insertRadioButton() {
- LayoutInflater inflater = getInflater();
- mRadioButton =
- (RadioButton) inflater.inflate(com.android.internal.R.layout.list_menu_item_radio,
- this, false);
+ mRadioButton = (RadioButton) getInflater()
+ .inflate(com.android.internal.R.layout.list_menu_item_radio, this, false);
addView(mRadioButton);
}
-
+
private void insertCheckBox() {
- LayoutInflater inflater = getInflater();
- mCheckBox =
- (CheckBox) inflater.inflate(com.android.internal.R.layout.list_menu_item_checkbox,
- this, false);
+ mCheckBox = (CheckBox) getInflater()
+ .inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, false);
addView(mCheckBox);
}
+ private void alignTextToStartOf(View v) {
+ final LayoutParams params = (LayoutParams) mTitleView.getLayoutParams();
+ params.addRule(RelativeLayout.START_OF, v.getId());
+ mTitleView.setLayoutParams(params);
+ }
+
+ @Override
public boolean prefersCondensedTitle() {
return false;
}
+ @Override
public boolean showsIcon() {
return mForceShowIcon;
}
-
+
private LayoutInflater getInflater() {
if (mInflater == null) {
mInflater = LayoutInflater.from(mContext);
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index cacc86b..6d39860 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.content.res.Resources;
-import android.database.DataSetObserver;
import android.os.Parcelable;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -47,23 +46,28 @@
static final int ITEM_LAYOUT = com.android.internal.R.layout.popup_menu_item_layout;
- private Context mContext;
- private LayoutInflater mInflater;
- private ListPopupWindow mPopup;
- private MenuBuilder mMenu;
- private int mPopupMaxWidth;
+ private final Context mContext;
+ private final LayoutInflater mInflater;
+ private final MenuBuilder mMenu;
+ private final MenuAdapter mAdapter;
+ private final boolean mOverflowOnly;
+ private final int mPopupMaxWidth;
+
private View mAnchorView;
- private boolean mOverflowOnly;
+ private ListPopupWindow mPopup;
private ViewTreeObserver mTreeObserver;
-
- private MenuAdapter mAdapter;
-
private Callback mPresenterCallback;
boolean mForceShowIcon;
private ViewGroup mMeasureParent;
+ /** Whether the cached content width value is valid. */
+ private boolean mHasContentWidth;
+
+ /** Cached content width from {@link #measureContentWidth}. */
+ private int mContentWidth;
+
public MenuPopupHelper(Context context, MenuBuilder menu) {
this(context, menu, null, false);
}
@@ -77,6 +81,7 @@
mContext = context;
mInflater = LayoutInflater.from(context);
mMenu = menu;
+ mAdapter = new MenuAdapter(mMenu);
mOverflowOnly = overflowOnly;
final Resources res = context.getResources();
@@ -106,8 +111,6 @@
mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle);
mPopup.setOnDismissListener(this);
mPopup.setOnItemClickListener(this);
-
- mAdapter = new MenuAdapter(mMenu);
mPopup.setAdapter(mAdapter);
mPopup.setModal(true);
@@ -122,7 +125,12 @@
return false;
}
- mPopup.setContentWidth(Math.min(measureContentWidth(mAdapter), mPopupMaxWidth));
+ if (!mHasContentWidth) {
+ mContentWidth = measureContentWidth();
+ mHasContentWidth = true;
+ }
+
+ mPopup.setContentWidth(mContentWidth);
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
mPopup.show();
mPopup.getListView().setOnKeyListener(this);
@@ -164,15 +172,15 @@
return false;
}
- private int measureContentWidth(ListAdapter adapter) {
+ private int measureContentWidth() {
// Menus don't tend to be long, so this is more sane than it looks.
- int width = 0;
+ int maxWidth = 0;
View itemView = null;
int itemType = 0;
- final int widthMeasureSpec =
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- final int heightMeasureSpec =
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
+ final ListAdapter adapter = mAdapter;
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
final int count = adapter.getCount();
for (int i = 0; i < count; i++) {
final int positionType = adapter.getItemViewType(i);
@@ -180,14 +188,23 @@
itemType = positionType;
itemView = null;
}
+
if (mMeasureParent == null) {
mMeasureParent = new FrameLayout(mContext);
}
+
itemView = adapter.getView(i, itemView, mMeasureParent);
itemView.measure(widthMeasureSpec, heightMeasureSpec);
- width = Math.max(width, itemView.getMeasuredWidth());
+
+ final int itemWidth = itemView.getMeasuredWidth();
+ if (itemWidth >= mPopupMaxWidth) {
+ return mPopupMaxWidth;
+ } else if (itemWidth > maxWidth) {
+ maxWidth = itemWidth;
+ }
}
- return width;
+
+ return maxWidth;
}
@Override
@@ -228,7 +245,11 @@
@Override
public void updateMenuView(boolean cleared) {
- if (mAdapter != null) mAdapter.notifyDataSetChanged();
+ mHasContentWidth = false;
+
+ if (mAdapter != null) {
+ mAdapter.notifyDataSetChanged();
+ }
}
@Override
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index 6120a09..8a49899 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -296,23 +296,7 @@
if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
final int containerHeight = getMeasuredHeight();
final int tabHeight = mTabContainer.getMeasuredHeight();
-
- if ((mActionBarView.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == 0) {
- // Not showing home, put tabs on top.
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
-
- if (child == mTabContainer) continue;
-
- if (!mActionBarView.isCollapsed()) {
- child.offsetTopAndBottom(tabHeight);
- }
- }
- mTabContainer.layout(l, 0, r, tabHeight);
- } else {
- mTabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
- }
+ mTabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
}
boolean needsInvalidate = false;
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index dda1a10..f2bd522 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -16,24 +16,12 @@
package com.android.internal.widget;
-import com.android.internal.R;
-import com.android.internal.view.menu.ActionMenuItem;
-import com.android.internal.view.menu.ActionMenuPresenter;
-import com.android.internal.view.menu.ActionMenuView;
-import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.view.menu.MenuItemImpl;
-import com.android.internal.view.menu.MenuPresenter;
-import com.android.internal.view.menu.MenuView;
-import com.android.internal.view.menu.SubMenuBuilder;
-
import android.animation.LayoutTransition;
import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
-import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
@@ -42,7 +30,6 @@
import android.text.Layout;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.CollapsibleActionView;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -62,6 +49,15 @@
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
+import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenuItem;
+import com.android.internal.view.menu.ActionMenuPresenter;
+import com.android.internal.view.menu.ActionMenuView;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuItemImpl;
+import com.android.internal.view.menu.MenuPresenter;
+import com.android.internal.view.menu.MenuView;
+import com.android.internal.view.menu.SubMenuBuilder;
/**
* @hide
@@ -188,34 +184,8 @@
ActionBar.NAVIGATION_MODE_STANDARD);
mTitle = a.getText(R.styleable.ActionBar_title);
mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
-
mLogo = a.getDrawable(R.styleable.ActionBar_logo);
- if (mLogo == null) {
- if (context instanceof Activity) {
- try {
- mLogo = pm.getActivityLogo(((Activity) context).getComponentName());
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Activity component name not found!", e);
- }
- }
- if (mLogo == null) {
- mLogo = appInfo.loadLogo(pm);
- }
- }
-
mIcon = a.getDrawable(R.styleable.ActionBar_icon);
- if (mIcon == null) {
- if (context instanceof Activity) {
- try {
- mIcon = pm.getActivityIcon(((Activity) context).getComponentName());
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Activity component name not found!", e);
- }
- }
- if (mIcon == null) {
- mIcon = appInfo.loadIcon(pm);
- }
- }
final LayoutInflater inflater = LayoutInflater.from(context);
@@ -729,7 +699,11 @@
}
public void setIcon(int resId) {
- setIcon(mContext.getResources().getDrawable(resId));
+ setIcon(resId != 0 ? mContext.getResources().getDrawable(resId) : null);
+ }
+
+ public boolean hasIcon() {
+ return mIcon != null;
}
public void setLogo(Drawable logo) {
@@ -740,7 +714,11 @@
}
public void setLogo(int resId) {
- setLogo(mContext.getResources().getDrawable(resId));
+ setLogo(resId != 0 ? mContext.getResources().getDrawable(resId) : null);
+ }
+
+ public boolean hasLogo() {
+ return mLogo != null;
}
public void setNavigationMode(int mode) {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d3ead26..521ba81 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import android.Manifest;
import android.app.ActivityManagerNative;
import android.app.admin.DevicePolicyManager;
import android.appwidget.AppWidgetManager;
@@ -149,6 +150,8 @@
private DevicePolicyManager mDevicePolicyManager;
private ILockSettings mLockSettingsService;
+ private final boolean mMultiUserMode;
+
// The current user is set by KeyguardViewMediator and shared by all LockPatternUtils.
private static volatile int sCurrentUserId = UserHandle.USER_NULL;
@@ -170,6 +173,12 @@
public LockPatternUtils(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+
+ // If this is being called by the system or by an application like keyguard that
+ // has permision INTERACT_ACROSS_USERS, then LockPatternUtils will operate in multi-user
+ // mode where calls are for the current user rather than the user of the calling process.
+ mMultiUserMode = context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL) == PackageManager.PERMISSION_GRANTED;
}
private ILockSettings getLockSettings() {
@@ -264,13 +273,12 @@
}
private int getCurrentOrCallingUserId() {
- int callingUid = Binder.getCallingUid();
- if (callingUid == android.os.Process.SYSTEM_UID) {
+ if (mMultiUserMode) {
// TODO: This is a little inefficient. See if all users of this are able to
// handle USER_CURRENT and pass that instead.
return getCurrentUser();
} else {
- return UserHandle.getUserId(callingUid);
+ return UserHandle.getCallingUserId();
}
}
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index f10a2e8..d82831f 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -46,6 +46,7 @@
// Trace of previous points.
private float[] mTraceX = new float[32];
private float[] mTraceY = new float[32];
+ private boolean[] mTraceCurrent = new boolean[32];
private int mTraceCount;
// True if the pointer is down.
@@ -76,7 +77,7 @@
mTraceCount = 0;
}
- public void addTrace(float x, float y) {
+ public void addTrace(float x, float y, boolean current) {
int traceCapacity = mTraceX.length;
if (mTraceCount == traceCapacity) {
traceCapacity *= 2;
@@ -87,10 +88,15 @@
float[] newTraceY = new float[traceCapacity];
System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
mTraceY = newTraceY;
+
+ boolean[] newTraceCurrent = new boolean[traceCapacity];
+ System.arraycopy(mTraceCurrent, 0, newTraceCurrent, 0, mTraceCount);
+ mTraceCurrent= newTraceCurrent;
}
mTraceX[mTraceCount] = x;
mTraceY[mTraceCount] = y;
+ mTraceCurrent[mTraceCount] = current;
mTraceCount += 1;
}
}
@@ -106,6 +112,7 @@
private final Paint mTextBackgroundPaint;
private final Paint mTextLevelPaint;
private final Paint mPaint;
+ private final Paint mCurrentPointPaint;
private final Paint mTargetPaint;
private final Paint mPathPaint;
private final FontMetricsInt mTextMetrics = new FontMetricsInt();
@@ -147,6 +154,11 @@
mPaint.setARGB(255, 255, 255, 255);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
+ mCurrentPointPaint = new Paint();
+ mCurrentPointPaint.setAntiAlias(true);
+ mCurrentPointPaint.setARGB(255, 255, 0, 0);
+ mCurrentPointPaint.setStyle(Paint.Style.STROKE);
+ mCurrentPointPaint.setStrokeWidth(2);
mTargetPaint = new Paint();
mTargetPaint.setAntiAlias(false);
mTargetPaint.setARGB(255, 0, 0, 192);
@@ -294,7 +306,8 @@
}
if (haveLast) {
canvas.drawLine(lastX, lastY, x, y, mPathPaint);
- canvas.drawPoint(lastX, lastY, mPaint);
+ final Paint paint = ps.mTraceCurrent[i] ? mCurrentPointPaint : mPaint;
+ canvas.drawPoint(lastX, lastY, paint);
drawn = true;
}
lastX = x;
@@ -549,8 +562,9 @@
final PointerState ps = mPointers.get(id);
ps.mCurDown = true;
- ps.mHasBoundingBox = (InputDevice.getDevice(event.getDeviceId()).
- getMotionRange(MotionEvent.AXIS_GENERIC_1) != null);
+ InputDevice device = InputDevice.getDevice(event.getDeviceId());
+ ps.mHasBoundingBox = device != null &&
+ device.getMotionRange(MotionEvent.AXIS_GENERIC_1) != null;
}
final int NI = event.getPointerCount();
@@ -573,7 +587,7 @@
logCoords("Pointer", action, i, coords, id, event);
}
if (ps != null) {
- ps.addTrace(coords.x, coords.y);
+ ps.addTrace(coords.x, coords.y, false);
}
}
}
@@ -586,7 +600,7 @@
logCoords("Pointer", action, i, coords, id, event);
}
if (ps != null) {
- ps.addTrace(coords.x, coords.y);
+ ps.addTrace(coords.x, coords.y, true);
ps.mXVelocity = mVelocity.getXVelocity(id);
ps.mYVelocity = mVelocity.getYVelocity(id);
mVelocity.getEstimator(id, ps.mEstimator);
@@ -625,7 +639,7 @@
if (mActivePointerId == id) {
mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
}
- ps.addTrace(Float.NaN, Float.NaN);
+ ps.addTrace(Float.NaN, Float.NaN, false);
}
}
diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java
deleted file mode 100644
index ca797eb..0000000
--- a/core/java/com/android/internal/widget/TransportControlView.java
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import java.lang.ref.WeakReference;
-
-import com.android.internal.widget.LockScreenWidgetCallback;
-import com.android.internal.widget.LockScreenWidgetInterface;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.media.AudioManager;
-import android.media.MediaMetadataRetriever;
-import android.media.RemoteControlClient;
-import android.media.IRemoteControlDisplay;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.text.Spannable;
-import android.text.TextUtils;
-import android.text.style.ForegroundColorSpan;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-
-import com.android.internal.R;
-
-public class TransportControlView extends FrameLayout implements OnClickListener,
- LockScreenWidgetInterface {
-
- private static final int MSG_UPDATE_STATE = 100;
- private static final int MSG_SET_METADATA = 101;
- private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
- private static final int MSG_SET_ARTWORK = 103;
- private static final int MSG_SET_GENERATION_ID = 104;
- private static final int MAXDIM = 512;
- private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
- protected static final boolean DEBUG = false;
- protected static final String TAG = "TransportControlView";
-
- private ImageView mAlbumArt;
- private TextView mTrackTitle;
- private ImageView mBtnPrev;
- private ImageView mBtnPlay;
- private ImageView mBtnNext;
- private int mClientGeneration;
- private Metadata mMetadata = new Metadata();
- private boolean mAttached;
- private PendingIntent mClientIntent;
- private int mTransportControlFlags;
- private int mCurrentPlayState;
- private AudioManager mAudioManager;
- private LockScreenWidgetCallback mWidgetCallbacks;
- private IRemoteControlDisplayWeak mIRCD;
-
- /**
- * The metadata which should be populated into the view once we've been attached
- */
- private Bundle mPopulateMetadataWhenAttached = null;
-
- // This handler is required to ensure messages from IRCD are handled in sequence and on
- // the UI thread.
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_STATE:
- if (mClientGeneration == msg.arg1) updatePlayPauseState(msg.arg2);
- break;
-
- case MSG_SET_METADATA:
- if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj);
- break;
-
- case MSG_SET_TRANSPORT_CONTROLS:
- if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2);
- break;
-
- case MSG_SET_ARTWORK:
- if (mClientGeneration == msg.arg1) {
- if (mMetadata.bitmap != null) {
- mMetadata.bitmap.recycle();
- }
- mMetadata.bitmap = (Bitmap) msg.obj;
- mAlbumArt.setImageBitmap(mMetadata.bitmap);
- }
- break;
-
- case MSG_SET_GENERATION_ID:
- if (msg.arg2 != 0) {
- // This means nobody is currently registered. Hide the view.
- if (mWidgetCallbacks != null) {
- mWidgetCallbacks.requestHide(TransportControlView.this);
- }
- }
- if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
- mClientGeneration = msg.arg1;
- mClientIntent = (PendingIntent) msg.obj;
- break;
-
- }
- }
- };
-
- /**
- * This class is required to have weak linkage to the current TransportControlView
- * because the remote process can hold a strong reference to this binder object and
- * we can't predict when it will be GC'd in the remote process. Without this code, it
- * would allow a heavyweight object to be held on this side of the binder when there's
- * no requirement to run a GC on the other side.
- */
- private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub {
- private WeakReference<Handler> mLocalHandler;
-
- IRemoteControlDisplayWeak(Handler handler) {
- mLocalHandler = new WeakReference<Handler>(handler);
- }
-
- public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
- long currentPosMs, float speed) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
- }
- }
-
- public void setMetadata(int generationId, Bundle metadata) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
- }
- }
-
- public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags)
- .sendToTarget();
- }
- }
-
- public void setArtwork(int generationId, Bitmap bitmap) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
- }
- }
-
- public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
- handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
- }
- }
-
- public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
- boolean clearing) throws RemoteException {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_GENERATION_ID,
- clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
- }
- }
- };
-
- public TransportControlView(Context context, AttributeSet attrs) {
- super(context, attrs);
- if (DEBUG) Log.v(TAG, "Create TCV " + this);
- mAudioManager = new AudioManager(mContext);
- mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
- mIRCD = new IRemoteControlDisplayWeak(mHandler);
- }
-
- private void updateTransportControls(int transportControlFlags) {
- mTransportControlFlags = transportControlFlags;
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
- mTrackTitle = (TextView) findViewById(R.id.title);
- mTrackTitle.setSelected(true); // enable marquee
- mAlbumArt = (ImageView) findViewById(R.id.albumart);
- mBtnPrev = (ImageView) findViewById(R.id.btn_prev);
- mBtnPlay = (ImageView) findViewById(R.id.btn_play);
- mBtnNext = (ImageView) findViewById(R.id.btn_next);
- final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext };
- for (View view : buttons) {
- view.setOnClickListener(this);
- }
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mPopulateMetadataWhenAttached != null) {
- updateMetadata(mPopulateMetadataWhenAttached);
- mPopulateMetadataWhenAttached = null;
- }
- if (!mAttached) {
- if (DEBUG) Log.v(TAG, "Registering TCV " + this);
- mAudioManager.registerRemoteControlDisplay(mIRCD);
- }
- mAttached = true;
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAttached) {
- if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
- mAudioManager.unregisterRemoteControlDisplay(mIRCD);
- }
- mAttached = false;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
-// Log.v(TAG, "setting max bitmap size: " + dim + "x" + dim);
-// mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
- }
-
- class Metadata {
- private String artist;
- private String trackTitle;
- private String albumTitle;
- private Bitmap bitmap;
-
- public String toString() {
- return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + albumTitle + "]";
- }
- }
-
- private String getMdString(Bundle data, int id) {
- return data.getString(Integer.toString(id));
- }
-
- private void updateMetadata(Bundle data) {
- if (mAttached) {
- mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
- mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
- mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
- populateMetadata();
- } else {
- mPopulateMetadataWhenAttached = data;
- }
- }
-
- /**
- * Populates the given metadata into the view
- */
- private void populateMetadata() {
- StringBuilder sb = new StringBuilder();
- int trackTitleLength = 0;
- if (!TextUtils.isEmpty(mMetadata.trackTitle)) {
- sb.append(mMetadata.trackTitle);
- trackTitleLength = mMetadata.trackTitle.length();
- }
- if (!TextUtils.isEmpty(mMetadata.artist)) {
- if (sb.length() != 0) {
- sb.append(" - ");
- }
- sb.append(mMetadata.artist);
- }
- if (!TextUtils.isEmpty(mMetadata.albumTitle)) {
- if (sb.length() != 0) {
- sb.append(" - ");
- }
- sb.append(mMetadata.albumTitle);
- }
- mTrackTitle.setText(sb.toString(), TextView.BufferType.SPANNABLE);
- Spannable str = (Spannable) mTrackTitle.getText();
- if (trackTitleLength != 0) {
- str.setSpan(new ForegroundColorSpan(0xffffffff), 0, trackTitleLength,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- trackTitleLength++;
- }
- if (sb.length() > trackTitleLength) {
- str.setSpan(new ForegroundColorSpan(0x7fffffff), trackTitleLength, sb.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
-
- mAlbumArt.setImageBitmap(mMetadata.bitmap);
- final int flags = mTransportControlFlags;
- setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
- setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
- setVisibilityBasedOnFlag(mBtnPlay, flags,
- RemoteControlClient.FLAG_KEY_MEDIA_PLAY
- | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
- | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
- | RemoteControlClient.FLAG_KEY_MEDIA_STOP);
-
- updatePlayPauseState(mCurrentPlayState);
- }
-
- private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
- if ((flags & flag) != 0) {
- view.setVisibility(View.VISIBLE);
- } else {
- view.setVisibility(View.GONE);
- }
- }
-
- private void updatePlayPauseState(int state) {
- if (DEBUG) Log.v(TAG,
- "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state);
- if (state == mCurrentPlayState) {
- return;
- }
- final int imageResId;
- final int imageDescId;
- boolean showIfHidden = false;
- switch (state) {
- case RemoteControlClient.PLAYSTATE_ERROR:
- imageResId = com.android.internal.R.drawable.stat_sys_warning;
- // TODO use more specific image description string for warning, but here the "play"
- // message is still valid because this button triggers a play command.
- imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
- break;
-
- case RemoteControlClient.PLAYSTATE_PLAYING:
- imageResId = com.android.internal.R.drawable.ic_media_pause;
- imageDescId = com.android.internal.R.string.lockscreen_transport_pause_description;
- showIfHidden = true;
- break;
-
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- imageResId = com.android.internal.R.drawable.ic_media_stop;
- imageDescId = com.android.internal.R.string.lockscreen_transport_stop_description;
- showIfHidden = true;
- break;
-
- case RemoteControlClient.PLAYSTATE_PAUSED:
- default:
- imageResId = com.android.internal.R.drawable.ic_media_play;
- imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
- showIfHidden = false;
- break;
- }
- mBtnPlay.setImageResource(imageResId);
- mBtnPlay.setContentDescription(getResources().getString(imageDescId));
- if (showIfHidden && mWidgetCallbacks != null && !mWidgetCallbacks.isVisible(this)) {
- mWidgetCallbacks.requestShow(this);
- }
- mCurrentPlayState = state;
- }
-
- static class SavedState extends BaseSavedState {
- boolean wasShowing;
-
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- private SavedState(Parcel in) {
- super(in);
- this.wasShowing = in.readInt() != 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(this.wasShowing ? 1 : 0);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- if (DEBUG) Log.v(TAG, "onSaveInstanceState()");
- Parcelable superState = super.onSaveInstanceState();
- SavedState ss = new SavedState(superState);
- ss.wasShowing = mWidgetCallbacks != null && mWidgetCallbacks.isVisible(this);
- return ss;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- if (DEBUG) Log.v(TAG, "onRestoreInstanceState()");
- if (!(state instanceof SavedState)) {
- super.onRestoreInstanceState(state);
- return;
- }
- SavedState ss = (SavedState) state;
- super.onRestoreInstanceState(ss.getSuperState());
- if (ss.wasShowing && mWidgetCallbacks != null) {
- mWidgetCallbacks.requestShow(this);
- }
- }
-
- public void onClick(View v) {
- int keyCode = -1;
- if (v == mBtnPrev) {
- keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
- } else if (v == mBtnNext) {
- keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
- } else if (v == mBtnPlay) {
- keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
-
- }
- if (keyCode != -1) {
- sendMediaButtonClick(keyCode);
- if (mWidgetCallbacks != null) {
- mWidgetCallbacks.userActivity(this);
- }
- }
- }
-
- private void sendMediaButtonClick(int keyCode) {
- if (mClientIntent == null) {
- // Shouldn't be possible because this view should be hidden in this case.
- Log.e(TAG, "sendMediaButtonClick(): No client is currently registered");
- return;
- }
- // use the registered PendingIntent that will be processed by the registered
- // media button event receiver, which is the component of mClientIntent
- KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
- Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- try {
- mClientIntent.send(getContext(), 0, intent);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending intent for media button down: "+e);
- e.printStackTrace();
- }
-
- keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
- intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- try {
- mClientIntent.send(getContext(), 0, intent);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending intent for media button up: "+e);
- e.printStackTrace();
- }
- }
-
- public void setCallback(LockScreenWidgetCallback callback) {
- mWidgetCallbacks = callback;
- }
-
- public boolean providesClock() {
- return false;
- }
-
- private boolean wasPlayingRecently(int state, long stateChangeTimeMs) {
- switch (state) {
- case RemoteControlClient.PLAYSTATE_PLAYING:
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- case RemoteControlClient.PLAYSTATE_REWINDING:
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- // actively playing or about to play
- return true;
- case RemoteControlClient.PLAYSTATE_NONE:
- return false;
- case RemoteControlClient.PLAYSTATE_STOPPED:
- case RemoteControlClient.PLAYSTATE_PAUSED:
- case RemoteControlClient.PLAYSTATE_ERROR:
- // we have stopped playing, check how long ago
- if (DEBUG) {
- if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) {
- Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently");
- } else {
- Log.v(TAG, "wasPlayingRecently: time > TIMEOUT");
- }
- }
- return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS);
- default:
- Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()");
- return false;
- }
- }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
index 30f5f2f..16bec16 100644
--- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
+++ b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
@@ -46,36 +46,6 @@
private boolean mEnabled = true;
private final int mResourceId;
- /* package */ static class DrawableWithAlpha extends Drawable {
- private float mAlpha = 1.0f;
- private Drawable mRealDrawable;
- public DrawableWithAlpha(Drawable realDrawable) {
- mRealDrawable = realDrawable;
- }
- public void setAlpha(float alpha) {
- mAlpha = alpha;
- }
- public float getAlpha() {
- return mAlpha;
- }
- public void draw(Canvas canvas) {
- mRealDrawable.setAlpha((int) Math.round(mAlpha * 255f));
- mRealDrawable.draw(canvas);
- }
- @Override
- public void setAlpha(int alpha) {
- mRealDrawable.setAlpha(alpha);
- }
- @Override
- public void setColorFilter(ColorFilter cf) {
- mRealDrawable.setColorFilter(cf);
- }
- @Override
- public int getOpacity() {
- return mRealDrawable.getOpacity();
- }
- }
-
public TargetDrawable(Resources res, int resId) {
mResourceId = resId;
setDrawable(res, resId);
diff --git a/core/java/com/google/android/collect/Maps.java b/core/java/com/google/android/collect/Maps.java
index d537e0c..fc2c9fe 100644
--- a/core/java/com/google/android/collect/Maps.java
+++ b/core/java/com/google/android/collect/Maps.java
@@ -16,6 +16,8 @@
package com.google.android.collect;
+import android.util.ArrayMap;
+
import java.util.HashMap;
/**
@@ -30,4 +32,11 @@
public static <K, V> HashMap<K, V> newHashMap() {
return new HashMap<K, V>();
}
+
+ /**
+ * Creates a {@code ArrayMap} instance.
+ */
+ public static <K, V> ArrayMap<K, V> newArrayMap() {
+ return new ArrayMap<K, V>();
+ }
}
diff --git a/core/java/com/google/android/collect/Sets.java b/core/java/com/google/android/collect/Sets.java
index fbfbe50..dd3cab1 100644
--- a/core/java/com/google/android/collect/Sets.java
+++ b/core/java/com/google/android/collect/Sets.java
@@ -16,6 +16,8 @@
package com.google.android.collect;
+import android.util.ArraySet;
+
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
@@ -90,4 +92,20 @@
return set;
}
+ /**
+ * Creates a {@code ArraySet} instance.
+ */
+ public static <E> ArraySet<E> newArraySet() {
+ return new ArraySet<E>();
+ }
+
+ /**
+ * Creates a {@code ArraySet} instance containing the given elements.
+ */
+ public static <E> ArraySet<E> newArraySet(E... elements) {
+ int capacity = elements.length * 4 / 3 + 1;
+ ArraySet<E> set = new ArraySet<E>(capacity);
+ Collections.addAll(set, elements);
+ return set;
+ }
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 337c1ec..20e5011 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -17,6 +17,10 @@
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+# When built as part of the system image we can enable certian non-NDK compliant
+# Skia optimizations.
+LOCAL_CFLAGS += -DSK_BUILD_FOR_ANDROID_FRAMEWORK
+
LOCAL_SRC_FILES:= \
AndroidRuntime.cpp \
Time.cpp \
@@ -51,6 +55,7 @@
android_view_KeyEvent.cpp \
android_view_KeyCharacterMap.cpp \
android_view_HardwareRenderer.cpp \
+ android_view_GraphicBuffer.cpp \
android_view_GLES20DisplayList.cpp \
android_view_GLES20Canvas.cpp \
android_view_MotionEvent.cpp \
@@ -126,6 +131,7 @@
android_media_RemoteDisplay.cpp \
android_media_ToneGenerator.cpp \
android_hardware_Camera.cpp \
+ android_hardware_photography_CameraMetadata.cpp \
android_hardware_SensorManager.cpp \
android_hardware_SerialPort.cpp \
android_hardware_UsbDevice.cpp \
@@ -135,6 +141,7 @@
android_util_FileObserver.cpp \
android/opengl/poly_clip.cpp.arm \
android/opengl/util.cpp.arm \
+ android/print/android_print_pdf_PdfDocument.cpp \
android_server_NetworkManagementSocketTagger.cpp \
android_server_Watchdog.cpp \
android_ddm_DdmHandleNativeHeap.cpp \
@@ -158,11 +165,9 @@
$(call include-path-for, libhardware)/hardware \
$(call include-path-for, libhardware_legacy)/hardware_legacy \
$(TOP)/frameworks/av/include \
- external/skia/include/core \
- external/skia/include/effects \
- external/skia/include/images \
- external/skia/include/ports \
+ $(TOP)/system/media/camera/include \
external/skia/src/core \
+ external/skia/src/pdf \
external/skia/src/images \
external/skia/include/utils \
external/sqlite/dist \
@@ -189,10 +194,10 @@
libnetutils \
libui \
libgui \
+ libinput \
libcamera_client \
libskia \
libsqlite \
- libdvm \
libEGL \
libGLESv1_CM \
libGLESv2 \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ca658da..1299579 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -24,7 +24,6 @@
#include <utils/Log.h>
#include <utils/misc.h>
#include <binder/Parcel.h>
-#include <utils/StringArray.h>
#include <utils/threads.h>
#include <cutils/properties.h>
@@ -34,6 +33,7 @@
#include "jni.h"
#include "JNIHelp.h"
+#include "JniInvocation.h"
#include "android_util_Binder.h"
#include <stdio.h>
@@ -76,6 +76,7 @@
extern int register_android_opengl_jni_GLES30(JNIEnv* env);
extern int register_android_hardware_Camera(JNIEnv *env);
+extern int register_android_hardware_photography_CameraMetadata(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);
@@ -117,6 +118,7 @@
extern int register_android_graphics_Xfermode(JNIEnv* env);
extern int register_android_graphics_PixelFormat(JNIEnv* env);
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
+extern int register_android_view_GraphicBuffer(JNIEnv* env);
extern int register_android_view_GLES20DisplayList(JNIEnv* env);
extern int register_android_view_GLES20Canvas(JNIEnv* env);
extern int register_android_view_HardwareRenderer(JNIEnv* env);
@@ -143,6 +145,7 @@
extern int register_android_os_FileUtils(JNIEnv *env);
extern int register_android_os_UEventObserver(JNIEnv* env);
extern int register_android_os_MemoryFile(JNIEnv* env);
+extern int register_android_print_pdf_PdfDocument(JNIEnv* env);
extern int register_android_net_LocalSocketImpl(JNIEnv* env);
extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_net_TrafficStats(JNIEnv* env);
@@ -460,6 +463,7 @@
char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX];
char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX];
char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
+ char jitcodecachesizeOptsBuf[sizeof("-Xjitcodecachesize:")-1 + PROPERTY_VALUE_MAX];
char extraOptsBuf[PROPERTY_VALUE_MAX];
char* stackTraceFile = NULL;
bool checkJni = false;
@@ -551,6 +555,14 @@
opt.optionString = "-XX:mainThreadStackSize=24K";
mOptions.add(opt);
+ // Set the max jit code cache size. Note: size of 0 will disable the JIT.
+ strcpy(jitcodecachesizeOptsBuf, "-Xjitcodecachesize:");
+ property_get("dalvik.vm.jit.codecachesize", jitcodecachesizeOptsBuf+19, NULL);
+ if (jitcodecachesizeOptsBuf[19] != '\0') {
+ opt.optionString = jitcodecachesizeOptsBuf;
+ mOptions.add(opt);
+ }
+
strcpy(heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
property_get("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf+20, "");
if (heapgrowthlimitOptsBuf[20] != '\0') {
@@ -831,6 +843,8 @@
//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
/* start the virtual machine */
+ JniInvocation jni_invocation;
+ jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) {
return;
@@ -1112,6 +1126,7 @@
REG_JNI(register_android_nio_utils),
REG_JNI(register_android_graphics_PixelFormat),
REG_JNI(register_android_graphics_Graphics),
+ REG_JNI(register_android_view_GraphicBuffer),
REG_JNI(register_android_view_GLES20DisplayList),
REG_JNI(register_android_view_GLES20Canvas),
REG_JNI(register_android_view_HardwareRenderer),
@@ -1169,6 +1184,7 @@
REG_JNI(register_android_os_SELinux),
REG_JNI(register_android_os_Trace),
REG_JNI(register_android_os_UEventObserver),
+ REG_JNI(register_android_print_pdf_PdfDocument),
REG_JNI(register_android_net_LocalSocketImpl),
REG_JNI(register_android_net_NetworkUtils),
REG_JNI(register_android_net_TrafficStats),
@@ -1176,6 +1192,7 @@
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_android_hardware_Camera),
+ REG_JNI(register_android_hardware_photography_CameraMetadata),
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/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index daabce3..016c11e 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -29,6 +29,10 @@
jfieldID gOptions_purgeableFieldID;
jfieldID gOptions_shareableFieldID;
jfieldID gOptions_preferQualityOverSpeedFieldID;
+jfieldID gOptions_scaledFieldID;
+jfieldID gOptions_densityFieldID;
+jfieldID gOptions_screenDensityFieldID;
+jfieldID gOptions_targetDensityFieldID;
jfieldID gOptions_widthFieldID;
jfieldID gOptions_heightFieldID;
jfieldID gOptions_mimeFieldID;
@@ -152,12 +156,75 @@
return pr;
}
+static SkBitmap::Config configForScaledOutput(SkBitmap::Config config) {
+ switch (config) {
+ case SkBitmap::kNo_Config:
+ case SkBitmap::kIndex8_Config:
+ case SkBitmap::kRLE_Index8_Config:
+ return SkBitmap::kARGB_8888_Config;
+ default:
+ break;
+ }
+ return config;
+}
+
+class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
+public:
+ ScaleCheckingAllocator(float scale, int size)
+ : mScale(scale), mSize(size) {
+ }
+
+ virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
+ // accounts for scale in final allocation, using eventual size and config
+ const int bytesPerPixel = SkBitmap::ComputeBytesPerPixel(
+ configForScaledOutput(bitmap->getConfig()));
+ const int requestedSize = bytesPerPixel *
+ int(bitmap->width() * mScale + 0.5f) *
+ int(bitmap->height() * mScale + 0.5f);
+ if (requestedSize > mSize) {
+ ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
+ mSize, requestedSize);
+ return false;
+ }
+ return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable);
+ }
+private:
+ const float mScale;
+ const int mSize;
+};
+
+class RecyclingPixelAllocator : public SkBitmap::Allocator {
+public:
+ RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size)
+ : mPixelRef(pixelRef), mSize(size) {
+ SkSafeRef(mPixelRef);
+ }
+
+ ~RecyclingPixelAllocator() {
+ SkSafeUnref(mPixelRef);
+ }
+
+ virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
+ if (!bitmap->getSize64().is32() || bitmap->getSize() > mSize) {
+ ALOGW("bitmap marked for reuse (%d bytes) can't fit new bitmap (%d bytes)",
+ mSize, bitmap->getSize());
+ return false;
+ }
+ bitmap->setPixelRef(mPixelRef);
+ bitmap->lockPixels();
+ return true;
+ }
+
+private:
+ SkPixelRef* const mPixelRef;
+ const unsigned int mSize;
+};
+
// since we "may" create a purgeable imageref, we require the stream be ref'able
// i.e. dynamically allocated, since its lifetime may exceed the current stack
// frame.
static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
- jobject options, bool allowPurgeable, bool forcePurgeable = false,
- bool applyScale = false, float scale = 1.0f) {
+ jobject options, bool allowPurgeable, bool forcePurgeable = false) {
int sampleSize = 1;
@@ -166,9 +233,8 @@
bool doDither = true;
bool isMutable = false;
- bool willScale = applyScale && scale != 1.0f;
- bool isPurgeable = !willScale &&
- (forcePurgeable || (allowPurgeable && optionsPurgeable(env, options)));
+ float scale = 1.0f;
+ bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
bool preferQualityOverSpeed = false;
jobject javaBitmap = NULL;
@@ -191,11 +257,19 @@
preferQualityOverSpeed = env->GetBooleanField(options,
gOptions_preferQualityOverSpeedFieldID);
javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
+
+ if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
+ const int density = env->GetIntField(options, gOptions_densityFieldID);
+ const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
+ const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
+ if (density != 0 && targetDensity != 0 && density != screenDensity) {
+ scale = (float) targetDensity / density;
+ }
+ }
}
- if (willScale && javaBitmap != NULL) {
- return nullObjectReturn("Cannot pre-scale a reused bitmap");
- }
+ const bool willScale = scale != 1.0f;
+ isPurgeable &= !willScale;
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
if (decoder == NULL) {
@@ -206,38 +280,47 @@
decoder->setDitherImage(doDither);
decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
- NinePatchPeeker peeker(decoder);
- JavaPixelAllocator javaAllocator(env);
-
- SkBitmap* bitmap;
- bool useExistingBitmap = false;
- if (javaBitmap == NULL) {
- bitmap = new SkBitmap;
- } else {
- if (sampleSize != 1) {
- return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1");
- }
-
- bitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
- // only reuse the provided bitmap if it is immutable
- if (!bitmap->isImmutable()) {
- useExistingBitmap = true;
- // config of supplied bitmap overrules config set in options
- prefConfig = bitmap->getConfig();
- } else {
+ SkBitmap* outputBitmap = NULL;
+ unsigned int existingBufferSize = 0;
+ if (javaBitmap != NULL) {
+ outputBitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
+ if (outputBitmap->isImmutable()) {
ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
- bitmap = new SkBitmap;
+ javaBitmap = NULL;
+ outputBitmap = NULL;
+ } else {
+ existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
}
}
- SkAutoTDelete<SkImageDecoder> add(decoder);
- SkAutoTDelete<SkBitmap> adb(!useExistingBitmap ? bitmap : NULL);
+ SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL);
+ if (outputBitmap == NULL) outputBitmap = adb.get();
+ NinePatchPeeker peeker(decoder);
decoder->setPeeker(&peeker);
- if (!isPurgeable) {
- decoder->setAllocator(&javaAllocator);
+
+ SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode;
+
+ JavaPixelAllocator javaAllocator(env);
+ RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
+ ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
+ SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
+ (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
+ if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
+ if (!willScale) {
+ decoder->setAllocator(outputAllocator);
+ } else if (javaBitmap != NULL) {
+ // check for eventual scaled bounds at allocation time, so we don't decode the bitmap
+ // only to find the scaled result too large to fit in the allocation
+ decoder->setAllocator(&scaleCheckingAllocator);
+ }
}
+ // Only setup the decoder to be deleted after its stack-based, refcounted
+ // components (allocators, peekers, etc) are declared. This prevents RefCnt
+ // asserts from firing due to the order objects are deleted from the stack.
+ SkAutoTDelete<SkImageDecoder> add(decoder);
+
AutoDecoderCancel adc(options, decoder);
// To fix the race condition in case "requestCancelDecode"
@@ -247,25 +330,13 @@
return nullObjectReturn("gOptions_mCancelID");
}
- SkImageDecoder::Mode decodeMode = mode;
- if (isPurgeable) {
- decodeMode = SkImageDecoder::kDecodeBounds_Mode;
- }
-
- SkBitmap* decoded;
- if (willScale) {
- decoded = new SkBitmap;
- } else {
- decoded = bitmap;
- }
- SkAutoTDelete<SkBitmap> adb2(willScale ? decoded : NULL);
-
- if (!decoder->decode(stream, decoded, prefConfig, decodeMode, javaBitmap != NULL)) {
+ SkBitmap decodingBitmap;
+ if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) {
return nullObjectReturn("decoder->decode returned false");
}
- int scaledWidth = decoded->width();
- int scaledHeight = decoded->height();
+ int scaledWidth = decodingBitmap.width();
+ int scaledHeight = decodingBitmap.height();
if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
scaledWidth = int(scaledWidth * scale + 0.5f);
@@ -333,33 +404,26 @@
// Dalvik code has always behaved. We simply recreate the behavior here.
// The result is slightly different from simply using scale because of
// the 0.5f rounding bias applied when computing the target image size
- const float sx = scaledWidth / float(decoded->width());
- const float sy = scaledHeight / float(decoded->height());
+ const float sx = scaledWidth / float(decodingBitmap.width());
+ const float sy = scaledHeight / float(decodingBitmap.height());
- SkBitmap::Config config = decoded->config();
- switch (config) {
- case SkBitmap::kNo_Config:
- case SkBitmap::kIndex8_Config:
- case SkBitmap::kRLE_Index8_Config:
- config = SkBitmap::kARGB_8888_Config;
- break;
- default:
- break;
- }
-
- bitmap->setConfig(config, scaledWidth, scaledHeight);
- bitmap->setIsOpaque(decoded->isOpaque());
- if (!bitmap->allocPixels(&javaAllocator, NULL)) {
+ // TODO: avoid copying when scaled size equals decodingBitmap size
+ SkBitmap::Config config = configForScaledOutput(decodingBitmap.config());
+ outputBitmap->setConfig(config, scaledWidth, scaledHeight);
+ outputBitmap->setIsOpaque(decodingBitmap.isOpaque());
+ if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
return nullObjectReturn("allocation failed for scaled bitmap");
}
- bitmap->eraseColor(0);
+ outputBitmap->eraseColor(0);
SkPaint paint;
paint.setFilterBitmap(true);
- SkCanvas canvas(*bitmap);
+ SkCanvas canvas(*outputBitmap);
canvas.scale(sx, sy);
- canvas.drawBitmap(*decoded, 0.0f, 0.0f, &paint);
+ canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
+ } else {
+ outputBitmap->swap(decodingBitmap);
}
if (padding) {
@@ -374,17 +438,17 @@
SkPixelRef* pr;
if (isPurgeable) {
- pr = installPixelRef(bitmap, stream, sampleSize, doDither);
+ pr = installPixelRef(outputBitmap, stream, sampleSize, doDither);
} else {
// if we get here, we're in kDecodePixels_Mode and will therefore
// already have a pixelref installed.
- pr = bitmap->pixelRef();
+ pr = outputBitmap->pixelRef();
}
if (pr == NULL) {
return nullObjectReturn("Got null SkPixelRef");
}
- if (!isMutable && !useExistingBitmap) {
+ if (!isMutable && javaBitmap == NULL) {
// promise we will never change our pixels (great for sharing and pictures)
pr->setImmutable();
}
@@ -392,35 +456,31 @@
// detach bitmap from its autodeleter, since we want to own it now
adb.detach();
- if (useExistingBitmap) {
+ if (javaBitmap != NULL) {
+ GraphicsJNI::reinitBitmap(env, javaBitmap);
+ outputBitmap->notifyPixelsChanged();
// If a java bitmap was passed in for reuse, pass it back
return javaBitmap;
}
// now create the java bitmap
- return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(),
+ return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
isMutable, ninePatchChunk, layoutBounds, -1);
}
-static jobject nativeDecodeStreamScaled(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
- jobject padding, jobject options, jboolean applyScale, jfloat scale) {
+static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
+ jobject padding, jobject options) {
jobject bitmap = NULL;
SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
if (stream) {
// for now we don't allow purgeable with java inputstreams
- bitmap = doDecode(env, stream, padding, options, false, false, applyScale, scale);
+ bitmap = doDecode(env, stream, padding, options, false, false);
stream->unref();
}
return bitmap;
}
-static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
- jobject padding, jobject options) {
-
- return nativeDecodeStreamScaled(env, clazz, is, storage, padding, options, false, 1.0f);
-}
-
static ssize_t getFDSize(int fd) {
off64_t curr = ::lseek64(fd, 0, SEEK_CUR);
if (curr < 0) {
@@ -495,8 +555,8 @@
return stream;
}
-static jobject nativeDecodeAssetScaled(JNIEnv* env, jobject clazz, jint native_asset,
- jobject padding, jobject options, jboolean applyScale, jfloat scale) {
+static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset,
+ jobject padding, jobject options) {
SkStream* stream;
Asset* asset = reinterpret_cast<Asset*>(native_asset);
@@ -514,13 +574,7 @@
stream = new AssetStreamAdaptor(asset);
}
SkAutoUnref aur(stream);
- return doDecode(env, stream, padding, options, true, forcePurgeable, applyScale, scale);
-}
-
-static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset,
- jobject padding, jobject options) {
-
- return nativeDecodeAssetScaled(env, clazz, native_asset, padding, options, false, 1.0f);
+ return doDecode(env, stream, padding, options, true, forcePurgeable);
}
static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
@@ -554,10 +608,6 @@
"(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
(void*)nativeDecodeStream
},
- { "nativeDecodeStream",
- "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;ZF)Landroid/graphics/Bitmap;",
- (void*)nativeDecodeStreamScaled
- },
{ "nativeDecodeFileDescriptor",
"(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
@@ -569,11 +619,6 @@
(void*)nativeDecodeAsset
},
- { "nativeDecodeAsset",
- "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;ZF)Landroid/graphics/Bitmap;",
- (void*)nativeDecodeAssetScaled
- },
-
{ "nativeDecodeByteArray",
"([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
(void*)nativeDecodeByteArray
@@ -616,6 +661,10 @@
gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
"inPreferQualityOverSpeed", "Z");
+ gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z");
+ gOptions_densityFieldID = getFieldIDCheck(env, options_class, "inDensity", "I");
+ gOptions_screenDensityFieldID = getFieldIDCheck(env, options_class, "inScreenDensity", "I");
+ gOptions_targetDensityFieldID = getFieldIDCheck(env, options_class, "inTargetDensity", "I");
gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index d4c7600..7c420ad 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -152,6 +152,8 @@
static jclass gBitmap_class;
static jfieldID gBitmap_nativeInstanceID;
static jmethodID gBitmap_constructorMethodID;
+static jmethodID gBitmap_reinitMethodID;
+static jmethodID gBitmap_getAllocationByteCountMethodID;
static jclass gBitmapConfig_class;
static jfieldID gBitmapConfig_nativeInstanceID;
@@ -363,6 +365,15 @@
return createBitmap(env, bitmap, NULL, isMutable, ninepatch, NULL, density);
}
+void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap)
+{
+ env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID);
+}
+
+int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap)
+{
+ return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID);
+}
jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
{
@@ -398,7 +409,7 @@
///////////////////////////////////////////////////////////////////////////////
AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj,
- SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) {
+ SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable, (storageObj == NULL)) {
SkASSERT(storage);
SkASSERT(env);
@@ -423,10 +434,6 @@
env->DeleteGlobalRef(fStorageObj);
}
fStorageObj = NULL;
-
- // Set this to NULL to prevent the SkMallocPixelRef destructor
- // from freeing the memory.
- fStorage = NULL;
}
}
@@ -588,6 +595,8 @@
gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
"(I[BZ[B[II)V");
+ gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "()V");
+ gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index c5b06f5..69b67cb 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -59,6 +59,10 @@
static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
jbyteArray ninepatch, int density = -1);
+ static void reinitBitmap(JNIEnv* env, jobject javaBitmap);
+
+ static int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap);
+
static jobject createRegion(JNIEnv* env, SkRegion* region);
static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
index ce31c5b..a75efcf 100644
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -138,16 +138,15 @@
hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData)
{
SkTypeface* typeface = reinterpret_cast<SkTypeface*>(userData);
- SkFontID uniqueID = typeface->uniqueID();
- const size_t tableSize = SkFontHost::GetTableSize(uniqueID, tag);
+ const size_t tableSize = typeface->getTableSize(tag);
if (!tableSize)
return 0;
char* buffer = reinterpret_cast<char*>(malloc(tableSize));
if (!buffer)
return 0;
- size_t actualSize = SkFontHost::GetTableData(uniqueID, tag, 0, tableSize, buffer);
+ size_t actualSize = typeface->getTableData(tag, 0, tableSize, buffer);
if (tableSize != actualSize) {
free(buffer);
return 0;
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 684b1c1..7e6aeae 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -21,22 +21,30 @@
#include <androidfw/ResourceTypes.h>
#include <utils/Log.h>
+#include <Caches.h>
+
#include "SkCanvas.h"
#include "SkRegion.h"
#include "GraphicsJNI.h"
#include "JNIHelp.h"
-extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
- const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
- const SkPaint* paint, SkRegion** outRegion);
+extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap,
+ const android::Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion);
using namespace android;
+/**
+ * IMPORTANT NOTE: 9patch chunks can be manipuated either as an array of bytes
+ * or as a Res_png_9patch instance. It is important to note that the size of the
+ * array required to hold a 9patch chunk is greater than sizeof(Res_png_9patch).
+ * The code below manipulates chunks as Res_png_9patch* types to draw and as
+ * int8_t* to allocate and free the backing storage.
+ */
+
class SkNinePatchGlue {
public:
- static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj)
- {
+ static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
if (NULL == obj) {
return false;
}
@@ -45,126 +53,110 @@
}
const jbyte* array = env->GetByteArrayElements(obj, 0);
if (array != NULL) {
- const Res_png_9patch* chunk =
- reinterpret_cast<const Res_png_9patch*>(array);
+ const Res_png_9patch* chunk = reinterpret_cast<const Res_png_9patch*>(array);
int8_t wasDeserialized = chunk->wasDeserialized;
- env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array),
- JNI_ABORT);
+ env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array), JNI_ABORT);
return wasDeserialized != -1;
}
return false;
}
- static void validateNinePatchChunk(JNIEnv* env, jobject, jint, jbyteArray obj)
- {
- if (env->GetArrayLength(obj) < (int) (sizeof(Res_png_9patch))) {
+ static int8_t* validateNinePatchChunk(JNIEnv* env, jobject, jint, jbyteArray obj) {
+ size_t chunkSize = env->GetArrayLength(obj);
+ if (chunkSize < (int) (sizeof(Res_png_9patch))) {
jniThrowRuntimeException(env, "Array too small for chunk.");
- return;
+ return NULL;
}
- // XXX Also check that dimensions are correct.
+ int8_t* storage = new int8_t[chunkSize];
+ // This call copies the content of the jbyteArray
+ env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast<jbyte*>(storage));
+ // Deserialize in place, return the array we just allocated
+ return (int8_t*) Res_png_9patch::deserialize(storage);
}
- static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds,
- const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
- jint destDensity, jint srcDensity)
- {
- size_t chunkSize = env->GetArrayLength(chunkObj);
- void* storage = alloca(chunkSize);
- env->GetByteArrayRegion(chunkObj, 0, chunkSize,
- reinterpret_cast<jbyte*>(storage));
- if (!env->ExceptionCheck()) {
- // need to deserialize the chunk
- Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage);
- assert(chunkSize == chunk->serializedSize());
- // this relies on deserialization being done in place
- Res_png_9patch::deserialize(chunk);
+ static void finalize(JNIEnv* env, jobject, int8_t* patch) {
+#ifdef USE_OPENGL_RENDERER
+ if (android::uirenderer::Caches::hasInstance()) {
+ Res_png_9patch* p = (Res_png_9patch*) patch;
+ android::uirenderer::Caches::getInstance().resourceCache.destructor(p);
+ return;
+ }
+#endif // USE_OPENGL_RENDERER
+ delete[] patch;
+ }
- if (destDensity == srcDensity || destDensity == 0
- || srcDensity == 0) {
- ALOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)",
- SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
- SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom));
- NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
- } else {
- canvas->save();
+ static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, const SkBitmap* bitmap,
+ Res_png_9patch* chunk, const SkPaint* paint, jint destDensity, jint srcDensity) {
+ if (destDensity == srcDensity || destDensity == 0 || srcDensity == 0) {
+ ALOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)",
+ SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
+ SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom));
+ NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
+ } else {
+ canvas->save();
- SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity);
- canvas->translate(bounds.fLeft, bounds.fTop);
- canvas->scale(scale, scale);
+ SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity);
+ canvas->translate(bounds.fLeft, bounds.fTop);
+ canvas->scale(scale, scale);
- bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale);
- bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale);
- bounds.fLeft = bounds.fTop = 0;
+ bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale);
+ bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale);
+ bounds.fLeft = bounds.fTop = 0;
- ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d",
- SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
- SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom),
- srcDensity, destDensity);
+ ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d",
+ SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
+ SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom),
+ srcDensity, destDensity);
- NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
+ NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
- canvas->restore();
- }
+ canvas->restore();
}
}
static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF,
- const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
- jint destDensity, jint srcDensity)
- {
+ const SkBitmap* bitmap, Res_png_9patch* chunk, const SkPaint* paint,
+ jint destDensity, jint srcDensity) {
SkASSERT(canvas);
SkASSERT(boundsRectF);
SkASSERT(bitmap);
- SkASSERT(chunkObj);
+ SkASSERT(chunk);
// paint is optional
- SkRect bounds;
+ SkRect bounds;
GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds);
- draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
+ draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity);
}
static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect,
- const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
- jint destDensity, jint srcDensity)
- {
+ const SkBitmap* bitmap, Res_png_9patch* chunk, const SkPaint* paint,
+ jint destDensity, jint srcDensity) {
SkASSERT(canvas);
SkASSERT(boundsRect);
SkASSERT(bitmap);
- SkASSERT(chunkObj);
+ SkASSERT(chunk);
// paint is optional
- SkRect bounds;
+ SkRect bounds;
GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
- draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
+ draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity);
}
- static jint getTransparentRegion(JNIEnv* env, jobject,
- const SkBitmap* bitmap, jbyteArray chunkObj,
- jobject boundsRect)
- {
+ static jint getTransparentRegion(JNIEnv* env, jobject, const SkBitmap* bitmap,
+ Res_png_9patch* chunk, jobject boundsRect) {
SkASSERT(bitmap);
- SkASSERT(chunkObj);
+ SkASSERT(chunk);
SkASSERT(boundsRect);
- SkRect bounds;
+ SkRect bounds;
GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
- size_t chunkSize = env->GetArrayLength(chunkObj);
- void* storage = alloca(chunkSize);
- env->GetByteArrayRegion(chunkObj, 0, chunkSize,
- reinterpret_cast<jbyte*>(storage));
- if (!env->ExceptionCheck()) {
- // need to deserialize the chunk
- Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage);
- assert(chunkSize == chunk->serializedSize());
- // this relies on deserialization being done in place
- Res_png_9patch::deserialize(chunk);
- SkRegion* region = NULL;
- NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion);
- return (jint)region;
- }
- return 0;
+
+ SkRegion* region = NULL;
+ NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion);
+
+ return (jint) region;
}
};
@@ -174,18 +166,16 @@
#include <android_runtime/AndroidRuntime.h>
static JNINativeMethod gNinePatchMethods[] = {
- { "isNinePatchChunk", "([B)Z", (void*)SkNinePatchGlue::isNinePatchChunk },
- { "validateNinePatchChunk", "(I[B)V", (void*)SkNinePatchGlue::validateNinePatchChunk },
- { "nativeDraw", "(ILandroid/graphics/RectF;I[BIII)V", (void*)SkNinePatchGlue::drawF },
- { "nativeDraw", "(ILandroid/graphics/Rect;I[BIII)V", (void*)SkNinePatchGlue::drawI },
- { "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I",
- (void*)SkNinePatchGlue::getTransparentRegion }
+ { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk },
+ { "validateNinePatchChunk", "(I[B)I", (void*) SkNinePatchGlue::validateNinePatchChunk },
+ { "nativeFinalize", "(I)V", (void*) SkNinePatchGlue::finalize },
+ { "nativeDraw", "(ILandroid/graphics/RectF;IIIII)V", (void*) SkNinePatchGlue::drawF },
+ { "nativeDraw", "(ILandroid/graphics/Rect;IIIII)V", (void*) SkNinePatchGlue::drawI },
+ { "nativeGetTransparentRegion", "(IILandroid/graphics/Rect;)I",
+ (void*) SkNinePatchGlue::getTransparentRegion }
};
-int register_android_graphics_NinePatch(JNIEnv* env)
-{
+int register_android_graphics_NinePatch(JNIEnv* env) {
return android::AndroidRuntime::registerNativeMethods(env,
- "android/graphics/NinePatch",
- gNinePatchMethods,
- SK_ARRAY_COUNT(gNinePatchMethods));
+ "android/graphics/NinePatch", gNinePatchMethods, SK_ARRAY_COUNT(gNinePatchMethods));
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 5454c08..527aee4 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -58,6 +58,10 @@
static void defaultSettingsForAndroid(SkPaint* paint) {
// GlyphID encoding is required because we are using Harfbuzz shaping
paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ SkPaintOptionsAndroid paintOpts = paint->getPaintOptionsAndroid();
+ paintOpts.setUseFontFallbacks(true);
+ paint->setPaintOptionsAndroid(paintOpts);
}
class SkPaintGlue {
@@ -109,7 +113,7 @@
static void setHinting(JNIEnv* env, jobject paint, jint mode) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setHinting(
- mode == 0 ? SkPaint::kNo_Hinting : SkPaint::kSlight_Hinting);
+ mode == 0 ? SkPaint::kNo_Hinting : SkPaint::kNormal_Hinting);
}
static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) {
@@ -300,7 +304,10 @@
ScopedUtfChars localeChars(env, locale);
char langTag[ULOC_FULLNAME_CAPACITY];
toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, localeChars.c_str());
- obj->setLanguage(SkLanguage(langTag));
+
+ SkPaintOptionsAndroid paintOpts = obj->getPaintOptionsAndroid();
+ paintOpts.setLanguage(langTag);
+ obj->setPaintOptionsAndroid(paintOpts);
}
static jfloat getTextSize(JNIEnv* env, jobject paint) {
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index b2cf9c1..73f3639 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -20,7 +20,6 @@
#include "TextLayoutCache.h"
#include "TextLayout.h"
-#include "SkFontHost.h"
#include "SkTypeface_android.h"
#include "HarfBuzzNGFaceSkia.h"
#include <unicode/unistr.h>
@@ -219,7 +218,8 @@
*/
TextLayoutCacheKey::TextLayoutCacheKey(): start(0), count(0), contextCount(0),
dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
- hinting(SkPaint::kNo_Hinting), variant(SkPaint::kDefault_Variant), language() {
+ hinting(SkPaint::kNo_Hinting) {
+ paintOpts.setUseFontFallbacks(true);
}
TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text,
@@ -233,8 +233,7 @@
textScaleX = paint->getTextScaleX();
flags = paint->getFlags();
hinting = paint->getHinting();
- variant = paint->getFontVariant();
- language = paint->getLanguage();
+ paintOpts = paint->getPaintOptionsAndroid();
}
TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
@@ -249,8 +248,7 @@
textScaleX(other.textScaleX),
flags(other.flags),
hinting(other.hinting),
- variant(other.variant),
- language(other.language) {
+ paintOpts(other.paintOpts) {
}
int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
@@ -284,11 +282,8 @@
deltaInt = lhs.dirFlags - rhs.dirFlags;
if (deltaInt) return (deltaInt);
- deltaInt = lhs.variant - rhs.variant;
- if (deltaInt) return (deltaInt);
-
- if (lhs.language < rhs.language) return -1;
- if (lhs.language > rhs.language) return +1;
+ if (lhs.paintOpts != rhs.paintOpts)
+ return memcmp(&lhs.paintOpts, &rhs.paintOpts, sizeof(SkPaintOptionsAndroid));
return memcmp(lhs.getText(), rhs.getText(), lhs.contextCount * sizeof(UChar));
}
@@ -307,7 +302,7 @@
hash = JenkinsHashMix(hash, hash_type(textScaleX));
hash = JenkinsHashMix(hash, flags);
hash = JenkinsHashMix(hash, hinting);
- hash = JenkinsHashMix(hash, variant);
+ hash = JenkinsHashMix(hash, paintOpts.getFontVariant());
// Note: leaving out language is not problematic, as equality comparisons
// are still valid - the only bad thing that could happen is collisions.
hash = JenkinsHashMixShorts(hash, getText(), contextCount);
@@ -319,6 +314,7 @@
*/
TextLayoutValue::TextLayoutValue(size_t contextCount) :
mTotalAdvance(0), mElapsedTime(0) {
+ mBounds.setEmpty();
// Give a hint for advances and glyphs vectors size
mAdvances.setCapacity(contextCount);
mGlyphs.setCapacity(contextCount);
@@ -346,11 +342,11 @@
hb_buffer_destroy(mBuffer);
}
-void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
- size_t start, size_t count, size_t contextCount, int dirFlags) {
-
+void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint,
+ const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) {
computeValues(paint, chars, start, count, contextCount, dirFlags,
- &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs, &value->mPos);
+ &value->mAdvances, &value->mTotalAdvance, &value->mBounds,
+ &value->mGlyphs, &value->mPos);
#if DEBUG_ADVANCES
ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
contextCount, value->mTotalAdvance);
@@ -359,7 +355,7 @@
void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags,
- Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
+ Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, SkRect* outBounds,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
*outTotalAdvance = 0;
if (!count) {
@@ -455,7 +451,7 @@
i, startRun, lengthRun, isRTL);
#endif
computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
- outAdvances, outTotalAdvance, outGlyphs, outPos);
+ outAdvances, outTotalAdvance, outBounds, outGlyphs, outPos);
}
}
@@ -479,7 +475,7 @@
"-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
#endif
computeRunValues(paint, chars, start, count, contextCount, isRTL,
- outAdvances, outTotalAdvance, outGlyphs, outPos);
+ outAdvances, outTotalAdvance, outBounds, outGlyphs, outPos);
}
#if DEBUG_GLYPHS
@@ -676,7 +672,7 @@
void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars,
size_t start, size_t count, size_t contextCount, bool isRTL,
- Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
+ Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, SkRect* outBounds,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
if (!count) {
// We cannot shape an empty run.
@@ -698,8 +694,7 @@
mShapingPaint.setTextScaleX(paint->getTextScaleX());
mShapingPaint.setFlags(paint->getFlags());
mShapingPaint.setHinting(paint->getHinting());
- mShapingPaint.setFontVariant(paint->getFontVariant());
- mShapingPaint.setLanguage(paint->getLanguage());
+ mShapingPaint.setPaintOptionsAndroid(paint->getPaintOptionsAndroid());
// Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
// into the shaperItem
@@ -750,12 +745,22 @@
size_t cluster = info[i].cluster - start;
float xAdvance = HBFixedToFloat(positions[i].x_advance);
outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster);
- outGlyphs->add(info[i].codepoint + glyphBaseCount);
+ jchar glyphId = info[i].codepoint + glyphBaseCount;
+ outGlyphs->add(glyphId);
float xo = HBFixedToFloat(positions[i].x_offset);
float yo = -HBFixedToFloat(positions[i].y_offset);
- outPos->add(totalAdvance + xo + yo * skewX);
- outPos->add(yo);
+
+ float xpos = totalAdvance + xo + yo * skewX;
+ float ypos = yo;
+ outPos->add(xpos);
+ outPos->add(ypos);
totalAdvance += xAdvance;
+
+ // TODO: consider using glyph cache
+ const SkGlyph& metrics = mShapingPaint.getGlyphMetrics(glyphId, NULL);
+ outBounds->join(xpos + metrics.fLeft, ypos + metrics.fTop,
+ xpos + metrics.fLeft + metrics.fWidth, ypos + metrics.fTop + metrics.fHeight);
+
}
}
@@ -839,7 +844,7 @@
if (typeface) {
SkSafeRef(typeface);
} else {
- typeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
+ typeface = SkTypeface::CreateFromName(NULL, SkTypeface::kNormal);
#if DEBUG_GLYPHS
ALOGD("Using Default Typeface (normal style)");
#endif
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 5414a11..54704ec 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -28,7 +28,6 @@
#include <utils/Singleton.h>
#include <SkAutoKern.h>
-#include <SkLanguage.h>
#include <SkPaint.h>
#include <SkTemplates.h>
#include <SkTypeface.h>
@@ -104,8 +103,7 @@
SkScalar textScaleX;
uint32_t flags;
SkPaint::Hinting hinting;
- SkPaint::FontVariant variant;
- SkLanguage language;
+ SkPaintOptionsAndroid paintOpts;
}; // TextLayoutCacheKey
@@ -134,6 +132,7 @@
inline const jfloat* getAdvances() const { return mAdvances.array(); }
inline size_t getAdvancesCount() const { return mAdvances.size(); }
inline jfloat getTotalAdvance() const { return mTotalAdvance; }
+ inline const SkRect& getBounds() const { return mBounds; }
inline const jchar* getGlyphs() const { return mGlyphs.array(); }
inline size_t getGlyphsCount() const { return mGlyphs.size(); }
inline const jfloat* getPos() const { return mPos.array(); }
@@ -150,6 +149,11 @@
jfloat mTotalAdvance;
/**
+ * Bounds containing all glyphs
+ */
+ SkRect mBounds;
+
+ /**
* Glyphs vector
*/
Vector<jchar> mGlyphs;
@@ -208,12 +212,12 @@
void computeValues(const SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags,
- Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
+ Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, SkRect* outBounds,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
void computeRunValues(const SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, bool isRTL,
- Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
+ Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, SkRect* outBounds,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
SkTypeface* setCachedTypeface(SkTypeface** typeface, hb_script_t script, SkTypeface::Style style);
diff --git a/core/jni/android/print/android_print_pdf_PdfDocument.cpp b/core/jni/android/print/android_print_pdf_PdfDocument.cpp
new file mode 100644
index 0000000..745fe8a
--- /dev/null
+++ b/core/jni/android/print/android_print_pdf_PdfDocument.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "SkCanvas.h"
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
+#include "SkRect.h"
+#include "SkSize.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+namespace android {
+
+static jint nativeCreateDocument(JNIEnv* env, jobject clazz) {
+ return reinterpret_cast<jint>(new SkPDFDocument());
+}
+
+static void nativeFinalize(JNIEnv* env, jobject thiz, jint documentPtr) {
+ delete reinterpret_cast<SkPDFDocument*>(documentPtr);
+}
+
+static jint nativeCreatePage(JNIEnv* env, jobject thiz,
+ jobject pageSize, jobject contentSize, jint initialTransformation) {
+ SkIRect skPageSizeRect;
+ GraphicsJNI::jrect_to_irect(env, pageSize, &skPageSizeRect);
+ SkISize skPageSize = SkISize::Make(skPageSizeRect.width(),
+ skPageSizeRect.height());
+
+ SkIRect skContentRect;
+ GraphicsJNI::jrect_to_irect(env, contentSize, &skContentRect);
+ SkISize skContentSize = SkISize::Make(skContentRect.width(),
+ skContentRect.height());
+
+ SkMatrix* transformation = reinterpret_cast<SkMatrix*>(initialTransformation);
+ SkPDFDevice* skPdfDevice = new SkPDFDevice(skPageSize, skContentSize, *transformation);
+
+ return reinterpret_cast<jint>(new SkCanvas(skPdfDevice));
+}
+
+static void nativeAppendPage(JNIEnv* env, jobject thiz, jint documentPtr, jint pagePtr) {
+ SkCanvas* page = reinterpret_cast<SkCanvas*>(pagePtr);
+ SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
+ SkPDFDevice* device = static_cast<SkPDFDevice*>(page->getDevice());
+ document->appendPage(device);
+}
+
+static void nativeWriteTo(JNIEnv* env, jobject clazz, jint documentPtr,
+ jobject out, jbyteArray chunk) {
+ SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk);
+ SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
+ document->emitPDF(skWStream);
+ delete skWStream;
+}
+
+static JNINativeMethod gPdfDocument_Methods[] = {
+ {"nativeCreateDocument", "()I", (void*) nativeCreateDocument},
+ {"nativeFinalize", "(I)V", (void*) nativeFinalize},
+ {"nativeCreatePage", "(Landroid/graphics/Rect;Landroid/graphics/Rect;I)I",
+ (void*) nativeCreatePage},
+ {"nativeAppendPage", "(II)V", (void*) nativeAppendPage},
+ {"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo}
+};
+
+int register_android_print_pdf_PdfDocument(JNIEnv* env) {
+ int result = android::AndroidRuntime::registerNativeMethods(
+ env, "android/print/pdf/PdfDocument", gPdfDocument_Methods,
+ NELEM(gPdfDocument_Methods));
+ return result;
+}
+
+};
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 9fc01e1..f768ce8 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -25,7 +25,7 @@
#include <android_runtime/android_util_AssetManager.h>
#include <android_runtime/android_view_Surface.h>
#include <android_runtime/AndroidRuntime.h>
-#include <androidfw/InputTransport.h>
+#include <input/InputTransport.h>
#include <gui/Surface.h>
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 686e4e3..dec4cd4 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -586,6 +586,30 @@
}
}
+static void android_hardware_Camera_setPreviewCallbackSurface(JNIEnv *env,
+ jobject thiz, jobject jSurface)
+{
+ ALOGV("setPreviewCallbackSurface");
+ JNICameraContext* context;
+ sp<Camera> camera = get_native_camera(env, thiz, &context);
+ if (camera == 0) return;
+
+ sp<IGraphicBufferProducer> gbp;
+ sp<Surface> surface;
+ if (jSurface) {
+ surface = android_view_Surface_getSurface(env, jSurface);
+ if (surface != NULL) {
+ gbp = surface->getIGraphicBufferProducer();
+ }
+ }
+ // Clear out normal preview callbacks
+ context->setCallbackMode(env, false, false);
+ // Then set up callback surface
+ if (camera->setPreviewCallbackTarget(gbp) != NO_ERROR) {
+ jniThrowException(env, "java/io/IOException", "setPreviewCallbackTarget failed");
+ }
+}
+
static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
{
ALOGV("startPreview");
@@ -881,6 +905,9 @@
{ "setPreviewTexture",
"(Landroid/graphics/SurfaceTexture;)V",
(void *)android_hardware_Camera_setPreviewTexture },
+ { "setPreviewCallbackSurface",
+ "(Landroid/view/Surface;)V",
+ (void *)android_hardware_Camera_setPreviewCallbackSurface },
{ "startPreview",
"()V",
(void *)android_hardware_Camera_startPreview },
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 6374494..ae0113b 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -142,7 +142,13 @@
while ((n = q->read(buffer, 16)) > 0) {
for (int i=0 ; i<n ; i++) {
- env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data);
+ if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER) {
+ // step-counter returns a uint64, but the java API only deals with floats
+ float value = float(buffer[i].u64.step_counter);
+ env->SetFloatArrayRegion(mScratch, 0, 1, &value);
+ } else {
+ env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data);
+ }
env->CallVoidMethod(mReceiverObject,
gBaseEventQueueClassInfo.dispatchSensorEvent,
diff --git a/core/jni/android_hardware_photography_CameraMetadata.cpp b/core/jni/android_hardware_photography_CameraMetadata.cpp
new file mode 100644
index 0000000..fa363f3
--- /dev/null
+++ b/core/jni/android_hardware_photography_CameraMetadata.cpp
@@ -0,0 +1,271 @@
+/*
+**
+** 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.
+*/
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "CameraMetadata-JNI"
+#include <utils/Log.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_os_Parcel.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <camera/CameraMetadata.h>
+
+// fully-qualified class name
+#define CAMERA_METADATA_CLASS_NAME "android/hardware/photography/CameraMetadata"
+
+using namespace android;
+
+struct fields_t {
+ jfieldID metadata_ptr;
+};
+
+static fields_t fields;
+
+extern "C" {
+
+static void CameraMetadata_classInit(JNIEnv *env, jobject thiz);
+
+// Less safe access to native pointer. Does NOT throw any Java exceptions if NULL.
+static CameraMetadata* CameraMetadata_getPointerNoThrow(JNIEnv *env, jobject thiz) {
+
+ if (thiz == NULL) {
+ return NULL;
+ }
+
+ return reinterpret_cast<CameraMetadata*>(env->GetLongField(thiz, fields.metadata_ptr));
+}
+
+// Safe access to native pointer from object. Throws if not possible to access.
+static CameraMetadata* CameraMetadata_getPointerThrow(JNIEnv *env, jobject thiz,
+ const char* argName = "this") {
+
+ if (thiz == NULL) {
+ ALOGV("%s: Throwing java.lang.NullPointerException for null reference",
+ __FUNCTION__);
+ jniThrowNullPointerException(env, argName);
+ return NULL;
+ }
+
+ CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
+ if (metadata == NULL) {
+ ALOGV("%s: Throwing java.lang.IllegalStateException for closed object",
+ __FUNCTION__);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Metadata object was already closed");
+ return NULL;
+ }
+
+ return metadata;
+}
+
+static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {
+ ALOGV("%s", __FUNCTION__);
+
+ return reinterpret_cast<jlong>(new CameraMetadata());
+}
+
+static jboolean CameraMetadata_isEmpty(JNIEnv *env, jobject thiz) {
+ ALOGV("%s", __FUNCTION__);
+
+ CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+
+ if (metadata == NULL) {
+ ALOGW("%s: Returning early due to exception being thrown",
+ __FUNCTION__);
+ return JNI_TRUE; // actually throws java exc.
+ }
+
+ jboolean empty = metadata->isEmpty();
+
+ ALOGV("%s: Empty returned %d, entry count was %d",
+ __FUNCTION__, empty, metadata->entryCount());
+
+ return empty;
+}
+
+static jint CameraMetadata_getEntryCount(JNIEnv *env, jobject thiz) {
+ ALOGV("%s", __FUNCTION__);
+
+ CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+
+ if (metadata == NULL) return 0; // actually throws java exc.
+
+ return metadata->entryCount();
+}
+
+// idempotent. calling more than once has no effect.
+static void CameraMetadata_close(JNIEnv *env, jobject thiz) {
+ ALOGV("%s", __FUNCTION__);
+
+ CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
+
+ if (metadata != NULL) {
+ delete metadata;
+ env->SetLongField(thiz, fields.metadata_ptr, 0);
+ }
+
+ LOG_ALWAYS_FATAL_IF(CameraMetadata_getPointerNoThrow(env, thiz) != NULL,
+ "Expected the native ptr to be 0 after #close");
+}
+
+static void CameraMetadata_swap(JNIEnv *env, jobject thiz, jobject other) {
+ ALOGV("%s", __FUNCTION__);
+
+ CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+
+ // order is important: we can't call another JNI method
+ // if there is an exception pending
+ if (metadata == NULL) return;
+
+ CameraMetadata* otherMetadata = CameraMetadata_getPointerThrow(env, other, "other");
+
+ if (otherMetadata == NULL) return;
+
+ metadata->swap(*otherMetadata);
+}
+
+static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) {
+ ALOGV("%s", __FUNCTION__);
+ CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+ if (metadata == NULL) {
+ return;
+ }
+
+ Parcel* parcelNative = parcelForJavaObject(env, parcel);
+ if (parcelNative == NULL) {
+ jniThrowNullPointerException(env, "parcel");
+ return;
+ }
+
+ status_t err;
+ if ((err = metadata->readFromParcel(parcelNative)) != OK) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Failed to read from parcel (error code %d)", err);
+ return;
+ }
+}
+
+static void CameraMetadata_writeToParcel(JNIEnv *env, jobject thiz, jobject parcel) {
+ ALOGV("%s", __FUNCTION__);
+ CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+ if (metadata == NULL) {
+ return;
+ }
+
+ Parcel* parcelNative = parcelForJavaObject(env, parcel);
+ if (parcelNative == NULL) {
+ jniThrowNullPointerException(env, "parcel");
+ return;
+ }
+
+ status_t err;
+ if ((err = metadata->writeToParcel(parcelNative)) != OK) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Failed to write to parcel (error code %d)", err);
+ return;
+ }
+}
+
+} // extern "C"
+
+//-------------------------------------------------
+
+static JNINativeMethod gCameraMetadataMethods[] = {
+ { "nativeClassInit",
+ "()V",
+ (void *)CameraMetadata_classInit },
+ { "nativeAllocate",
+ "()J",
+ (void*)CameraMetadata_allocate },
+ { "nativeIsEmpty",
+ "()Z",
+ (void*)CameraMetadata_isEmpty },
+ { "nativeGetEntryCount",
+ "()I",
+ (void*)CameraMetadata_getEntryCount },
+ { "nativeClose",
+ "()V",
+ (void*)CameraMetadata_close },
+ { "nativeSwap",
+ "(L" CAMERA_METADATA_CLASS_NAME ";)V",
+ (void *)CameraMetadata_swap },
+ { "nativeReadFromParcel",
+ "(Landroid/os/Parcel;)V",
+ (void *)CameraMetadata_readFromParcel },
+ { "nativeWriteToParcel",
+ "(Landroid/os/Parcel;)V",
+ (void *)CameraMetadata_writeToParcel },
+};
+
+struct field {
+ const char *class_name;
+ const char *field_name;
+ const char *field_type;
+ jfieldID *jfield;
+};
+
+static int find_fields(JNIEnv *env, field *fields, int count)
+{
+ for (int i = 0; i < count; i++) {
+ field *f = &fields[i];
+ jclass clazz = env->FindClass(f->class_name);
+ if (clazz == NULL) {
+ ALOGE("Can't find %s", f->class_name);
+ return -1;
+ }
+
+ jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type);
+ if (field == NULL) {
+ ALOGE("Can't find %s.%s", f->class_name, f->field_name);
+ return -1;
+ }
+
+ *(f->jfield) = field;
+ }
+
+ return 0;
+}
+
+// Get all the required offsets in java class and register native functions
+int register_android_hardware_photography_CameraMetadata(JNIEnv *env)
+{
+ // Register native functions
+ return AndroidRuntime::registerNativeMethods(env,
+ CAMERA_METADATA_CLASS_NAME,
+ gCameraMetadataMethods,
+ NELEM(gCameraMetadataMethods));
+}
+
+extern "C" {
+static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) {
+ // XX: Why do this separately instead of doing it in the register function?
+ ALOGV("%s", __FUNCTION__);
+
+ field fields_to_find[] = {
+ { CAMERA_METADATA_CLASS_NAME, "mMetadataPtr", "J", &fields.metadata_ptr },
+ };
+
+ // Do this here instead of in register_native_methods,
+ // since otherwise it will fail to find the fields.
+ if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0)
+ return;
+
+ jclass clazz = env->FindClass(CAMERA_METADATA_CLASS_NAME);
+}
+} // extern "C"
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 9537ac4..08962e2 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -33,11 +33,11 @@
static jint DBG = false;
-static int doCommand(const char *ifname, const char *cmd, char *replybuf, int replybuflen)
+static int doCommand(const char *ifname, char *cmd, char *replybuf, int replybuflen)
{
size_t reply_len = replybuflen - 1;
- if (::wifi_command(ifname, cmd, replybuf, &reply_len) != 0)
+ if (::wifi_command(ifname, cmd, BUF_SIZE, replybuf, &reply_len) != 0)
return -1;
else {
// Strip off trailing newline
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 664af07..48babb3 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -136,7 +136,7 @@
(JNIEnv *_env, jobject _this) {
EGLint _returnValue = (EGLint) 0;
_returnValue = eglGetError();
- return _returnValue;
+ return (jint)_returnValue;
}
/* EGLDisplay eglGetDisplay ( EGLNativeDisplayType display_id ) */
@@ -230,7 +230,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglTerminate ( EGLDisplay dpy ) */
@@ -243,7 +243,7 @@
_returnValue = eglTerminate(
(EGLDisplay)dpy_native
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* const char * eglQueryString ( EGLDisplay dpy, EGLint name ) */
@@ -331,7 +331,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglChooseConfig ( EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config ) */
@@ -454,7 +454,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglGetConfigAttrib ( EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value ) */
@@ -509,7 +509,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLSurface eglCreateWindowSurface ( EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list ) */
@@ -753,7 +753,7 @@
(EGLDisplay)dpy_native,
(EGLSurface)surface_native
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglQuerySurface ( EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value ) */
@@ -808,7 +808,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglBindAPI ( EGLenum api ) */
@@ -819,7 +819,7 @@
_returnValue = eglBindAPI(
(EGLenum)api
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLenum eglQueryAPI ( void ) */
@@ -828,7 +828,7 @@
(JNIEnv *_env, jobject _this) {
EGLenum _returnValue = (EGLenum) 0;
_returnValue = eglQueryAPI();
- return _returnValue;
+ return (jint)_returnValue;
}
/* EGLBoolean eglWaitClient ( void ) */
@@ -837,7 +837,7 @@
(JNIEnv *_env, jobject _this) {
EGLBoolean _returnValue = (EGLBoolean) 0;
_returnValue = eglWaitClient();
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglReleaseThread ( void ) */
@@ -846,7 +846,7 @@
(JNIEnv *_env, jobject _this) {
EGLBoolean _returnValue = (EGLBoolean) 0;
_returnValue = eglReleaseThread();
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLSurface eglCreatePbufferFromClientBuffer ( EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list ) */
@@ -927,7 +927,7 @@
(EGLint)attribute,
(EGLint)value
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglBindTexImage ( EGLDisplay dpy, EGLSurface surface, EGLint buffer ) */
@@ -943,7 +943,7 @@
(EGLSurface)surface_native,
(EGLint)buffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglReleaseTexImage ( EGLDisplay dpy, EGLSurface surface, EGLint buffer ) */
@@ -959,7 +959,7 @@
(EGLSurface)surface_native,
(EGLint)buffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglSwapInterval ( EGLDisplay dpy, EGLint interval ) */
@@ -973,7 +973,7 @@
(EGLDisplay)dpy_native,
(EGLint)interval
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLContext eglCreateContext ( EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list ) */
@@ -1052,7 +1052,7 @@
(EGLDisplay)dpy_native,
(EGLContext)ctx_native
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglMakeCurrent ( EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx ) */
@@ -1071,7 +1071,7 @@
(EGLSurface)read_native,
(EGLContext)ctx_native
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLContext eglGetCurrentContext ( void ) */
@@ -1155,7 +1155,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglWaitGL ( void ) */
@@ -1164,7 +1164,7 @@
(JNIEnv *_env, jobject _this) {
EGLBoolean _returnValue = (EGLBoolean) 0;
_returnValue = eglWaitGL();
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglWaitNative ( EGLint engine ) */
@@ -1175,7 +1175,7 @@
_returnValue = eglWaitNative(
(EGLint)engine
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglSwapBuffers ( EGLDisplay dpy, EGLSurface surface ) */
@@ -1190,7 +1190,7 @@
(EGLDisplay)dpy_native,
(EGLSurface)surface_native
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* EGLBoolean eglCopyBuffers ( EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target ) */
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index 336c0ec..cc34e99 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -63,6 +63,12 @@
glVertexAttribPointer(indx, size, type, normalized, stride, pointer);
}
#endif
+#ifdef GL_ES_VERSION_3_0
+static void glVertexAttribIPointerBounds(GLuint indx, GLint size, GLenum type,
+ GLsizei stride, const GLvoid *pointer, GLsizei count) {
+ glVertexAttribIPointer(indx, size, type, stride, pointer);
+}
+#endif
}
/* Cache method IDs each time the class is loaded. */
@@ -288,6 +294,7 @@
int _needed = 0;
params = (CTYPE *)getPointer(_env, params_buf, &_array, &_remaining, &_bufferOffset);
+ _remaining /= sizeof(CTYPE); // convert from bytes to item count
_needed = getNeededCount(pname);
// if we didn't find this pname, we just assume the user passed
// an array of the right size -- this might happen with extensions
@@ -1184,7 +1191,7 @@
(JNIEnv *_env, jobject _this) {
GLenum _returnValue;
_returnValue = glGetError();
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glGetIntegerv ( GLenum pname, GLint *params ) */
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index 59e63e1..9284384 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -63,6 +63,12 @@
glVertexAttribPointer(indx, size, type, normalized, stride, pointer);
}
#endif
+#ifdef GL_ES_VERSION_3_0
+static void glVertexAttribIPointerBounds(GLuint indx, GLint size, GLenum type,
+ GLsizei stride, const GLvoid *pointer, GLsizei count) {
+ glVertexAttribIPointer(indx, size, type, stride, pointer);
+}
+#endif
}
/* Cache method IDs each time the class is loaded. */
@@ -288,6 +294,7 @@
int _needed = 0;
params = (CTYPE *)getPointer(_env, params_buf, &_array, &_remaining, &_bufferOffset);
+ _remaining /= sizeof(CTYPE); // convert from bytes to item count
_needed = getNeededCount(pname);
// if we didn't find this pname, we just assume the user passed
// an array of the right size -- this might happen with extensions
@@ -395,7 +402,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jint)_returnValue;
}
/* GLbitfield glQueryMatrixxOES ( GLfixed *mantissa, GLint *exponent ) */
@@ -452,7 +459,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jint)_returnValue;
}
static const char *classPathName = "android/opengl/GLES10Ext";
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 352f5bf..871e84d 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -63,6 +63,12 @@
glVertexAttribPointer(indx, size, type, normalized, stride, pointer);
}
#endif
+#ifdef GL_ES_VERSION_3_0
+static void glVertexAttribIPointerBounds(GLuint indx, GLint size, GLenum type,
+ GLsizei stride, const GLvoid *pointer, GLsizei count) {
+ glVertexAttribIPointer(indx, size, type, stride, pointer);
+}
+#endif
}
/* Cache method IDs each time the class is loaded. */
@@ -288,6 +294,7 @@
int _needed = 0;
params = (CTYPE *)getPointer(_env, params_buf, &_array, &_remaining, &_bufferOffset);
+ _remaining /= sizeof(CTYPE); // convert from bytes to item count
_needed = getNeededCount(pname);
// if we didn't find this pname, we just assume the user passed
// an array of the right size -- this might happen with extensions
@@ -571,7 +578,7 @@
(GLint)size,
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
@@ -672,7 +679,7 @@
(GLenum)mode,
(GLsizei)count,
(GLenum)type,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -2263,7 +2270,7 @@
_returnValue = glIsBuffer(
(GLuint)buffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsEnabled ( GLenum cap ) */
@@ -2274,7 +2281,7 @@
_returnValue = glIsEnabled(
(GLenum)cap
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsTexture ( GLuint texture ) */
@@ -2285,7 +2292,7 @@
_returnValue = glIsTexture(
(GLuint)texture
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* void glNormalPointer ( GLenum type, GLsizei stride, GLint offset ) */
@@ -2295,7 +2302,7 @@
glNormalPointer(
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
@@ -2522,7 +2529,7 @@
(GLint)size,
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
@@ -2930,7 +2937,7 @@
(GLint)size,
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index c91baa2..3e038ad 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -63,6 +63,12 @@
glVertexAttribPointer(indx, size, type, normalized, stride, pointer);
}
#endif
+#ifdef GL_ES_VERSION_3_0
+static void glVertexAttribIPointerBounds(GLuint indx, GLint size, GLenum type,
+ GLsizei stride, const GLvoid *pointer, GLsizei count) {
+ glVertexAttribIPointer(indx, size, type, stride, pointer);
+}
+#endif
}
/* Cache method IDs each time the class is loaded. */
@@ -288,6 +294,7 @@
int _needed = 0;
params = (CTYPE *)getPointer(_env, params_buf, &_array, &_remaining, &_bufferOffset);
+ _remaining /= sizeof(CTYPE); // convert from bytes to item count
_needed = getNeededCount(pname);
// if we didn't find this pname, we just assume the user passed
// an array of the right size -- this might happen with extensions
@@ -2129,7 +2136,7 @@
_returnValue = glIsRenderbufferOES(
(GLuint)renderbuffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* void glBindRenderbufferOES ( GLenum target, GLuint renderbuffer ) */
@@ -2422,7 +2429,7 @@
_returnValue = glIsFramebufferOES(
(GLuint)framebuffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* void glBindFramebufferOES ( GLenum target, GLuint framebuffer ) */
@@ -2615,7 +2622,7 @@
_returnValue = glCheckFramebufferStatusOES(
(GLenum)target
);
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glFramebufferRenderbufferOES ( GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer ) */
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index 4179785..9bc69ae 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -63,6 +63,12 @@
glVertexAttribPointer(indx, size, type, normalized, stride, pointer);
}
#endif
+#ifdef GL_ES_VERSION_3_0
+static void glVertexAttribIPointerBounds(GLuint indx, GLint size, GLenum type,
+ GLsizei stride, const GLvoid *pointer, GLsizei count) {
+ glVertexAttribIPointer(indx, size, type, stride, pointer);
+}
+#endif
}
/* Cache method IDs each time the class is loaded. */
@@ -288,6 +294,7 @@
int _needed = 0;
params = (CTYPE *)getPointer(_env, params_buf, &_array, &_remaining, &_bufferOffset);
+ _remaining /= sizeof(CTYPE); // convert from bytes to item count
_needed = getNeededCount(pname);
// if we didn't find this pname, we just assume the user passed
// an array of the right size -- this might happen with extensions
@@ -549,7 +556,7 @@
_returnValue = glCheckFramebufferStatus(
(GLenum)target
);
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glClear ( GLbitfield mask ) */
@@ -709,7 +716,7 @@
(JNIEnv *_env, jobject _this) {
GLuint _returnValue;
_returnValue = glCreateProgram();
- return _returnValue;
+ return (jint)_returnValue;
}
/* GLuint glCreateShader ( GLenum type ) */
@@ -720,7 +727,7 @@
_returnValue = glCreateShader(
(GLenum)type
);
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glCullFace ( GLenum mode ) */
@@ -1172,7 +1179,7 @@
(GLenum)mode,
(GLsizei)count,
(GLenum)type,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -2466,7 +2473,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glGetBooleanv ( GLenum pname, GLboolean *params ) */
@@ -2576,7 +2583,7 @@
(JNIEnv *_env, jobject _this) {
GLenum _returnValue;
_returnValue = glGetError();
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glGetFloatv ( GLenum pname, GLfloat *params ) */
@@ -3614,7 +3621,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glGetVertexAttribfv ( GLuint index, GLenum pname, GLfloat *params ) */
@@ -3855,7 +3862,7 @@
_returnValue = glIsBuffer(
(GLuint)buffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsEnabled ( GLenum cap ) */
@@ -3866,7 +3873,7 @@
_returnValue = glIsEnabled(
(GLenum)cap
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsFramebuffer ( GLuint framebuffer ) */
@@ -3877,7 +3884,7 @@
_returnValue = glIsFramebuffer(
(GLuint)framebuffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsProgram ( GLuint program ) */
@@ -3888,7 +3895,7 @@
_returnValue = glIsProgram(
(GLuint)program
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsRenderbuffer ( GLuint renderbuffer ) */
@@ -3899,7 +3906,7 @@
_returnValue = glIsRenderbuffer(
(GLuint)renderbuffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsShader ( GLuint shader ) */
@@ -3910,7 +3917,7 @@
_returnValue = glIsShader(
(GLuint)shader
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsTexture ( GLuint texture ) */
@@ -3921,7 +3928,7 @@
_returnValue = glIsTexture(
(GLuint)texture
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* void glLineWidth ( GLfloat width ) */
@@ -5975,7 +5982,7 @@
(GLenum)type,
(GLboolean)normalized,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index 3c50aa0..832d643 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -294,6 +294,7 @@
int _needed = 0;
params = (CTYPE *)getPointer(_env, params_buf, &_array, &_remaining, &_bufferOffset);
+ _remaining /= sizeof(CTYPE); // convert from bytes to item count
_needed = getNeededCount(pname);
// if we didn't find this pname, we just assume the user passed
// an array of the right size -- this might happen with extensions
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 2883c10..8b7f3dd 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -43,6 +43,7 @@
HEAP_UNKNOWN,
HEAP_DALVIK,
HEAP_NATIVE,
+ HEAP_DALVIK_OTHER,
HEAP_STACK,
HEAP_CURSOR,
HEAP_ASHMEM,
@@ -52,38 +53,56 @@
HEAP_APK,
HEAP_TTF,
HEAP_DEX,
+ HEAP_OAT,
+ HEAP_ART,
HEAP_UNKNOWN_MAP,
+ HEAP_DALVIK_NORMAL,
+ HEAP_DALVIK_LARGE,
+ HEAP_DALVIK_LINEARALLOC,
+ HEAP_DALVIK_ACCOUNTING,
+ HEAP_DALVIK_CODE_CACHE,
+
_NUM_HEAP,
+ _NUM_EXCLUSIVE_HEAP = HEAP_UNKNOWN_MAP+1,
_NUM_CORE_HEAP = HEAP_NATIVE+1
};
struct stat_fields {
jfieldID pss_field;
+ jfieldID pssSwappable_field;
jfieldID privateDirty_field;
jfieldID sharedDirty_field;
+ jfieldID privateClean_field;
+ jfieldID sharedClean_field;
};
struct stat_field_names {
const char* pss_name;
+ const char* pssSwappable_name;
const char* privateDirty_name;
const char* sharedDirty_name;
+ const char* privateClean_name;
+ const char* sharedClean_name;
};
static stat_fields stat_fields[_NUM_CORE_HEAP];
static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
- { "otherPss", "otherPrivateDirty", "otherSharedDirty" },
- { "dalvikPss", "dalvikPrivateDirty", "dalvikSharedDirty" },
- { "nativePss", "nativePrivateDirty", "nativeSharedDirty" }
+ { "otherPss", "otherSwappablePss", "otherPrivateDirty", "otherSharedDirty", "otherPrivateClean", "otherSharedClean" },
+ { "dalvikPss", "dalvikSwappablePss", "dalvikPrivateDirty", "dalvikSharedDirty", "dalvikPrivateClean", "dalvikSharedClean" },
+ { "nativePss", "nativeSwappablePss", "nativePrivateDirty", "nativeSharedDirty", "nativePrivateClean", "nativeSharedClean" }
};
jfieldID otherStats_field;
struct stats_t {
int pss;
+ int swappablePss;
int privateDirty;
int sharedDirty;
+ int privateClean;
+ int sharedClean;
};
#define BINDER_STATS "/proc/binder/stats"
@@ -124,9 +143,11 @@
int len, nameLen;
bool skip, done = false;
- unsigned size = 0, resident = 0, pss = 0;
+ unsigned size = 0, resident = 0, pss = 0, swappable_pss = 0;
+ float sharing_proportion = 0.0;
unsigned shared_clean = 0, shared_dirty = 0;
unsigned private_clean = 0, private_dirty = 0;
+ bool is_swappable = false;
unsigned referenced = 0;
unsigned temp;
@@ -137,6 +158,7 @@
int name_pos;
int whichHeap = HEAP_UNKNOWN;
+ int subHeap = HEAP_UNKNOWN;
int prevHeap = HEAP_UNKNOWN;
if(fgets(line, sizeof(line), fp) == 0) return;
@@ -145,7 +167,9 @@
prevHeap = whichHeap;
prevEnd = end;
whichHeap = HEAP_UNKNOWN;
+ subHeap = HEAP_UNKNOWN;
skip = false;
+ is_swappable = false;
len = strlen(line);
if (len < 1) return;
@@ -160,30 +184,68 @@
name = line + name_pos;
nameLen = strlen(name);
- if ((strstr(name, "[heap]") == name) ||
- (strstr(name, "/dev/ashmem/libc malloc") == name)) {
+ if ((strstr(name, "[heap]") == name)) {
whichHeap = HEAP_NATIVE;
- } else if (strstr(name, "/dev/ashmem/dalvik-") == name) {
- whichHeap = HEAP_DALVIK;
- } else if (strstr(name, "[stack") == name) {
+ } else if (strncmp(name, "/dev/ashmem", 11) == 0) {
+ if (strncmp(name, "/dev/ashmem/dalvik-", 19) == 0) {
+ whichHeap = HEAP_DALVIK_OTHER;
+ if (strstr(name, "/dev/ashmem/dalvik-LinearAlloc") == name) {
+ subHeap = HEAP_DALVIK_LINEARALLOC;
+ } else if ((strstr(name, "/dev/ashmem/dalvik-mark") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-allocspace alloc space live-bitmap") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-allocspace alloc space mark-bitmap") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-card table") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-allocation stack") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-live stack") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-imagespace") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-bitmap") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-card-table") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-mark-stack") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-aux-structure") == name)) {
+ subHeap = HEAP_DALVIK_ACCOUNTING;
+ } else if (strstr(name, "/dev/ashmem/dalvik-large") == name) {
+ whichHeap = HEAP_DALVIK;
+ subHeap = HEAP_DALVIK_LARGE;
+ } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name) {
+ subHeap = HEAP_DALVIK_CODE_CACHE;
+ } else {
+ // This is the regular Dalvik heap.
+ whichHeap = HEAP_DALVIK;
+ subHeap = HEAP_DALVIK_NORMAL;
+ }
+ } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) {
+ whichHeap = HEAP_CURSOR;
+ } else if (strncmp(name, "/dev/ashmem/libc malloc", 23) == 0) {
+ whichHeap = HEAP_NATIVE;
+ } else {
+ whichHeap = HEAP_ASHMEM;
+ }
+ } else if (strncmp(name, "[stack", 6) == 0) {
whichHeap = HEAP_STACK;
- } else if (strstr(name, "/dev/ashmem/CursorWindow") == name) {
- whichHeap = HEAP_CURSOR;
- } else if (strstr(name, "/dev/ashmem/") == name) {
- whichHeap = HEAP_ASHMEM;
- } else if (strstr(name, "/dev/") == name) {
+ } else if (strncmp(name, "/dev/", 5) == 0) {
whichHeap = HEAP_UNKNOWN_DEV;
} else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
whichHeap = HEAP_SO;
+ is_swappable = true;
} else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) {
whichHeap = HEAP_JAR;
+ is_swappable = true;
} else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) {
whichHeap = HEAP_APK;
+ is_swappable = true;
} else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) {
whichHeap = HEAP_TTF;
+ is_swappable = true;
} else if ((nameLen > 4 && strcmp(name+nameLen-4, ".dex") == 0) ||
(nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) {
whichHeap = HEAP_DEX;
+ is_swappable = true;
+ } else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) {
+ whichHeap = HEAP_OAT;
+ is_swappable = true;
+ } else if (nameLen > 4 && strcmp(name+nameLen-4, ".art") == 0) {
+ whichHeap = HEAP_ART;
+ is_swappable = true;
} else if (nameLen > 0) {
whichHeap = HEAP_UNKNOWN_MAP;
} else if (start == prevEnd && prevHeap == HEAP_SO) {
@@ -225,9 +287,29 @@
}
if (!skip) {
+ if (is_swappable && (pss > 0)) {
+ sharing_proportion = 0.0;
+ if ((shared_clean > 0) || (shared_dirty > 0)) {
+ sharing_proportion = (pss - private_clean - private_dirty)/(shared_clean+shared_dirty);
+ }
+ swappable_pss = (sharing_proportion*shared_clean) + private_clean;
+ } else
+ swappable_pss = 0;
+
stats[whichHeap].pss += pss;
+ stats[whichHeap].swappablePss += swappable_pss;
stats[whichHeap].privateDirty += private_dirty;
stats[whichHeap].sharedDirty += shared_dirty;
+ stats[whichHeap].privateClean += private_clean;
+ stats[whichHeap].sharedClean += shared_clean;
+ if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) {
+ stats[subHeap].pss += pss;
+ stats[subHeap].swappablePss += swappable_pss;
+ stats[subHeap].privateDirty += private_dirty;
+ stats[subHeap].sharedDirty += shared_dirty;
+ stats[subHeap].privateClean += private_clean;
+ stats[subHeap].sharedClean += shared_clean;
+ }
}
}
}
@@ -251,20 +333,28 @@
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
+
load_maps(pid, stats);
- for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
+ for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
stats[HEAP_UNKNOWN].pss += stats[i].pss;
+ stats[HEAP_UNKNOWN].swappablePss += stats[i].swappablePss;
stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
+ stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
+ stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
}
for (int i=0; i<_NUM_CORE_HEAP; i++) {
env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
+ env->SetIntField(object, stat_fields[i].pssSwappable_field, stats[i].swappablePss);
env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
+ env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
+ env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
}
+
jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
@@ -275,8 +365,11 @@
int j=0;
for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
otherArray[j++] = stats[i].pss;
+ otherArray[j++] = stats[i].swappablePss;
otherArray[j++] = stats[i].privateDirty;
otherArray[j++] = stats[i].sharedDirty;
+ otherArray[j++] = stats[i].privateClean;
+ otherArray[j++] = stats[i].sharedClean;
}
env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
@@ -301,12 +394,16 @@
if (fp == 0) return 0;
while (true) {
- if (fgets(line, 1024, fp) == 0) {
+ if (fgets(line, 1024, fp) == NULL) {
break;
}
- if (sscanf(line, "Pss: %d kB", &temp) == 1) {
- pss += temp;
+ if (strncmp(line, "Pss: ", 5) == 0) {
+ char* c = line + 5;
+ while (*c != 0 && (*c < '0' || *c > '9')) {
+ c++;
+ }
+ pss += atoi(c);
}
}
@@ -617,11 +714,13 @@
// Sanity check the number of other statistics expected in Java matches here.
jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I");
jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field);
+ jfieldID numDvkStats_field = env->GetStaticFieldID(clazz, "NUM_DVK_STATS", "I");
+ jint numDvkStats = env->GetStaticIntField(clazz, numDvkStats_field);
int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP;
- if (numOtherStats != expectedNumOtherStats) {
+ if ((numOtherStats + numDvkStats) != expectedNumOtherStats) {
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
- "android.os.Debug.Meminfo.NUM_OTHER_STATS=%d expected %d",
- numOtherStats, expectedNumOtherStats);
+ "android.os.Debug.Meminfo.NUM_OTHER_STATS+android.os.Debug.Meminfo.NUM_DVK_STATS=%d expected %d",
+ numOtherStats+numDvkStats, expectedNumOtherStats);
return JNI_ERR;
}
@@ -630,10 +729,16 @@
for (int i=0; i<_NUM_CORE_HEAP; i++) {
stat_fields[i].pss_field =
env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
+ stat_fields[i].pssSwappable_field =
+ env->GetFieldID(clazz, stat_field_names[i].pssSwappable_name, "I");
stat_fields[i].privateDirty_field =
env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
stat_fields[i].sharedDirty_field =
env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
+ stat_fields[i].privateClean_field =
+ env->GetFieldID(clazz, stat_field_names[i].privateClean_name, "I");
+ stat_fields[i].sharedClean_field =
+ env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
}
return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index a07f5b7..0aaa2b1 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -55,6 +55,24 @@
return chmod(file8.string(), mode) == 0 ? 0 : errno;
}
+jint android_os_FileUtils_getUid(JNIEnv* env, jobject clazz, jstring file)
+{
+ struct stat stats;
+ const jchar* str = env->GetStringCritical(file, 0);
+ String8 file8;
+ if (str) {
+ file8 = String8(str, env->GetStringLength(file));
+ env->ReleaseStringCritical(file, str);
+ }
+ if (file8.size() <= 0) {
+ return ENOENT;
+ }
+ if (stat(file8.string(), &stats) < 0) {
+ return -1;
+ }
+ return stats.st_uid;
+}
+
jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path)
{
if (path == NULL) {
@@ -78,6 +96,7 @@
static const JNINativeMethod methods[] = {
{"setPermissions", "(Ljava/lang/String;III)I", (void*)android_os_FileUtils_setPermissions},
+ {"getUid", "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getUid},
{"getFatVolumeId", "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
};
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index 7540645..c9c3720 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -141,6 +141,11 @@
return nativeMessageQueue->wake();
}
+static jboolean android_os_MessageQueue_nativeIsIdling(JNIEnv* env, jclass clazz, jint ptr) {
+ NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
+ return nativeMessageQueue->getLooper()->isIdling();
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gMessageQueueMethods[] = {
@@ -148,7 +153,8 @@
{ "nativeInit", "()I", (void*)android_os_MessageQueue_nativeInit },
{ "nativeDestroy", "(I)V", (void*)android_os_MessageQueue_nativeDestroy },
{ "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce },
- { "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake }
+ { "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake },
+ { "nativeIsIdling", "(I)Z", (void*)android_os_MessageQueue_nativeIsIdling }
};
#define FIND_CLASS(var, className) \
diff --git a/core/jni/android_os_SystemClock.cpp b/core/jni/android_os_SystemClock.cpp
index 5c135ee..d20b800 100644
--- a/core/jni/android_os_SystemClock.cpp
+++ b/core/jni/android_os_SystemClock.cpp
@@ -19,18 +19,67 @@
* System clock functions.
*/
+#ifdef HAVE_ANDROID_OS
+#include <linux/ioctl.h>
+#include <linux/rtc.h>
+#include <utils/Atomic.h>
+#include <linux/android_alarm.h>
+#endif
+
+#include <sys/time.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
#include "JNIHelp.h"
#include "jni.h"
#include "android_runtime/AndroidRuntime.h"
-#include "utils/SystemClock.h"
-
#include <sys/time.h>
#include <time.h>
+#include <utils/SystemClock.h>
+
namespace android {
/*
+ * Set the current time. This only works when running as root.
+ */
+static int setCurrentTimeMillis(int64_t millis)
+{
+ struct timeval tv;
+ struct timespec ts;
+ int fd;
+ int res;
+ int ret = 0;
+
+ if (millis <= 0 || millis / 1000LL >= INT_MAX) {
+ return -1;
+ }
+
+ tv.tv_sec = (time_t) (millis / 1000LL);
+ tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL);
+
+ ALOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec);
+
+ fd = open("/dev/alarm", O_RDWR);
+ if(fd < 0) {
+ ALOGW("Unable to open alarm driver: %s\n", strerror(errno));
+ return -1;
+ }
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
+ if(res < 0) {
+ ALOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno));
+ ret = -1;
+ }
+ close(fd);
+ return ret;
+}
+
+/*
* native public static void setCurrentTimeMillis(long millis)
*
* Set the current time. This only works when running as root.
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 8766cf9..1c92803 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -986,6 +986,7 @@
}
// From frameworks/base/core/java/android/content/EventLogTags.logtags:
+#define ENABLE_BINDER_SAMPLE 0
#define LOGTAG_BINDER_OPERATION 52004
static void conditionally_log_binder_call(int64_t start_millis,
@@ -1063,6 +1064,7 @@
ALOGV("Java code calling transact on %p in Java object %p with code %d\n",
target, obj, code);
+#if ENABLE_BINDER_SAMPLE
// Only log the binder call duration for things on the Java-level main thread.
// But if we don't
const bool time_binder_calls = should_time_binder_calls();
@@ -1071,12 +1073,15 @@
if (time_binder_calls) {
start_millis = uptimeMillis();
}
+#endif
//printf("Transact from Java code to %p sending: ", target); data->print();
status_t err = target->transact(code, *data, reply, flags);
//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
+#if ENABLE_BINDER_SAMPLE
if (time_binder_calls) {
conditionally_log_binder_call(start_millis, target, code);
}
+#endif
if (err == NO_ERROR) {
return JNI_TRUE;
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 5d32328..33ded03 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -99,6 +99,11 @@
return getpid();
}
+jint android_os_Process_myPpid(JNIEnv* env, jobject clazz)
+{
+ return getppid();
+}
+
jint android_os_Process_myUid(JNIEnv* env, jobject clazz)
{
return getuid();
@@ -673,6 +678,7 @@
PROC_SPACE_TERM = ' ',
PROC_COMBINE = 0x100,
PROC_PARENS = 0x200,
+ PROC_QUOTES = 0x400,
PROC_OUT_STRING = 0x1000,
PROC_OUT_LONG = 0x2000,
PROC_OUT_FLOAT = 0x4000,
@@ -714,9 +720,15 @@
jboolean res = JNI_TRUE;
for (jsize fi=0; fi<NF; fi++) {
- const jint mode = formatData[fi];
+ jint mode = formatData[fi];
if ((mode&PROC_PARENS) != 0) {
i++;
+ } else if ((mode&PROC_QUOTES != 0)) {
+ if (buffer[i] == '"') {
+ i++;
+ } else {
+ mode &= ~PROC_QUOTES;
+ }
}
const char term = (char)(mode&PROC_TERM_MASK);
const jsize start = i;
@@ -732,6 +744,12 @@
}
end = i;
i++;
+ } else if ((mode&PROC_QUOTES) != 0) {
+ while (buffer[i] != '"' && i < endIndex) {
+ i++;
+ }
+ end = i;
+ i++;
}
while (buffer[i] != term && i < endIndex) {
i++;
@@ -990,6 +1008,7 @@
static const JNINativeMethod methods[] = {
{"myPid", "()I", (void*)android_os_Process_myPid},
+ {"myPpid", "()I", (void*)android_os_Process_myPpid},
{"myTid", "()I", (void*)android_os_Process_myTid},
{"myUid", "()I", (void*)android_os_Process_myUid},
{"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index b87fe27..70a4daa 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -16,18 +16,19 @@
#define LOG_TAG "OpenGLRenderer"
-#include <EGL/egl.h>
-
#include "jni.h"
#include "GraphicsJNI.h"
#include <nativehelper/JNIHelp.h>
+#include "android_view_GraphicBuffer.h"
+
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
-#include <gui/GLConsumer.h>
#include <androidfw/ResourceTypes.h>
+#include <gui/GLConsumer.h>
+
#include <private/hwui/DrawGlInfo.h>
#include <cutils/properties.h>
@@ -99,10 +100,11 @@
}
}
-static void android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
+static bool android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
if (Caches::hasInstance()) {
- Caches::getInstance().init();
+ return Caches::getInstance().init();
}
+ return false;
}
static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) {
@@ -112,6 +114,21 @@
}
// ----------------------------------------------------------------------------
+// Caching
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_initAtlas(JNIEnv* env, jobject clazz,
+ jobject graphicBuffer, jintArray atlasMapArray, jint count) {
+
+ sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer);
+ jint* atlasMap = env->GetIntArrayElements(atlasMapArray, NULL);
+
+ Caches::getInstance().assetAtlas.init(buffer, atlasMap, count);
+
+ env->ReleaseIntArrayElements(atlasMapArray, atlasMap, 0);
+}
+
+// ----------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------
@@ -168,6 +185,16 @@
}
}
+static void android_view_GLES20Canvas_setCountOverdrawEnabled(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer, jboolean enabled) {
+ renderer->setCountOverdrawEnabled(enabled);
+}
+
+static jfloat android_view_GLES20Canvas_getOverdraw(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer) {
+ return renderer->getOverdraw();
+}
+
// ----------------------------------------------------------------------------
// Functor
// ----------------------------------------------------------------------------
@@ -271,7 +298,7 @@
static bool android_view_GLES20Canvas_quickReject(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom) {
- return renderer->quickReject(left, top, right, bottom);
+ return renderer->quickRejectNoScissor(left, top, right, bottom);
}
static bool android_view_GLES20Canvas_clipRectF(JNIEnv* env, jobject clazz,
@@ -370,8 +397,8 @@
}
static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject clazz,
- OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, SkMatrix* matrix,
- SkPaint* paint) {
+ OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer,
+ SkMatrix* matrix, SkPaint* paint) {
// This object allows the renderer to allocate a global JNI ref to the buffer object.
JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
@@ -405,8 +432,8 @@
static void android_view_GLES20Canvas_drawBitmapMesh(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer,
- jint meshWidth, jint meshHeight, jfloatArray vertices, jint offset,
- jintArray colors, jint colorOffset, SkPaint* paint) {
+ jint meshWidth, jint meshHeight, jfloatArray vertices, jint offset, jintArray colors,
+ jint colorOffset, SkPaint* paint) {
// This object allows the renderer to allocate a global JNI ref to the buffer object.
JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
@@ -420,20 +447,12 @@
}
static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject clazz,
- OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, jbyteArray chunks,
+ OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, Res_png_9patch* patch,
float left, float top, float right, float bottom, SkPaint* paint) {
// This object allows the renderer to allocate a global JNI ref to the buffer object.
JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
- jbyte* storage = env->GetByteArrayElements(chunks, NULL);
- Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(storage);
- Res_png_9patch::deserialize(patch);
-
- renderer->drawPatch(bitmap, &patch->xDivs[0], &patch->yDivs[0],
- &patch->colors[0], patch->numXDivs, patch->numYDivs, patch->numColors,
- left, top, right, bottom, paint);
-
- env->ReleaseByteArrayElements(chunks, storage, 0);
+ renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
}
static void android_view_GLES20Canvas_drawColor(JNIEnv* env, jobject clazz,
@@ -567,6 +586,20 @@
// Text
// ----------------------------------------------------------------------------
+static float xOffsetForTextAlign(SkPaint* paint, float totalAdvance) {
+ switch (paint->getTextAlign()) {
+ case SkPaint::kCenter_Align:
+ return -totalAdvance / 2.0f;
+ break;
+ case SkPaint::kRight_Align:
+ return -totalAdvance;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
@@ -579,8 +612,13 @@
jfloat totalAdvance = value->getTotalAdvance();
const float* positions = value->getPos();
int bytesCount = glyphsCount * sizeof(jchar);
- renderer->drawText((const char*) glyphs, bytesCount, glyphsCount, x, y,
- positions, paint, totalAdvance);
+ const SkRect& r = value->getBounds();
+ android::uirenderer::Rect bounds(r.fLeft, r.fTop, r.fRight, r.fBottom);
+ bounds.translate(x, y);
+
+ renderer->drawText((const char*) glyphs, bytesCount, glyphsCount,
+ x + xOffsetForTextAlign(paint, totalAdvance), y, positions,
+ paint, totalAdvance, bounds);
}
static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count,
@@ -610,8 +648,13 @@
jfloat totalAdvance = value->getTotalAdvance();
const float* positions = value->getPos();
int bytesCount = glyphsCount * sizeof(jchar);
- renderer->drawText((const char*) glyphs, bytesCount, glyphsCount, x, y,
- positions, paint, totalAdvance);
+ const SkRect& r = value->getBounds();
+ android::uirenderer::Rect bounds(r.fLeft, r.fTop, r.fRight, r.fBottom);
+ bounds.translate(x, y);
+
+ renderer->drawText((const char*) glyphs, bytesCount, glyphsCount,
+ x + xOffsetForTextAlign(paint, totalAdvance), y, positions,
+ paint, totalAdvance, bounds);
}
static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz,
@@ -882,11 +925,21 @@
renderer->pushLayerUpdate(layer);
}
+static void android_view_GLES20Canvas_cancelLayerUpdate(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer, Layer* layer) {
+ renderer->cancelLayerUpdate(layer);
+}
+
static void android_view_GLES20Canvas_clearLayerUpdates(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer) {
renderer->clearLayerUpdates();
}
+static void android_view_GLES20Canvas_flushLayerUpdates(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer) {
+ renderer->flushLayerUpdates();
+}
+
#endif // USE_OPENGL_RENDERER
// ----------------------------------------------------------------------------
@@ -932,9 +985,12 @@
#ifdef USE_OPENGL_RENDERER
{ "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches },
- { "nInitCaches", "()V", (void*) android_view_GLES20Canvas_initCaches },
+ { "nInitCaches", "()Z", (void*) android_view_GLES20Canvas_initCaches },
{ "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches },
+ { "nInitAtlas", "(Landroid/view/GraphicBuffer;[II)V",
+ (void*) android_view_GLES20Canvas_initAtlas },
+
{ "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
{ "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
{ "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport },
@@ -944,6 +1000,9 @@
{ "nSetName", "(ILjava/lang/String;)V",
(void*) android_view_GLES20Canvas_setName },
+ { "nSetCountOverdrawEnabled", "(IZ)V", (void*) android_view_GLES20Canvas_setCountOverdrawEnabled },
+ { "nGetOverdraw", "(I)F", (void*) android_view_GLES20Canvas_getOverdraw },
+
{ "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize },
{ "nCallDrawGLFunction", "(II)I", (void*) android_view_GLES20Canvas_callDrawGLFunction },
@@ -984,7 +1043,7 @@
{ "nDrawBitmapMesh", "(II[BII[FI[III)V",(void*) android_view_GLES20Canvas_drawBitmapMesh },
- { "nDrawPatch", "(II[B[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
+ { "nDrawPatch", "(II[BIFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
{ "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
{ "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
@@ -1053,7 +1112,9 @@
{ "nDrawLayer", "(IIFF)V", (void*) android_view_GLES20Canvas_drawLayer },
{ "nCopyLayer", "(II)Z", (void*) android_view_GLES20Canvas_copyLayer },
{ "nClearLayerUpdates", "(I)V", (void*) android_view_GLES20Canvas_clearLayerUpdates },
+ { "nFlushLayerUpdates", "(I)V", (void*) android_view_GLES20Canvas_flushLayerUpdates },
{ "nPushLayerUpdate", "(II)V", (void*) android_view_GLES20Canvas_pushLayerUpdate },
+ { "nCancelLayerUpdate", "(II)V", (void*) android_view_GLES20Canvas_cancelLayerUpdate },
{ "nSetTextureLayerTransform", "(II)V", (void*) android_view_GLES20Canvas_setTextureLayerTransform },
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
new file mode 100644
index 0000000..d68c0b2
--- /dev/null
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GraphicBuffer"
+
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include "android_os_Parcel.h"
+#include "android_view_GraphicBuffer.h"
+
+#include <android_runtime/AndroidRuntime.h>
+
+#include <binder/Parcel.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
+
+#include <gui/IGraphicBufferAlloc.h>
+#include <gui/ISurfaceComposer.h>
+
+#include <SkCanvas.h>
+#include <SkBitmap.h>
+
+#include <private/gui/ComposerService.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+// Defines
+// ----------------------------------------------------------------------------
+
+// Debug
+#define DEBUG_GRAPHIC_BUFFER 0
+
+// Debug
+#if DEBUG_GRAPHIC_BUFFER
+ #define GB_LOGD(...) ALOGD(__VA_ARGS__)
+ #define GB_LOGW(...) ALOGW(__VA_ARGS__)
+#else
+ #define GB_LOGD(...)
+ #define GB_LOGW(...)
+#endif
+
+#define LOCK_CANVAS_USAGE GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_OFTEN
+
+// ----------------------------------------------------------------------------
+// JNI Helpers
+// ----------------------------------------------------------------------------
+
+static struct {
+ jfieldID mNativeObject;
+} gGraphicBufferClassInfo;
+
+static struct {
+ jmethodID set;
+ jfieldID left;
+ jfieldID top;
+ jfieldID right;
+ jfieldID bottom;
+} gRectClassInfo;
+
+static struct {
+ jfieldID mFinalizer;
+ jfieldID mNativeCanvas;
+ jfieldID mSurfaceFormat;
+} gCanvasClassInfo;
+
+static struct {
+ jfieldID mNativeCanvas;
+} gCanvasFinalizerClassInfo;
+
+#define GET_INT(object, field) \
+ env->GetIntField(object, field)
+
+#define SET_INT(object, field, value) \
+ env->SetIntField(object, field, value)
+
+#define INVOKEV(object, method, ...) \
+ env->CallVoidMethod(object, method, __VA_ARGS__)
+
+// ----------------------------------------------------------------------------
+// Types
+// ----------------------------------------------------------------------------
+
+class GraphicBufferWrapper {
+public:
+ GraphicBufferWrapper(const sp<GraphicBuffer>& buffer): buffer(buffer) {
+ }
+
+ sp<GraphicBuffer> buffer;
+};
+
+// ----------------------------------------------------------------------------
+// GraphicBuffer lifecycle
+// ----------------------------------------------------------------------------
+
+static GraphicBufferWrapper* android_view_GraphiceBuffer_create(JNIEnv* env, jobject clazz,
+ jint width, jint height, jint format, jint usage) {
+
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+ sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
+ if (alloc == NULL) {
+ GB_LOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()");
+ return NULL;
+ }
+
+ status_t error;
+ sp<GraphicBuffer> buffer(alloc->createGraphicBuffer(width, height, format, usage, &error));
+ if (buffer == NULL) {
+ GB_LOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
+ return NULL;
+ }
+
+ return new GraphicBufferWrapper(buffer);
+}
+
+static void android_view_GraphiceBuffer_destroy(JNIEnv* env, jobject clazz,
+ GraphicBufferWrapper* wrapper) {
+ delete wrapper;
+}
+
+// ----------------------------------------------------------------------------
+// Canvas management
+// ----------------------------------------------------------------------------
+
+static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
+ jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
+ SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
+ GET_INT(canvasObj, gCanvasClassInfo.mNativeCanvas));
+ SET_INT(canvasObj, gCanvasClassInfo.mNativeCanvas, (int) newCanvas);
+ SET_INT(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int) newCanvas);
+ SkSafeUnref(previousCanvas);
+}
+
+static inline SkBitmap::Config convertPixelFormat(int32_t format) {
+ switch (format) {
+ case PIXEL_FORMAT_RGBA_8888:
+ return SkBitmap::kARGB_8888_Config;
+ case PIXEL_FORMAT_RGBX_8888:
+ return SkBitmap::kARGB_8888_Config;
+ case PIXEL_FORMAT_RGB_565:
+ return SkBitmap::kRGB_565_Config;
+ default:
+ return SkBitmap::kNo_Config;
+ }
+}
+
+static jboolean android_view_GraphicBuffer_lockCanvas(JNIEnv* env, jobject,
+ GraphicBufferWrapper* wrapper, jobject canvas, jobject dirtyRect) {
+
+ if (!wrapper) {
+ return false;
+ }
+
+ sp<GraphicBuffer> buffer(wrapper->buffer);
+
+ Rect rect;
+ if (dirtyRect) {
+ rect.left = GET_INT(dirtyRect, gRectClassInfo.left);
+ rect.top = GET_INT(dirtyRect, gRectClassInfo.top);
+ rect.right = GET_INT(dirtyRect, gRectClassInfo.right);
+ rect.bottom = GET_INT(dirtyRect, gRectClassInfo.bottom);
+ } else {
+ rect.set(Rect(buffer->getWidth(), buffer->getHeight()));
+ }
+
+ void* bits = NULL;
+ status_t status = buffer->lock(LOCK_CANVAS_USAGE, rect, &bits);
+
+ if (status) return false;
+ if (!bits) {
+ buffer->unlock();
+ return false;
+ }
+
+ ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
+
+ SkBitmap bitmap;
+ bitmap.setConfig(convertPixelFormat(buffer->getPixelFormat()),
+ buffer->getWidth(), buffer->getHeight(), bytesCount);
+
+ if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
+ bitmap.setPixels(bits);
+ } else {
+ bitmap.setPixels(NULL);
+ }
+
+ SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat());
+
+ SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
+ swapCanvasPtr(env, canvas, nativeCanvas);
+
+ SkRect clipRect;
+ clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
+ nativeCanvas->clipRect(clipRect);
+
+ if (dirtyRect) {
+ INVOKEV(dirtyRect, gRectClassInfo.set,
+ int(rect.left), int(rect.top), int(rect.right), int(rect.bottom));
+ }
+
+ return true;
+}
+
+static jboolean android_view_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobject,
+ GraphicBufferWrapper* wrapper, jobject canvas) {
+
+ SkCanvas* nativeCanvas = SkNEW(SkCanvas);
+ swapCanvasPtr(env, canvas, nativeCanvas);
+
+ if (wrapper) {
+ status_t status = wrapper->buffer->unlock();
+ return status == 0;
+ }
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+// Serialization
+// ----------------------------------------------------------------------------
+
+static void android_view_GraphiceBuffer_write(JNIEnv* env, jobject clazz,
+ GraphicBufferWrapper* wrapper, jobject dest) {
+ Parcel* parcel = parcelForJavaObject(env, dest);
+ if (parcel) {
+ parcel->write(*wrapper->buffer);
+ }
+}
+
+static GraphicBufferWrapper* android_view_GraphiceBuffer_read(JNIEnv* env, jobject clazz,
+ jobject in) {
+
+ Parcel* parcel = parcelForJavaObject(env, in);
+ if (parcel) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ parcel->read(*buffer);
+ return new GraphicBufferWrapper(buffer);
+ }
+
+ return NULL;
+}
+
+// ----------------------------------------------------------------------------
+// External helpers
+// ----------------------------------------------------------------------------
+
+sp<GraphicBuffer> graphicBufferForJavaObject(JNIEnv* env, jobject obj) {
+ if (obj) {
+ jint nativeObject = env->GetIntField(obj, gGraphicBufferClassInfo.mNativeObject);
+ GraphicBufferWrapper* wrapper = (GraphicBufferWrapper*) nativeObject;
+ if (wrapper != NULL) {
+ sp<GraphicBuffer> buffer(wrapper->buffer);
+ return buffer;
+ }
+ }
+ return NULL;
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(!var, "Unable to find method " methodName);
+
+const char* const kClassPathName = "android/view/GraphicBuffer";
+
+static JNINativeMethod gMethods[] = {
+ { "nCreateGraphicBuffer", "(IIII)I", (void*) android_view_GraphiceBuffer_create },
+ { "nDestroyGraphicBuffer", "(I)V", (void*) android_view_GraphiceBuffer_destroy },
+
+ { "nWriteGraphicBufferToParcel", "(ILandroid/os/Parcel;)V",
+ (void*) android_view_GraphiceBuffer_write },
+ { "nReadGraphicBufferFromParcel", "(Landroid/os/Parcel;)I",
+ (void*) android_view_GraphiceBuffer_read },
+
+ { "nLockCanvas", "(ILandroid/graphics/Canvas;Landroid/graphics/Rect;)Z",
+ (void*) android_view_GraphicBuffer_lockCanvas },
+ { "nUnlockCanvasAndPost", "(ILandroid/graphics/Canvas;)Z",
+ (void*) android_view_GraphicBuffer_unlockCanvasAndPost },
+};
+
+int register_android_view_GraphicBuffer(JNIEnv* env) {
+ jclass clazz;
+ FIND_CLASS(clazz, "android/view/GraphicBuffer");
+ GET_FIELD_ID(gGraphicBufferClassInfo.mNativeObject, clazz, "mNativeObject", "I");
+
+ FIND_CLASS(clazz, "android/graphics/Rect");
+ GET_METHOD_ID(gRectClassInfo.set, clazz, "set", "(IIII)V");
+ GET_FIELD_ID(gRectClassInfo.left, clazz, "left", "I");
+ GET_FIELD_ID(gRectClassInfo.top, clazz, "top", "I");
+ GET_FIELD_ID(gRectClassInfo.right, clazz, "right", "I");
+ GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I");
+
+ FIND_CLASS(clazz, "android/graphics/Canvas");
+ GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer",
+ "Landroid/graphics/Canvas$CanvasFinalizer;");
+ GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
+ GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
+
+ FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer");
+ GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
+
+ return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/core/jni/android_view_GraphicBuffer.h b/core/jni/android_view_GraphicBuffer.h
new file mode 100644
index 0000000..509587c
--- /dev/null
+++ b/core/jni/android_view_GraphicBuffer.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/GraphicBuffer.h>
+
+#include "jni.h"
+
+namespace android {
+
+// This function does not perform any type checking, the specified
+// object must be an instance of android.view.GraphicBuffer
+extern sp<GraphicBuffer> graphicBufferForJavaObject(JNIEnv* env, jobject obj);
+
+}
diff --git a/core/jni/android_view_HardwareRenderer.cpp b/core/jni/android_view_HardwareRenderer.cpp
index 948a966..479fbe2 100644
--- a/core/jni/android_view_HardwareRenderer.cpp
+++ b/core/jni/android_view_HardwareRenderer.cpp
@@ -20,9 +20,14 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
#include <EGL/egl_cache.h>
+#include <utils/Timers.h>
+
#include <Caches.h>
+#include <Extensions.h>
#ifdef USE_OPENGL_RENDERER
EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
@@ -119,6 +124,13 @@
eglBeginFrame(display, surface);
}
+static jlong android_view_HardwareRenderer_getSystemTime(JNIEnv* env, jobject clazz) {
+ if (uirenderer::Extensions::getInstance().hasNvSystemTime()) {
+ return eglGetSystemTimeNV();
+ }
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
#endif // USE_OPENGL_RENDERER
// ----------------------------------------------------------------------------
@@ -146,6 +158,8 @@
{ "nLoadProperties", "()Z", (void*) android_view_HardwareRenderer_loadProperties },
{ "nBeginFrame", "([I)V", (void*) android_view_HardwareRenderer_beginFrame },
+
+ { "nGetSystemTime", "()J", (void*) android_view_HardwareRenderer_getSystemTime },
#endif
{ "nSetupShadersDiskCache", "(Ljava/lang/String;)V",
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 9fa9fe4..ce475e0 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -21,7 +21,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <binder/Parcel.h>
#include <utils/Log.h>
-#include <androidfw/InputTransport.h>
+#include <input/InputTransport.h>
#include "android_view_InputChannel.h"
#include "android_os_Parcel.h"
#include "android_util_Binder.h"
diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h
index 0967021..2ba2dc0 100644
--- a/core/jni/android_view_InputChannel.h
+++ b/core/jni/android_view_InputChannel.h
@@ -19,7 +19,7 @@
#include "jni.h"
-#include <androidfw/InputTransport.h>
+#include <input/InputTransport.h>
namespace android {
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index e3a54a8..8ef5d0b 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <androidfw/Input.h>
+#include <input/Input.h>
#include <android_runtime/AndroidRuntime.h>
#include <nativehelper/jni.h>
diff --git a/core/jni/android_view_InputDevice.h b/core/jni/android_view_InputDevice.h
index 78651ba..ac38eb1 100644
--- a/core/jni/android_view_InputDevice.h
+++ b/core/jni/android_view_InputDevice.h
@@ -19,7 +19,7 @@
#include "jni.h"
-#include <androidfw/InputDevice.h>
+#include <input/InputDevice.h>
namespace android {
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 8e1e04a..b254de7 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -29,7 +29,7 @@
#include <utils/Looper.h>
#include <utils/Vector.h>
#include <utils/threads.h>
-#include <androidfw/InputTransport.h>
+#include <input/InputTransport.h>
#include "android_os_MessageQueue.h"
#include "android_view_InputChannel.h"
#include "android_view_KeyEvent.h"
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index b46eb4b..e4b65a1 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -29,7 +29,7 @@
#include <utils/Looper.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
-#include <androidfw/InputTransport.h>
+#include <input/InputTransport.h>
#include "android_os_MessageQueue.h"
#include "android_view_InputChannel.h"
#include "android_view_KeyEvent.h"
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index ec56afa..7532c9d 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -23,7 +23,7 @@
#include <android/input.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_InputQueue.h>
-#include <androidfw/Input.h>
+#include <input/Input.h>
#include <utils/Looper.h>
#include <utils/TypeHelpers.h>
#include <ScopedLocalRef.h>
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index 3e56a89..ffe2dea 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -16,8 +16,8 @@
#include <android_runtime/AndroidRuntime.h>
-#include <androidfw/KeyCharacterMap.h>
-#include <androidfw/Input.h>
+#include <input/KeyCharacterMap.h>
+#include <input/Input.h>
#include <binder/Parcel.h>
#include <nativehelper/jni.h>
diff --git a/core/jni/android_view_KeyCharacterMap.h b/core/jni/android_view_KeyCharacterMap.h
index 04024f6..e8465c2 100644
--- a/core/jni/android_view_KeyCharacterMap.h
+++ b/core/jni/android_view_KeyCharacterMap.h
@@ -19,7 +19,7 @@
#include "jni.h"
-#include <androidfw/KeyCharacterMap.h>
+#include <input/KeyCharacterMap.h>
namespace android {
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index 36a98f9..17a0677 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -20,7 +20,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
-#include <androidfw/Input.h>
+#include <input/Input.h>
#include "android_view_KeyEvent.h"
namespace android {
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index e69fb74..73c0683 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -20,7 +20,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
-#include <androidfw/Input.h>
+#include <input/Input.h>
#include "android_os_Parcel.h"
#include "android_view_MotionEvent.h"
#include "android_util_Binder.h"
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index d9f6be9..842a7f7 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -208,25 +208,19 @@
return;
}
- // get dirty region
- Region dirtyRegion;
+ Rect dirtyRect;
+ Rect* dirtyRectPtr = NULL;
+
if (dirtyRectObj) {
- Rect dirty;
- dirty.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
- dirty.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
- dirty.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
- dirty.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
- if (!dirty.isEmpty()) {
- dirtyRegion.set(dirty);
- }
- } else {
- dirtyRegion.set(Rect(0x3FFF, 0x3FFF));
+ dirtyRect.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
+ dirtyRect.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
+ dirtyRect.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
+ dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
+ dirtyRectPtr = &dirtyRect;
}
ANativeWindow_Buffer outBuffer;
- Rect dirtyBounds(dirtyRegion.getBounds());
- status_t err = surface->lock(&outBuffer, &dirtyBounds);
- dirtyRegion.set(dirtyBounds);
+ status_t err = surface->lock(&outBuffer, dirtyRectPtr);
if (err < 0) {
const char* const exception = (err == NO_MEMORY) ?
OutOfResourcesException :
@@ -254,27 +248,15 @@
SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
swapCanvasPtr(env, canvasObj, nativeCanvas);
- SkRegion clipReg;
- if (dirtyRegion.isRect()) { // very common case
- const Rect b(dirtyRegion.getBounds());
- clipReg.setRect(b.left, b.top, b.right, b.bottom);
- } else {
- size_t count;
- Rect const* r = dirtyRegion.getArray(&count);
- while (count) {
- clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op);
- r++, count--;
- }
+ if (dirtyRectPtr) {
+ nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) );
}
- nativeCanvas->clipRegion(clipReg);
-
if (dirtyRectObj) {
- const Rect& bounds(dirtyRegion.getBounds());
- env->SetIntField(dirtyRectObj, gRectClassInfo.left, bounds.left);
- env->SetIntField(dirtyRectObj, gRectClassInfo.top, bounds.top);
- env->SetIntField(dirtyRectObj, gRectClassInfo.right, bounds.right);
- env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, bounds.bottom);
+ env->SetIntField(dirtyRectObj, gRectClassInfo.left, dirtyRect.left);
+ env->SetIntField(dirtyRectObj, gRectClassInfo.top, dirtyRect.top);
+ env->SetIntField(dirtyRectObj, gRectClassInfo.right, dirtyRect.right);
+ env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom);
}
}
@@ -392,7 +374,7 @@
jclass clazz = env->FindClass("android/view/Surface");
gSurfaceClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
gSurfaceClassInfo.mNativeObject =
- env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeSurface", "I");
+ env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeObject", "I");
gSurfaceClassInfo.mLock =
env->GetFieldID(gSurfaceClassInfo.clazz, "mLock", "Ljava/lang/Object;");
gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "(I)V");
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 5baae84..d515696 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -126,19 +126,19 @@
}
static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
- jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
- SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
+ jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
+ SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
- env->SetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas, (int)newCanvas);
- env->SetIntField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int)newCanvas);
- SkSafeUnref(previousCanvas);
+ env->SetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas, (int)newCanvas);
+ env->SetIntField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int)newCanvas);
+ SkSafeUnref(previousCanvas);
}
-static void android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
+static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
jint nativeWindow, jobject canvas, jobject dirtyRect) {
if (!nativeWindow) {
- return;
+ return false;
}
ANativeWindow_Buffer buffer;
@@ -154,7 +154,8 @@
}
sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
- native_window_lock(window.get(), &buffer, &rect);
+ int32_t status = native_window_lock(window.get(), &buffer, &rect);
+ if (status) return false;
ssize_t bytesCount = buffer.stride * bytesPerPixel(buffer.format);
@@ -184,6 +185,8 @@
INVOKEV(dirtyRect, gRectClassInfo.set,
int(rect.left), int(rect.top), int(rect.right), int(rect.bottom));
}
+
+ return true;
}
static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
@@ -213,7 +216,7 @@
{ "nDestroyNativeWindow", "()V",
(void*) android_view_TextureView_destroyNativeWindow },
- { "nLockCanvas", "(ILandroid/graphics/Canvas;Landroid/graphics/Rect;)V",
+ { "nLockCanvas", "(ILandroid/graphics/Canvas;Landroid/graphics/Rect;)Z",
(void*) android_view_TextureView_lockCanvas },
{ "nUnlockCanvasAndPost", "(ILandroid/graphics/Canvas;)V",
(void*) android_view_TextureView_unlockCanvasAndPost },
@@ -241,7 +244,8 @@
GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I");
FIND_CLASS(clazz, "android/graphics/Canvas");
- GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer", "Landroid/graphics/Canvas$CanvasFinalizer;");
+ GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer",
+ "Landroid/graphics/Canvas$CanvasFinalizer;");
GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index c2fa3be..90ba2ba 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -20,8 +20,8 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
-#include <androidfw/Input.h>
-#include <androidfw/VelocityTracker.h>
+#include <input/Input.h>
+#include <input/VelocityTracker.h>
#include "android_view_MotionEvent.h"
#include <ScopedUtfChars.h>
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 8d6fab4..bf5accd 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -20,7 +20,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
-#include <utils/ZipFileRO.h>
+#include <androidfw/ZipFileRO.h>
#include <ScopedUtfChars.h>
#include <zlib.h>
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index badd697..b0c26c51 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -1181,7 +1181,7 @@
(JNIEnv *_env, jobject _this) {
GLenum _returnValue;
_returnValue = glGetError();
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glGetIntegerv ( GLenum pname, GLint *params ) */
@@ -4017,7 +4017,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jint)_returnValue;
}
/* GLbitfield glQueryMatrixxOES ( GLfixed *mantissa, GLint *exponent ) */
@@ -4074,7 +4074,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
}
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glBindBuffer ( GLenum target, GLuint buffer ) */
@@ -4359,7 +4359,7 @@
(GLint)size,
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
@@ -4460,7 +4460,7 @@
(GLenum)mode,
(GLsizei)count,
(GLenum)type,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -6067,7 +6067,7 @@
_returnValue = glIsBuffer(
(GLuint)buffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsEnabled ( GLenum cap ) */
@@ -6078,7 +6078,7 @@
_returnValue = glIsEnabled(
(GLenum)cap
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsTexture ( GLuint texture ) */
@@ -6089,7 +6089,7 @@
_returnValue = glIsTexture(
(GLuint)texture
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* void glNormalPointer ( GLenum type, GLsizei stride, GLint offset ) */
@@ -6099,7 +6099,7 @@
glNormalPointer(
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
@@ -6326,7 +6326,7 @@
(GLint)size,
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
@@ -6756,7 +6756,7 @@
(GLint)size,
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
@@ -7196,7 +7196,7 @@
(GLint)size,
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
@@ -7232,7 +7232,7 @@
(GLint)size,
(GLenum)type,
(GLsizei)stride,
- (const GLvoid *)offset
+ (GLvoid *)offset
);
}
@@ -7325,7 +7325,7 @@
_returnValue = glCheckFramebufferStatusOES(
(GLint)target
);
- return _returnValue;
+ return (jint)_returnValue;
}
/* void glDeleteFramebuffersOES ( GLint n, GLuint *framebuffers ) */
@@ -8166,7 +8166,7 @@
_returnValue = glIsFramebufferOES(
(GLint)framebuffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* GLboolean glIsRenderbufferOES ( GLint renderbuffer ) */
@@ -8182,7 +8182,7 @@
_returnValue = glIsRenderbufferOES(
(GLint)renderbuffer
);
- return _returnValue;
+ return (jboolean)_returnValue;
}
/* void glRenderbufferStorageOES ( GLint target, GLint internalformat, GLint width, GLint height ) */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4aa540a..7ddb0dc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -29,8 +29,10 @@
<protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
<protected-broadcast android:name="android.intent.action.SCREEN_ON" />
<protected-broadcast android:name="android.intent.action.USER_PRESENT" />
+ <protected-broadcast android:name="android.intent.action.TIME_SET" />
<protected-broadcast android:name="android.intent.action.TIME_TICK" />
<protected-broadcast android:name="android.intent.action.TIMEZONE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.DATE_CHANGED" />
<protected-broadcast android:name="android.intent.action.BOOT_COMPLETED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_INSTALL" />
<protected-broadcast android:name="android.intent.action.PACKAGE_ADDED" />
@@ -64,6 +66,9 @@
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
<protected-broadcast android:name="android.intent.action.USER_ADDED" />
<protected-broadcast android:name="android.intent.action.USER_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.USER_STARTING" />
+ <protected-broadcast android:name="android.intent.action.USER_STARTED" />
+ <protected-broadcast android:name="android.intent.action.USER_STOPPING" />
<protected-broadcast android:name="android.intent.action.USER_STOPPED" />
<protected-broadcast android:name="android.intent.action.USER_BACKGROUND" />
<protected-broadcast android:name="android.intent.action.USER_FOREGROUND" />
@@ -89,6 +94,8 @@
<protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />
<protected-broadcast android:name="android.bluetooth.adapter.action.LOCAL_NAME_CHANGED" />
<protected-broadcast android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.UUID" />
+ <protected-broadcast android:name="android.bluetooth.device.action.ALIAS_CHANGED" />
<protected-broadcast android:name="android.bluetooth.device.action.FOUND" />
<protected-broadcast android:name="android.bluetooth.device.action.DISAPPEARED" />
<protected-broadcast android:name="android.bluetooth.device.action.CLASS_CHANGED" />
@@ -131,12 +138,34 @@
<protected-broadcast android:name="android.intent.action.USB_AUDIO_ACCESSORY_PLUG" />
<protected-broadcast android:name="android.intent.action.USB_AUDIO_DEVICE_PLUG" />
+ <protected-broadcast android:name="android.media.AUDIO_BECOMING_NOISY" />
+ <protected-broadcast android:name="android.media.RINGER_MODE_CHANGED" />
+ <protected-broadcast android:name="android.media.VIBRATE_SETTING_CHANGED" />
+ <protected-broadcast android:name="android.media.VOLUME_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.MASTER_VOLUME_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.MASTER_MUTE_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.SCO_AUDIO_STATE_CHANGED" />
+ <protected-broadcast android:name="android.media.ACTION_SCO_AUDIO_STATE_UPDATED" />
+
+ <protected-broadcast android:name="android.intent.action.MEDIA_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_CHECKING" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_NOFS" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_MOUNTED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_SHARED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_UNSHARED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_BAD_REMOVAL" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTABLE" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_EJECT" />
+
<protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
<protected-broadcast android:name="android.net.conn.DATA_ACTIVITY_CHANGE" />
<protected-broadcast android:name="android.net.conn.BACKGROUND_DATA_SETTING_CHANGED" />
<protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED" />
+ <protected-broadcast android:name="android.net.nsd.STATE_CHANGED" />
+
<protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
<protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_ON_DETECTED" />
<protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED" />
@@ -181,6 +210,14 @@
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
+ <protected-broadcast android:name="android.intent.action.HDMI_PLUGGED" />
+
+ <protected-broadcast android:name="android.intent.action.PHONE_STATE" />
+
+ <protected-broadcast android:name="android.location.GPS_ENABLED_CHANGE" />
+ <protected-broadcast android:name="android.location.PROVIDERS_CHANGED" />
+ <protected-broadcast android:name="android.location.GPS_FIX_CHANGE" />
+
<!-- ====================================== -->
<!-- Permissions for things that cost money -->
<!-- ====================================== -->
@@ -1085,6 +1122,14 @@
android:description="@string/permdesc_mediaStorageWrite"
android:protectionLevel="signature|system" />
+ <!-- Allows an application to manage access to documents, usually as part
+ of a document picker. -->
+ <permission android:name="android.permission.MANAGE_DOCUMENTS"
+ android:permissionGroup="android.permission-group.STORAGE"
+ android:label="@string/permlab_manageDocs"
+ android:description="@string/permdesc_manageDocs"
+ android:protectionLevel="signature|system" />
+
<!-- ================================== -->
<!-- Permissions for screenlock -->
<!-- ================================== -->
@@ -1177,6 +1222,13 @@
android:label="@string/permlab_removeTasks"
android:description="@string/permdesc_removeTasks" />
+ <!-- @hide Allows an application to create/manage/remove stacks -->
+ <permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
+ android:permissionGroup="android.permission-group.APP_INFO"
+ android:protectionLevel="signature"
+ android:label="@string/permlab_manageActivityStacks"
+ android:description="@string/permdesc_manageActivityStacks" />
+
<!-- Allows an application to start any activity, regardless of permission
protection or exported state. @hide -->
<permission android:name="android.permission.START_ANY_ACTIVITY"
@@ -1805,6 +1857,22 @@
android:description="@string/permdesc_bindAccessibilityService"
android:protectionLevel="signature" />
+ <!-- Must be required by an {@link android.printservice.PrintService},
+ to ensure that only the system can bind to it. -->
+ <permission android:name="android.permission.BIND_PRINT_SERVICE"
+ android:label="@string/permlab_bindPrintService"
+ android:description="@string/permdesc_bindPrintService"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to call APIs that give it access to all print jobs
+ on the device. Usually an app can access only the print jobts it created.
+ This permission is not available to third party applications.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"
+ android:label="@string/permlab_accessAllPrintJobs"
+ android:description="@string/permdesc_accessAllPrintJobs"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a TextService (e.g. SpellCheckerService)
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_TEXT_SERVICE"
@@ -2248,6 +2316,20 @@
android:description="@string/permdesc_accessNotifications"
android:protectionLevel="signature|system" />
+ <!-- Allows access to keyguard secure storage. Only allowed for system processes.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
+ android:protectionLevel="signature"
+ android:label="@string/permlab_access_keyguard_secure_storage"
+ android:description="@string/permdesc_access_keyguard_secure_storage" />
+
+ <!-- Allows an application to control keyguard. Only allowed for system processes.
+ @hide -->
+ <permission android:name="android.permission.CONTROL_KEYGUARD"
+ android:protectionLevel="signature"
+ android:label="@string/permlab_control_keyguard"
+ android:description="@string/permdesc_control_keyguard" />
+
<!-- Must be required by an {@link
android.service.notification.NotificationListenerService},
to ensure that only the system can bind to it. -->
@@ -2322,8 +2404,7 @@
<activity android:name="android.accounts.CantAddAccountActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@android:style/Theme.Holo.Dialog"
- android:label="@string/error_message_title"
+ android:theme="@android:style/Theme.Holo.Dialog.NoActionBar"
android:process=":ui">
</activity>
@@ -2359,6 +2440,29 @@
android:process=":ui">
</activity>
+ <activity android:name="com.android.internal.app.RestrictionsPinSetupActivity"
+ android:theme="@style/Theme.Holo.Dialog.Alert"
+ android:permission="android.permission.MANAGE_USERS"
+ android:excludeFromRecents="true"
+ android:windowSoftInputMode="adjustPan"
+ android:process=":ui">
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.RESTRICTIONS_PIN_CREATE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="com.android.internal.app.RestrictionsPinActivity"
+ android:theme="@style/Theme.Holo.Dialog.Alert"
+ android:excludeFromRecents="true"
+ android:windowSoftInputMode="adjustPan"
+ android:process=":ui">
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.RESTRICTIONS_PIN_CHALLENGE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<receiver android:name="com.android.server.BootReceiver"
android:primaryUserOnly="true">
<intent-filter>
@@ -2397,9 +2501,9 @@
</receiver>
<receiver android:name="com.android.server.MasterClearReceiver"
- android:permission="android.permission.MASTER_CLEAR"
- android:priority="100" >
- <intent-filter>
+ android:permission="android.permission.MASTER_CLEAR">
+ <intent-filter
+ android:priority="100" >
<!-- For Checkin, Settings, etc.: action=MASTER_CLEAR -->
<action android:name="android.intent.action.MASTER_CLEAR" />
diff --git a/core/res/res/anim/keyguard_security_fade_in.xml b/core/res/res/anim/keyguard_security_fade_in.xml
deleted file mode 100644
index 6293432..0000000
--- a/core/res/res/anim/keyguard_security_fade_in.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<alpha xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:interpolator/decelerate_quad"
- android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@*android:integer/kg_security_fade_duration" />
-
-
diff --git a/core/res/res/anim/keyguard_security_fade_out.xml b/core/res/res/anim/keyguard_security_fade_out.xml
deleted file mode 100644
index 4ab0229..0000000
--- a/core/res/res/anim/keyguard_security_fade_out.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<alpha xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:interpolator/accelerate_quad"
- android:fromAlpha="1.0"
- android:toAlpha="0.0"
- android:duration="@*android:integer/kg_security_fade_duration"
-/>
diff --git a/core/res/res/anim/toast_bar_enter.xml b/core/res/res/anim/toast_bar_enter.xml
new file mode 100644
index 0000000..5c0dfcf
--- /dev/null
+++ b/core/res/res/anim/toast_bar_enter.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <translate android:fromYDelta="10%" android:toYDelta="0"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:duration="@android:integer/config_shortAnimTime"/>
+ <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+ android:interpolator="@interpolator/decelerate_cubic"
+ android:duration="@android:integer/config_shortAnimTime" />
+</set>
diff --git a/core/res/res/anim/toast_bar_exit.xml b/core/res/res/anim/toast_bar_exit.xml
new file mode 100644
index 0000000..4e3b7da
--- /dev/null
+++ b/core/res/res/anim/toast_bar_exit.xml
@@ -0,0 +1,26 @@
+<?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.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <translate android:fromYDelta="0" android:toYDelta="10%"
+ android:interpolator="@interpolator/accelerate_quint"
+ android:duration="@android:integer/config_shortAnimTime"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:interpolator="@interpolator/accelerate_cubic"
+ android:duration="@android:integer/config_shortAnimTime"/>
+</set>
diff --git a/core/res/res/drawable-hdpi/kg_add_widget.png b/core/res/res/drawable-hdpi/kg_add_widget.png
deleted file mode 100644
index 68971a5..0000000
--- a/core/res/res/drawable-hdpi/kg_add_widget.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_lock_focused.png b/core/res/res/drawable-hdpi/kg_security_lock_focused.png
deleted file mode 100644
index abcf683..0000000
--- a/core/res/res/drawable-hdpi/kg_security_lock_focused.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_lock_normal.png b/core/res/res/drawable-hdpi/kg_security_lock_normal.png
deleted file mode 100644
index e8cff24..0000000
--- a/core/res/res/drawable-hdpi/kg_security_lock_normal.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_lock_pressed.png b/core/res/res/drawable-hdpi/kg_security_lock_pressed.png
deleted file mode 100644
index 3214dcb..0000000
--- a/core/res/res/drawable-hdpi/kg_security_lock_pressed.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/toast_bar_bg.9.png b/core/res/res/drawable-hdpi/toast_bar_bg.9.png
new file mode 100644
index 0000000..2396b26
--- /dev/null
+++ b/core/res/res/drawable-hdpi/toast_bar_bg.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_add_widget.png b/core/res/res/drawable-mdpi/kg_add_widget.png
deleted file mode 100644
index 136ae17..0000000
--- a/core/res/res/drawable-mdpi/kg_add_widget.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_lock_focused.png b/core/res/res/drawable-mdpi/kg_security_lock_focused.png
deleted file mode 100644
index c567a82..0000000
--- a/core/res/res/drawable-mdpi/kg_security_lock_focused.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_lock_normal.png b/core/res/res/drawable-mdpi/kg_security_lock_normal.png
deleted file mode 100644
index 6fbecc1..0000000
--- a/core/res/res/drawable-mdpi/kg_security_lock_normal.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_lock_pressed.png b/core/res/res/drawable-mdpi/kg_security_lock_pressed.png
deleted file mode 100644
index a883258..0000000
--- a/core/res/res/drawable-mdpi/kg_security_lock_pressed.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/toast_bar_bg.9.png b/core/res/res/drawable-mdpi/toast_bar_bg.9.png
new file mode 100644
index 0000000..291a936
--- /dev/null
+++ b/core/res/res/drawable-mdpi/toast_bar_bg.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_add_widget.png b/core/res/res/drawable-xhdpi/kg_add_widget.png
deleted file mode 100644
index ca48be2..0000000
--- a/core/res/res/drawable-xhdpi/kg_add_widget.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_lock_focused.png b/core/res/res/drawable-xhdpi/kg_security_lock_focused.png
deleted file mode 100644
index ee21647..0000000
--- a/core/res/res/drawable-xhdpi/kg_security_lock_focused.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_lock_normal.png b/core/res/res/drawable-xhdpi/kg_security_lock_normal.png
deleted file mode 100644
index eae7d8c..0000000
--- a/core/res/res/drawable-xhdpi/kg_security_lock_normal.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_lock_pressed.png b/core/res/res/drawable-xhdpi/kg_security_lock_pressed.png
deleted file mode 100644
index 5e9a52b..0000000
--- a/core/res/res/drawable-xhdpi/kg_security_lock_pressed.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/toast_bar_bg.9.png b/core/res/res/drawable-xhdpi/toast_bar_bg.9.png
new file mode 100644
index 0000000..1dc4927
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/toast_bar_bg.9.png
Binary files differ
diff --git a/core/res/res/drawable/lockscreen_password_field_dark.xml b/core/res/res/drawable/lockscreen_password_field_dark.xml
deleted file mode 100644
index 92ceb79..0000000
--- a/core/res/res/drawable/lockscreen_password_field_dark.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_bg_default_holo_dark" />
- <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_bg_disabled_holo_dark" />
- <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_bg_activated_holo_dark" />
- <iten android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_bg_focused_holo_dark" />
- <item android:state_enabled="true" android:drawable="@drawable/textfield_bg_default_holo_dark" />
- <item android:state_focused="true" android:drawable="@drawable/textfield_bg_disabled_focused_holo_dark" />
- <item android:drawable="@drawable/textfield_bg_disabled_holo_dark" />
-</selector>
-
diff --git a/core/res/res/layout-land/keyguard_glow_pad_container.xml b/core/res/res/layout-land/keyguard_glow_pad_container.xml
deleted file mode 100644
index f8364f1..0000000
--- a/core/res/res/layout-land/keyguard_glow_pad_container.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <include layout="@layout/keyguard_glow_pad_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center" />
-</merge>
\ No newline at end of file
diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml
deleted file mode 100644
index 6b36235..0000000
--- a/core/res/res/layout-land/keyguard_host_view.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the host view that generally contains two sub views: the widget view
- and the security view. -->
-<com.android.internal.policy.impl.keyguard.KeyguardHostView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_host_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
-
- <com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout
- android:id="@+id/multi_pane_challenge"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false">
-
- <include layout="@layout/keyguard_widget_remove_drop_target"
- android:id="@+id/keyguard_widget_pager_delete_target"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|center_horizontal"
- androidprv:layout_childType="pageDeleteDropTarget" />
-
- <include layout="@layout/keyguard_widget_pager"
- android:id="@+id/app_widget_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_centerWithinArea="0.55"
- androidprv:layout_childType="widget"
- androidprv:layout_maxWidth="480dp"
- androidprv:layout_maxHeight="480dp" />
- <include layout="@layout/keyguard_multi_user_selector"/>
-
- <View android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_childType="scrim"
- android:background="#99000000" />
-
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
- android:id="@+id/keyguard_security_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- androidprv:layout_childType="challenge"
- androidprv:layout_centerWithinArea="0.55">
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingLeft="@dimen/keyguard_security_view_margin"
- android:paddingTop="@dimen/keyguard_security_view_margin"
- android:paddingRight="@dimen/keyguard_security_view_margin"
- android:paddingBottom="@dimen/keyguard_security_view_margin"
- android:gravity="center">
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
-
- </com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardHostView>
-
diff --git a/core/res/res/layout-land/keyguard_widget_pager.xml b/core/res/res/layout-land/keyguard_widget_pager.xml
deleted file mode 100644
index 02c6d0e..0000000
--- a/core/res/res/layout-land/keyguard_widget_pager.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the selector widget that allows the user to select an action. -->
-<com.android.internal.policy.impl.keyguard.KeyguardWidgetCarousel
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingLeft="25dp"
- android:paddingRight="25dp"
- android:paddingTop="25dp"
- android:paddingBottom="25dp"
- android:clipToPadding="false"
- androidprv:pageSpacing="10dp">
-</com.android.internal.policy.impl.keyguard.KeyguardWidgetCarousel>
\ No newline at end of file
diff --git a/core/res/res/layout-port/keyguard_host_view.xml b/core/res/res/layout-port/keyguard_host_view.xml
deleted file mode 100644
index fb25f9c..0000000
--- a/core/res/res/layout-port/keyguard_host_view.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the host view that generally contains two sub views: the widget view
- and the security view. -->
-<com.android.internal.policy.impl.keyguard.KeyguardHostView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_host_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical">
-
- <com.android.internal.policy.impl.keyguard.SlidingChallengeLayout
- android:id="@+id/sliding_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- androidprv:layout_childType="pageDeleteDropTarget">
- <include layout="@layout/keyguard_widget_remove_drop_target"
- android:id="@+id/keyguard_widget_pager_delete_target"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|center_horizontal" />
- </FrameLayout>
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_childType="widgets">
- <include layout="@layout/keyguard_widget_pager"
- android:id="@+id/app_widget_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"/>
- </FrameLayout>
-
- <View android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_childType="scrim"
- android:background="#99000000" />
-
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
- android:id="@+id/keyguard_security_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- androidprv:layout_childType="challenge"
- android:padding="0dp"
- android:gravity="bottom|center_horizontal">
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingTop="@dimen/keyguard_security_view_margin"
- android:gravity="center">
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
-
- <ImageButton
- android:layout_width="match_parent"
- android:layout_height="@dimen/kg_widget_pager_bottom_padding"
- androidprv:layout_childType="expandChallengeHandle"
- android:focusable="true"
- android:background="@null"
- android:src="@drawable/keyguard_expand_challenge_handle"
- android:scaleType="center"
- android:contentDescription="@string/keyguard_accessibility_expand_lock_area" />
-
- </com.android.internal.policy.impl.keyguard.SlidingChallengeLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardHostView>
-
diff --git a/core/res/res/layout-port/keyguard_widget_pager.xml b/core/res/res/layout-port/keyguard_widget_pager.xml
deleted file mode 100644
index 7f22709..0000000
--- a/core/res/res/layout-port/keyguard_widget_pager.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the selector widget that allows the user to select an action. -->
-<com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/app_widget_container"
- android:paddingLeft="25dp"
- android:paddingRight="25dp"
- android:paddingTop="25dp"
- android:paddingBottom="@dimen/kg_widget_pager_bottom_padding"
- android:clipToPadding="false"
- androidprv:pageSpacing="10dp">
-</com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
diff --git a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
deleted file mode 100644
index e3d577d..0000000
--- a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the host view that generally contains two sub views: the widget view
- and the security view. -->
-<com.android.internal.policy.impl.keyguard.KeyguardHostView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_host_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
-
- <com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout
- android:id="@+id/multi_pane_challenge"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:orientation="vertical">
-
- <include layout="@layout/keyguard_widget_remove_drop_target"
- android:id="@+id/keyguard_widget_pager_delete_target"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|center_horizontal"
- androidprv:layout_childType="pageDeleteDropTarget" />
-
- <include layout="@layout/keyguard_widget_pager"
- android:id="@+id/app_widget_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_centerWithinArea="0.5"
- androidprv:layout_childType="widget"
- androidprv:layout_maxWidth="480dp"
- androidprv:layout_maxHeight="480dp" />
-
- <include layout="@layout/keyguard_multi_user_selector"/>
-
- <View android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_childType="scrim"
- android:background="#99000000" />
-
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
- android:id="@+id/keyguard_security_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- androidprv:layout_centerWithinArea="0.5"
- androidprv:layout_childType="challenge"
- android:layout_gravity="center_horizontal|bottom">
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingLeft="@dimen/keyguard_security_view_margin"
- android:paddingTop="@dimen/keyguard_security_view_margin"
- android:paddingRight="@dimen/keyguard_security_view_margin"
- android:paddingBottom="@dimen/keyguard_security_view_margin"
- android:gravity="center">
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
-
- </com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardHostView>
diff --git a/core/res/res/layout-sw600dp-port/keyguard_status_area.xml b/core/res/res/layout-sw600dp-port/keyguard_status_area.xml
deleted file mode 100644
index 88dd760..0000000
--- a/core/res/res/layout-sw600dp-port/keyguard_status_area.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<!-- This is a view that shows general status information in Keyguard. -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_status_area"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:layout_marginTop="-16dp"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:layout_marginEnd="@dimen/kg_status_line_font_right_margin"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@dimen/kg_status_date_font_size"
- />
-
- <TextView
- android:id="@+id/alarm_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:layout_marginTop="28dp"
- android:layout_marginEnd="@dimen/kg_status_line_font_right_margin"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearance"
- android:textSize="@dimen/kg_status_line_font_size"
- android:drawablePadding="4dip"
- />
-
-</LinearLayout>
diff --git a/core/res/res/layout-sw600dp/keyguard_navigation.xml b/core/res/res/layout-sw600dp/keyguard_navigation.xml
deleted file mode 100644
index 2e6fa37..0000000
--- a/core/res/res/layout-sw600dp/keyguard_navigation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <include layout="@layout/default_navigation" />
-</merge>
diff --git a/core/res/res/layout-sw600dp/keyguard_sim_puk_pin_navigation.xml b/core/res/res/layout-sw600dp/keyguard_sim_puk_pin_navigation.xml
deleted file mode 100644
index 2e6fa37..0000000
--- a/core/res/res/layout-sw600dp/keyguard_sim_puk_pin_navigation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <include layout="@layout/default_navigation" />
-</merge>
diff --git a/core/res/res/layout-sw600dp/keyguard_transport_control.xml b/core/res/res/layout-sw600dp/keyguard_transport_control.xml
deleted file mode 100644
index f864339..0000000
--- a/core/res/res/layout-sw600dp/keyguard_transport_control.xml
+++ /dev/null
@@ -1,111 +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.
--->
-
-<!-- *** Note *** This should mirror the file in layout/ with the exception of the background set
- here for adding a drop shadow on tablets. -->
-
-<com.android.internal.widget.TransportControlView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/transport_controls"
- android:background="@drawable/transportcontrol_bg">
-
- <!-- FrameLayout used as scrim to show between album art and buttons -->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:foreground="@drawable/ic_lockscreen_player_background">
- <!-- We use ImageView for its cropping features, otherwise could be android:background -->
- <ImageView
- android:id="@+id/albumart"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="fill"
- android:scaleType="centerCrop"
- android:adjustViewBounds="false"
- />
- </FrameLayout>
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom">
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dip"
- android:layout_marginStart="16dip"
- android:layout_marginEnd="16dip"
- android:gravity="center_horizontal"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceMedium"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="5dip">
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1">
- <ImageView
- android:id="@+id/btn_prev"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:src="@drawable/ic_media_previous"
- android:clickable="true"
- android:background="?android:attr/selectableItemBackground"
- android:padding="10dip"
- android:contentDescription="@string/lockscreen_transport_prev_description"/>
- </FrameLayout>
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1">
- <ImageView
- android:id="@+id/btn_play"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:clickable="true"
- android:src="@drawable/ic_media_play"
- android:background="?android:attr/selectableItemBackground"
- android:padding="10dip"
- android:contentDescription="@string/lockscreen_transport_play_description"/>
- </FrameLayout>
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1">
- <ImageView
- android:id="@+id/btn_next"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:clickable="true"
- android:src="@drawable/ic_media_next"
- android:background="?android:attr/selectableItemBackground"
- android:padding="10dip"
- android:contentDescription="@string/lockscreen_transport_next_description"/>
- </FrameLayout>
- </LinearLayout>
- </LinearLayout>
-
-</com.android.internal.widget.TransportControlView>
diff --git a/core/res/res/layout/keyguard_account_view.xml b/core/res/res/layout/keyguard_account_view.xml
deleted file mode 100644
index fa36371..0000000
--- a/core/res/res/layout/keyguard_account_view.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<com.android.internal.policy.impl.keyguard.KeyguardAccountView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_account_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- android:orientation="vertical">
-
- <include layout="@layout/keyguard_message_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
-
- <EditText
- android:id="@+id/login"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dip"
- android:layout_marginStart="7dip"
- android:layout_marginEnd="7dip"
- android:layout_alignParentTop="true"
- android:hint="@string/kg_login_username_hint"
- android:inputType="textEmailAddress"
- />
-
- <EditText
- android:id="@+id/password"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/login"
- android:layout_toLeftOf="@+id/ok"
- android:layout_marginTop="15dip"
- android:layout_marginStart="7dip"
- android:layout_marginEnd="7dip"
- android:inputType="textPassword"
- android:hint="@string/kg_login_password_hint"
- android:nextFocusRight="@+id/ok"
- android:nextFocusDown="@+id/ok"
- />
-
- <!-- ok below password, aligned to right of screen -->
- <Button
- android:id="@+id/ok"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="7dip"
- android:layout_alignParentEnd="true"
- android:layout_below="@id/login"
- android:text="@string/kg_login_submit_button"
- />
-
- </RelativeLayout>
-
- <!-- no room for ECA on this screen right now
- <include layout="@layout/keyguard_eca"
- android:id="@+id/keyguard_selector_fade_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="bottom|center_horizontal"
- android:gravity="center_horizontal" />
- -->
-
-</com.android.internal.policy.impl.keyguard.KeyguardAccountView>
diff --git a/core/res/res/layout/keyguard_add_widget.xml b/core/res/res/layout/keyguard_add_widget.xml
deleted file mode 100644
index d043fdb..0000000
--- a/core/res/res/layout/keyguard_add_widget.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<!-- This is a view that shows general status information in Keyguard. -->
-<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_add_widget"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- >
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:contentDescription="@string/keyguard_accessibility_widget_empty_slot"
- >
- <ImageView
- android:id="@+id/keyguard_add_widget_view"
- android:clickable="true"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:padding="24dp"
- android:src="@drawable/keyguard_add_widget_button"
- android:contentDescription="@string/keyguard_accessibility_add_widget"/>
- </FrameLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
diff --git a/core/res/res/layout/keyguard_emergency_carrier_area.xml b/core/res/res/layout/keyguard_emergency_carrier_area.xml
deleted file mode 100644
index b8a7654..0000000
--- a/core/res/res/layout/keyguard_emergency_carrier_area.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
-<com.android.internal.policy.impl.keyguard.EmergencyCarrierArea
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center"
- android:layout_gravity="center_horizontal"
- android:layout_alignParentBottom="true"
- android:clickable="true">
-
- <com.android.internal.policy.impl.keyguard.CarrierText
- android:id="@+id/carrier_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-10dip"
- style="?android:attr/buttonBarStyle"
- android:orientation="horizontal"
- android:gravity="center"
- android:weightSum="2">
-
- <com.android.internal.policy.impl.keyguard.EmergencyButton
- android:id="@+id/emergency_call_button"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawableLeft="@*android:drawable/lockscreen_emergency_button"
- android:text="@string/kg_emergency_call_label"
- style="?android:attr/buttonBarButtonStyle"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
- android:drawablePadding="8dip" />
-
- <Button android:id="@+id/forgot_password_button"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawableLeft="@*android:drawable/lockscreen_forgot_password_button"
- style="?android:attr/buttonBarButtonStyle"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:drawablePadding="8dip"
- android:visibility="gone"/>
- </LinearLayout>
-
-</com.android.internal.policy.impl.keyguard.EmergencyCarrierArea>
diff --git a/core/res/res/layout/keyguard_face_unlock_view.xml b/core/res/res/layout/keyguard_face_unlock_view.xml
deleted file mode 100644
index c9f1176..0000000
--- a/core/res/res/layout/keyguard_face_unlock_view.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the screen that allows the user to unlock by showing their face. -->
-<com.android.internal.policy.impl.keyguard.KeyguardFaceUnlockView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_face_unlock_view"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- android:contentDescription="@string/keyguard_accessibility_face_unlock">
-
- <include layout="@layout/keyguard_message_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
-
- <FrameLayout
- android:id="@+id/keyguard_bouncer_frame"
- android:background="@*android:drawable/kg_bouncer_bg_white"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- >
- <com.android.internal.widget.FaceUnlockView
- android:id="@+id/face_unlock_area_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_horizontal"
- android:background="@*android:drawable/intro_bg"
- android:gravity="center">
-
- <View
- android:id="@+id/spotlightMask"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@*android:color/facelock_spotlight_mask"
- />
-
- <ImageButton
- android:id="@+id/face_unlock_cancel_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="5dip"
- android:layout_alignParentTop="true"
- android:layout_alignParentEnd="true"
- android:background="#00000000"
- android:src="@*android:drawable/ic_facial_backup"
- />
- </com.android.internal.widget.FaceUnlockView>
- </FrameLayout>
-
- <include layout="@layout/keyguard_eca"
- android:id="@+id/keyguard_selector_fade_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="bottom|center_horizontal"
- android:gravity="center_horizontal" />
-</com.android.internal.policy.impl.keyguard.KeyguardFaceUnlockView>
diff --git a/core/res/res/layout/keyguard_glow_pad_container.xml b/core/res/res/layout/keyguard_glow_pad_container.xml
deleted file mode 100644
index d86707f..0000000
--- a/core/res/res/layout/keyguard_glow_pad_container.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <include layout="@layout/keyguard_glow_pad_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_marginBottom="-60dp"/>
-</merge>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_glow_pad_view.xml b/core/res/res/layout/keyguard_glow_pad_view.xml
deleted file mode 100644
index cfd8160..0000000
--- a/core/res/res/layout/keyguard_glow_pad_view.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the selector widget that allows the user to select an action. -->
-<com.android.internal.widget.multiwaveview.GlowPadView
- xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/glow_pad_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:orientation="horizontal"
- android:gravity="@integer/kg_selector_gravity"
- android:contentDescription="@string/keyguard_accessibility_slide_area"
-
- prvandroid:targetDrawables="@array/lockscreen_targets_unlock_only"
- prvandroid:targetDescriptions="@array/lockscreen_target_descriptions_unlock_only"
- prvandroid:directionDescriptions="@*android:array/lockscreen_direction_descriptions"
- prvandroid:handleDrawable="@*android:drawable/ic_lockscreen_handle"
- prvandroid:outerRingDrawable="@*android:drawable/ic_lockscreen_outerring"
- prvandroid:outerRadius="@*android:dimen/glowpadview_target_placement_radius"
- prvandroid:innerRadius="@*android:dimen/glowpadview_inner_radius"
- prvandroid:snapMargin="@*android:dimen/glowpadview_snap_margin"
- prvandroid:firstItemOffset="@integer/kg_glowpad_rotation_offset"
- prvandroid:magneticTargets="true"
- prvandroid:feedbackCount="1"
- prvandroid:vibrationDuration="20"
- prvandroid:glowRadius="@*android:dimen/glowpadview_glow_radius"
- prvandroid:pointDrawable="@*android:drawable/ic_lockscreen_glowdot"
- prvandroid:allowScaling="true" />
diff --git a/core/res/res/layout/keyguard_message_area.xml b/core/res/res/layout/keyguard_message_area.xml
deleted file mode 100644
index 37463cf..0000000
--- a/core/res/res/layout/keyguard_message_area.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
-<com.android.internal.policy.impl.keyguard.KeyguardMessageArea
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:id="@+id/keyguard_message_area"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearance"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
- android:clickable="true" />
-
diff --git a/core/res/res/layout/keyguard_message_area_large.xml b/core/res/res/layout/keyguard_message_area_large.xml
deleted file mode 100644
index 584cec4..0000000
--- a/core/res/res/layout/keyguard_message_area_large.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
-<com.android.internal.policy.impl.keyguard.KeyguardMessageArea
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:id="@+id/keyguard_message_area"
- android:maxLines="4"
- android:textAppearance="?android:attr/textAppearance"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary" />
-
diff --git a/core/res/res/layout/keyguard_multi_user_avatar.xml b/core/res/res/layout/keyguard_multi_user_avatar.xml
deleted file mode 100644
index 2d8f02d..0000000
--- a/core/res/res/layout/keyguard_multi_user_avatar.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<!-- This is a view that shows general status information in Keyguard. -->
-<com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/keyguard_avatar_size"
- android:layout_height="@dimen/keyguard_avatar_size"
- android:background="#00000000"
- android:gravity="center_horizontal">
- <ImageView
- android:id="@+id/keyguard_user_avatar"
- android:scaleType="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"/>
- <TextView
- android:id="@+id/keyguard_user_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:gravity="center"
- android:textSize="@dimen/keyguard_avatar_name_size"
- android:textColor="#ffffff"
- android:singleLine="true"
- android:ellipsize="end"
- android:paddingLeft="2dp"
- android:paddingRight="2dp" />
-</com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar>
diff --git a/core/res/res/layout/keyguard_multi_user_selector.xml b/core/res/res/layout/keyguard_multi_user_selector.xml
deleted file mode 100644
index ee01285..0000000
--- a/core/res/res/layout/keyguard_multi_user_selector.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-<com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android"
- androidprv:layout_childType="userSwitcher"
- android:id="@+id/keyguard_user_selector"
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:contentDescription="@*android:string/keyguard_accessibility_user_selector"
- android:visibility="gone">
-
- <com.android.internal.policy.impl.keyguard.KeyguardLinearLayout
- android:id="@+id/keyguard_users_grid"
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_marginBottom="@dimen/keyguard_muliuser_selector_margin"
- android:layout_height="@dimen/keyguard_avatar_size"
- android:layout_gravity="center|bottom" />
-
-</com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView>
diff --git a/core/res/res/layout/keyguard_multi_user_selector_widget.xml b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
deleted file mode 100644
index fc126fe..0000000
--- a/core/res/res/layout/keyguard_multi_user_selector_widget.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<!-- This is a view that shows general status information in Keyguard. -->
-<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_multi_user_selector"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_password_view.xml b/core/res/res/layout/keyguard_password_view.xml
deleted file mode 100644
index aab54c3..0000000
--- a/core/res/res/layout/keyguard_password_view.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-<com.android.internal.policy.impl.keyguard.KeyguardPasswordView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_password_view"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- android:gravity="bottom"
- android:contentDescription="@string/keyguard_accessibility_password_unlock"
- >
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
-
- <include layout="@layout/keyguard_message_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <!-- Password entry field -->
- <!-- Note: the entire container is styled to look like the edit field,
- since the backspace/IME switcher looks better inside -->
- <FrameLayout
- android:id="@+id/keyguard_bouncer_frame"
- android:background="@*android:drawable/kg_bouncer_bg_white"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- >
- <LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:orientation="horizontal"
- android:background="#70000000"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="8dp"
- >
-
- <EditText android:id="@+id/passwordEntry"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="center_horizontal"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left"
- android:singleLine="true"
- android:textStyle="normal"
- android:inputType="textPassword"
- android:textSize="36sp"
- android:background="@null"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="#ffffffff"
- android:imeOptions="flagForceAscii|actionDone"
- />
-
- <ImageView android:id="@+id/switch_ime_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@*android:drawable/ic_lockscreen_ime"
- android:clickable="true"
- android:padding="8dip"
- android:layout_gravity="center"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone"
- />
-
- </LinearLayout>
- </FrameLayout>
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
-
- <include layout="@layout/keyguard_eca"
- android:id="@+id/keyguard_selector_fade_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="bottom|center_horizontal"
- android:gravity="center_horizontal" />
-
-</com.android.internal.policy.impl.keyguard.KeyguardPasswordView>
diff --git a/core/res/res/layout/keyguard_pattern_view.xml b/core/res/res/layout/keyguard_pattern_view.xml
deleted file mode 100644
index 1bd3e4e..0000000
--- a/core/res/res/layout/keyguard_pattern_view.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the screen that shows the 9 circle unlock widget and instructs
- the user how to unlock their device, or make an emergency call. This
- is the portrait layout. -->
-<com.android.internal.policy.impl.keyguard.KeyguardPatternView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_pattern_view"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- android:gravity="center_horizontal"
- android:contentDescription="@string/keyguard_accessibility_pattern_unlock">
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="center">
-
- <include layout="@layout/keyguard_message_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
-
- <FrameLayout
- android:id="@+id/keyguard_bouncer_frame"
- android:background="@*android:drawable/kg_bouncer_bg_white"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- >
- <com.android.internal.widget.LockPatternView
- android:id="@+id/lockPatternView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:layout_marginEnd="8dip"
- android:layout_marginBottom="4dip"
- android:layout_marginStart="8dip"
- android:layout_gravity="center_horizontal"
- android:gravity="center"
- android:contentDescription="@string/keyguard_accessibility_pattern_area" />
- </FrameLayout>
- <include layout="@layout/keyguard_eca"
- android:id="@+id/keyguard_selector_fade_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="bottom|center_horizontal"
- android:gravity="center_horizontal" />
- </LinearLayout>
- </FrameLayout>
-
-</com.android.internal.policy.impl.keyguard.KeyguardPatternView>
diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml
deleted file mode 100644
index 6a3b9e6..0000000
--- a/core/res/res/layout/keyguard_pin_view.xml
+++ /dev/null
@@ -1,224 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.internal.policy.impl.keyguard.KeyguardPINView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_pin_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- android:orientation="vertical"
- android:contentDescription="@string/keyguard_accessibility_pin_unlock"
- >
- <include layout="@layout/keyguard_message_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
- <LinearLayout
- android:id="@+id/keyguard_bouncer_frame"
- android:background="@*android:drawable/kg_bouncer_bg_white"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="vertical"
- android:layout_weight="1"
- android:layoutDirection="ltr"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="horizontal"
- android:layout_weight="1"
- >
- <TextView android:id="@+id/pinEntry"
- android:editable="true"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center"
- android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left"
- android:singleLine="true"
- android:cursorVisible="false"
- android:background="@null"
- android:textAppearance="@style/TextAppearance.NumPadKey"
- android:imeOptions="flagForceAscii|actionDone"
- />
- <ImageButton android:id="@+id/delete_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:src="@*android:drawable/ic_input_delete"
- android:clickable="true"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
- android:paddingLeft="24dp"
- android:paddingRight="24dp"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@string/keyboardview_keycode_delete"
- />
- </LinearLayout>
- <View
- android:layout_width="wrap_content"
- android:layout_height="1dp"
- android:background="#55FFFFFF"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key1"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="1"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key2"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="2"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key3"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="3"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key4"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="4"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key5"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="5"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key6"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="6"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="horizontal"
- android:layout_weight="1"
- >
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key7"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="7"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key8"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="8"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key9"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="9"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <Space
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key0"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="0"
- />
- <ImageButton
- android:id="@+id/key_enter"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:paddingRight="30dp"
- android:src="@drawable/sym_keyboard_return_holo"
- android:contentDescription="@string/keyboardview_keycode_enter"
- />
- </LinearLayout>
- </LinearLayout>
- <include layout="@layout/keyguard_eca"
- android:id="@+id/keyguard_selector_fade_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="bottom|center_horizontal"
- android:gravity="center_horizontal" />
-
-</com.android.internal.policy.impl.keyguard.KeyguardPINView>
diff --git a/core/res/res/layout/keyguard_selector_view.xml b/core/res/res/layout/keyguard_selector_view.xml
deleted file mode 100644
index dfacb6a..0000000
--- a/core/res/res/layout/keyguard_selector_view.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the selector widget that allows the user to select an action. -->
-<com.android.internal.policy.impl.keyguard.KeyguardSelectorView
- xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_selector_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_maxWidth="420dp"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="vertical"
- android:contentDescription="@string/keyguard_accessibility_slide_unlock">
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:gravity="center">
-
- <include layout="@layout/keyguard_message_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <View
- android:id="@+id/keyguard_selector_view_frame"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:background="@*android:drawable/kg_bouncer_bg_white"/>
-
- <include layout="@layout/keyguard_glow_pad_container" />
-
- <include layout="@layout/keyguard_eca"
- android:id="@+id/keyguard_selector_fade_container"
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:layout_gravity="bottom|center_horizontal" />
- </FrameLayout>
-
-</com.android.internal.policy.impl.keyguard.KeyguardSelectorView>
-
diff --git a/core/res/res/layout/keyguard_sim_pin_view.xml b/core/res/res/layout/keyguard_sim_pin_view.xml
deleted file mode 100644
index 6e6fe08..0000000
--- a/core/res/res/layout/keyguard_sim_pin_view.xml
+++ /dev/null
@@ -1,230 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<!-- This is the SIM PIN view that allows the user to enter a SIM PIN to unlock the device. -->
-<com.android.internal.policy.impl.keyguard.KeyguardSimPinView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_sim_pin_view"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- android:gravity="center_horizontal">
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_lockscreen_sim"/>
-
- <include layout="@layout/keyguard_message_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
- <LinearLayout
- android:id="@+id/keyguard_bouncer_frame"
- android:background="@*android:drawable/kg_bouncer_bg_white"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="vertical"
- android:layout_weight="1"
- android:layoutDirection="ltr"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="horizontal"
- android:layout_weight="1"
- >
- <TextView android:id="@+id/pinEntry"
- android:editable="true"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center"
- android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left"
- android:singleLine="true"
- android:cursorVisible="false"
- android:background="@null"
- android:textAppearance="@style/TextAppearance.NumPadKey"
- android:imeOptions="flagForceAscii|actionDone"
- />
- <ImageButton android:id="@+id/delete_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:src="@*android:drawable/ic_input_delete"
- android:clickable="true"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
- android:paddingLeft="24dp"
- android:paddingRight="24dp"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@string/keyboardview_keycode_delete"
- />
- </LinearLayout>
- <View
- android:layout_width="wrap_content"
- android:layout_height="1dp"
- android:background="#55FFFFFF"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key1"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="1"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key2"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="2"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key3"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="3"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key4"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="4"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key5"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="5"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key6"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="6"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="horizontal"
- android:layout_weight="1"
- >
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key7"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="7"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key8"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="8"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key9"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="9"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <Space
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key0"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="0"
- />
- <ImageButton
- android:id="@+id/key_enter"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:paddingRight="30dp"
- android:src="@drawable/sym_keyboard_return_holo"
- android:contentDescription="@string/keyboardview_keycode_enter"
- />
- </LinearLayout>
- </LinearLayout>
-
- <include layout="@layout/keyguard_eca"
- android:id="@+id/keyguard_selector_fade_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="bottom|center_horizontal"
- android:gravity="center_horizontal" />
-
-</com.android.internal.policy.impl.keyguard.KeyguardSimPinView>
diff --git a/core/res/res/layout/keyguard_sim_puk_pin_account_navigation.xml b/core/res/res/layout/keyguard_sim_puk_pin_account_navigation.xml
deleted file mode 100644
index 2e6fa37..0000000
--- a/core/res/res/layout/keyguard_sim_puk_pin_account_navigation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <include layout="@layout/default_navigation" />
-</merge>
diff --git a/core/res/res/layout/keyguard_sim_puk_view.xml b/core/res/res/layout/keyguard_sim_puk_view.xml
deleted file mode 100644
index 0412fdc..0000000
--- a/core/res/res/layout/keyguard_sim_puk_view.xml
+++ /dev/null
@@ -1,230 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-<!-- This is the SIM PUK view that allows the user to recover their device by entering the
- carrier-provided PUK code and entering a new SIM PIN for it. -->
-<com.android.internal.policy.impl.keyguard.KeyguardSimPukView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_sim_puk_view"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- android:gravity="center_horizontal">
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_lockscreen_sim"/>
-
- <include layout="@layout/keyguard_message_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
- <LinearLayout
- android:id="@+id/keyguard_bouncer_frame"
- android:background="@*android:drawable/kg_bouncer_bg_white"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="vertical"
- android:layout_weight="1"
- android:layoutDirection="ltr"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="horizontal"
- android:layout_weight="1"
- >
- <TextView android:id="@+id/pinEntry"
- android:editable="true"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center"
- android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left"
- android:singleLine="true"
- android:cursorVisible="false"
- android:background="@null"
- android:textAppearance="@style/TextAppearance.NumPadKey"
- android:imeOptions="flagForceAscii|actionDone"
- />
- <ImageButton android:id="@+id/delete_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:src="@*android:drawable/ic_input_delete"
- android:clickable="true"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
- android:paddingLeft="24dp"
- android:paddingRight="24dp"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@string/keyboardview_keycode_delete"
- />
- </LinearLayout>
- <View
- android:layout_width="wrap_content"
- android:layout_height="1dp"
- android:background="#55FFFFFF"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key1"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="1"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key2"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="2"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key3"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="3"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key4"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="4"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key5"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="5"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key6"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="6"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="horizontal"
- android:layout_weight="1"
- >
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key7"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="7"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key8"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="8"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key9"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="9"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <Space
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- />
- <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
- android:id="@+id/key0"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="0"
- />
- <ImageButton
- android:id="@+id/key_enter"
- style="@style/Widget.Button.NumPadKey"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:paddingRight="30dp"
- android:src="@drawable/sym_keyboard_return_holo"
- android:contentDescription="@string/keyboardview_keycode_enter"
- />
- </LinearLayout>
- </LinearLayout>
-
- <include layout="@layout/keyguard_eca"
- android:id="@+id/keyguard_selector_fade_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="bottom|center_horizontal"
- android:gravity="center_horizontal" />
-</com.android.internal.policy.impl.keyguard.KeyguardSimPukView>
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
deleted file mode 100644
index 9e36df3..0000000
--- a/core/res/res/layout/keyguard_status_view.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<!-- This is a view that shows general status information in Keyguard. -->
-<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_status_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- android:gravity="center_horizontal">
-
- <com.android.internal.policy.impl.keyguard.KeyguardStatusView
- android:id="@+id/keyguard_status_view_face_palm"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_horizontal|top"
- android:contentDescription="@android:string/keyguard_accessibility_status">
-
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top"
- android:orientation="vertical"
- android:focusable="true">
- <com.android.internal.policy.impl.keyguard.ClockView
- android:id="@+id/clock_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/kg_status_line_font_right_margin"
- android:layout_gravity="right">
-
- <TextView android:id="@+id/clock_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="none"
- android:textSize="@dimen/kg_status_clock_font_size"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="#ffffffff"
- android:drawablePadding="2dip"
- />
-
- </com.android.internal.policy.impl.keyguard.ClockView>
-
- <include layout="@layout/keyguard_status_area" />
- </LinearLayout>
-
- </com.android.internal.policy.impl.keyguard.KeyguardStatusView>
-</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
diff --git a/core/res/res/layout/keyguard_transport_control_view.xml b/core/res/res/layout/keyguard_transport_control_view.xml
deleted file mode 100644
index 532322c..0000000
--- a/core/res/res/layout/keyguard_transport_control_view.xml
+++ /dev/null
@@ -1,112 +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.
--->
-
-<!-- This is a view to control music playback in keyguard. -->
-<com.android.internal.policy.impl.keyguard.KeyguardTransportControlView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:id="@+id/keyguard_transport_control">
-
- <!-- FrameLayout used as scrim to show between album art and buttons -->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:foreground="@*android:drawable/ic_lockscreen_player_background"
- android:contentDescription="@*android:string/keygaurd_accessibility_media_controls">
- <!-- Use ImageView for its cropping features; otherwise could be android:background -->
- <ImageView
- android:id="@+id/albumart"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="fill"
- android:scaleType="centerCrop"
- android:adjustViewBounds="false"
- />
- </FrameLayout>
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom">
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dip"
- android:layout_marginStart="16dip"
- android:layout_marginEnd="16dip"
- android:gravity="center_horizontal"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceMedium"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="5dip">
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1">
- <ImageView
- android:id="@+id/btn_prev"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:src="@*android:drawable/ic_media_previous"
- android:clickable="true"
- android:background="?android:attr/selectableItemBackground"
- android:padding="10dip"
- android:contentDescription="@*android:string/lockscreen_transport_prev_description"/>
- </FrameLayout>
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1">
- <ImageView
- android:id="@+id/btn_play"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:clickable="true"
- android:src="@*android:drawable/ic_media_play"
- android:background="?android:attr/selectableItemBackground"
- android:padding="10dip"
- android:contentDescription="@*android:string/lockscreen_transport_play_description"/>
- </FrameLayout>
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1">
- <ImageView
- android:id="@+id/btn_next"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:clickable="true"
- android:src="@*android:drawable/ic_media_next"
- android:background="?android:attr/selectableItemBackground"
- android:padding="10dip"
- android:contentDescription="@*android:string/lockscreen_transport_next_description"/>
- </FrameLayout>
- </LinearLayout>
- </LinearLayout>
-
-</com.android.internal.policy.impl.keyguard.KeyguardTransportControlView>
\ No newline at end of file
diff --git a/core/res/res/layout/list_menu_item_checkbox.xml b/core/res/res/layout/list_menu_item_checkbox.xml
index dc02a1e..02febc4 100644
--- a/core/res/res/layout/list_menu_item_checkbox.xml
+++ b/core/res/res/layout/list_menu_item_checkbox.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,9 +18,8 @@
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
android:focusable="false"
android:clickable="false"
android:duplicateParentState="true" />
-
-
diff --git a/core/res/res/layout/list_menu_item_icon.xml b/core/res/res/layout/list_menu_item_icon.xml
index a30be6a..89b6b09 100644
--- a/core/res/res/layout/list_menu_item_icon.xml
+++ b/core/res/res/layout/list_menu_item_icon.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,11 +18,12 @@
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_centerVertical="true"
+ android:layout_alignParentStart="true"
android:layout_marginStart="8dip"
android:layout_marginEnd="-8dip"
android:layout_marginTop="8dip"
android:layout_marginBottom="8dip"
android:scaleType="centerInside"
- android:duplicateParentState="true" />
-
+ android:duplicateParentState="true"
+ android:contentDescription="@null" />
diff --git a/core/res/res/layout/list_menu_item_layout.xml b/core/res/res/layout/list_menu_item_layout.xml
index e8d4983..3130215 100644
--- a/core/res/res/layout/list_menu_item_layout.xml
+++ b/core/res/res/layout/list_menu_item_layout.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,46 +16,38 @@
<com.android.internal.view.menu.ListMenuItemView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeightSmall">
-
+ android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="start|center_vertical">
+
<!-- Icon will be inserted here. -->
-
- <!-- The title and summary have some gap between them, and this 'group' should be centered vertically. -->
- <RelativeLayout
- android:layout_width="0dip"
- android:layout_weight="1"
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_alignWithParentIfMissing="true"
android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd"
- android:duplicateParentState="true">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_alignParentStart="true"
- android:textAppearance="?android:attr/textAppearanceListItemSmall"
- android:singleLine="true"
- android:duplicateParentState="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:textAlignment="viewStart" />
+ android:layout_toEndOf="@id/icon"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
+ android:singleLine="true"
+ android:duplicateParentState="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:textAlignment="viewStart" />
- <TextView
- android:id="@+id/shortcut"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/title"
- android:layout_alignParentStart="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true"
- android:duplicateParentState="true"
- android:textAlignment="viewStart" />
-
- </RelativeLayout>
+ <TextView
+ android:id="@+id/shortcut"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title"
+ android:layout_alignStart="@id/title"
+ android:layout_alignWithParentIfMissing="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:duplicateParentState="true"
+ android:textAlignment="viewStart" />
<!-- Checkbox, and/or radio button will be inserted here. -->
-
+
</com.android.internal.view.menu.ListMenuItemView>
diff --git a/core/res/res/layout/list_menu_item_radio.xml b/core/res/res/layout/list_menu_item_radio.xml
index ac4459e..74564ad 100644
--- a/core/res/res/layout/list_menu_item_radio.xml
+++ b/core/res/res/layout/list_menu_item_radio.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,7 +18,8 @@
android:id="@+id/radio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
android:focusable="false"
android:clickable="false"
android:duplicateParentState="true" />
diff --git a/core/res/res/layout/pin_challenge.xml b/core/res/res/layout/pin_challenge.xml
new file mode 100644
index 0000000..2cb14b4
--- /dev/null
+++ b/core/res/res/layout/pin_challenge.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Layout used as the dialog's content View for EditTextPreference. -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="48dp"
+ android:layout_marginBottom="48dp"
+ android:overScrollMode="ifContentScrolls">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dip"
+ android:orientation="vertical">
+
+ <TextView android:id="@+id/pin_message"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/restr_pin_create_pin"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ <!-- TextView android:id="@+id/pin1_label"
+ style="?android:attr/textAppearanceSmall"
+ android:layout_marginBottom="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/restr_pin_enter_pin"
+ android:textColor="?android:attr/textColorSecondary" /-->
+
+ <EditText android:id="@+id/pin1_text"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_marginBottom="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/restr_pin_enter_pin"
+ android:inputType="textPassword"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <!-- TextView android:id="@+id/pin2_label"
+ style="?android:attr/textAppearanceSmall"
+ android:layout_marginBottom="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/restr_pin_confirm_pin"
+ android:textColor="?android:attr/textColorSecondary" /-->
+
+ <EditText android:id="@+id/pin2_text"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_marginBottom="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/restr_pin_confirm_pin"
+ android:inputType="textPassword"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <TextView android:id="@+id/pin_error_message"
+ style="?android:attr/textAppearanceSmall"
+ android:layout_marginBottom="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/restr_pin_error_doesnt_match"
+ android:textColor="#FFFF0000" />
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/core/res/res/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml
index 452f85d..aff3779 100644
--- a/core/res/res/layout/popup_menu_item_layout.xml
+++ b/core/res/res/layout/popup_menu_item_layout.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,45 +18,37 @@
android:layout_width="match_parent"
android:layout_height="?android:attr/dropdownListPreferredItemHeight"
android:minWidth="196dip"
- android:paddingEnd="16dip">
-
+ android:paddingEnd="16dip"
+ android:gravity="start|center_vertical">
+
<!-- Icon will be inserted here. -->
-
- <!-- The title and summary have some gap between them, and this 'group' should be centered vertically. -->
- <RelativeLayout
- android:layout_width="0dip"
- android:layout_weight="1"
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_alignWithParentIfMissing="true"
android:layout_marginStart="16dip"
- android:duplicateParentState="true">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_alignParentStart="true"
- android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
- android:singleLine="true"
- android:duplicateParentState="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:textAlignment="viewStart" />
+ android:layout_toEndOf="@id/icon"
+ android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+ android:singleLine="true"
+ android:duplicateParentState="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:textAlignment="viewStart" />
- <TextView
- android:id="@+id/shortcut"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/title"
- android:layout_alignParentStart="true"
- android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
- android:singleLine="true"
- android:duplicateParentState="true"
- android:textAlignment="viewStart" />
-
- </RelativeLayout>
+ <TextView
+ android:id="@+id/shortcut"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title"
+ android:layout_alignStart="@id/title"
+ android:layout_alignWithParentIfMissing="true"
+ android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
+ android:singleLine="true"
+ android:duplicateParentState="true"
+ android:textAlignment="viewStart" />
<!-- Checkbox, and/or radio button will be inserted here. -->
-
+
</com.android.internal.view.menu.ListMenuItemView>
diff --git a/core/res/res/layout/toast_bar.xml b/core/res/res/layout/toast_bar.xml
new file mode 100644
index 0000000..b7443d5
--- /dev/null
+++ b/core/res/res/layout/toast_bar.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:background="@drawable/toast_bar_bg"
+ android:layout_height="50dp"
+ android:layout_width="match_parent">
+
+ <TextView
+ android:id="@android:id/message"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:ellipsize="end"
+ android:gravity="center_vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:singleLine="true"
+ android:textColor="@android:color/white"
+ android:textSize="16sp" />
+
+ <LinearLayout
+ android:id="@android:id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true">
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginBottom="10dp"
+ android:layout_marginRight="12dp"
+ android:layout_marginTop="10dp"
+ android:background="#aaaaaa" />
+
+ <TextView
+ android:id="@android:id/text1"
+ android:textSize="12sp"
+ android:textColor="#aaaaaa"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:paddingLeft="8dp"
+ android:paddingRight="20dp"
+ android:textAllCaps="true" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+</FrameLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 405a978..06a5e67 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Laat die program toe om take na die voorgrond of agtergrond te skuif. Die program kan dit moontlik sonder jou insette doen."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"stop lopende programme"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Laat die program toe om take te verwyder en hul programme te dood. Kwaadwillige programme kan die gedrag van ander programme ontwrig."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"begin enige aktiwiteit"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Laat die program toe om \'n aktiwiteit te begin, ongeag toestemming-beskerming of uitgevoerde status."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"stel skermversoenbaarheid"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n invoermetode te bind. Dit moet nooit vir normale programme nodig wees nie."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"verbind aan \'n toeganklikheidsdiens"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Dit laat die houer toe om aan die top-koppelvlak van \'n toeganklikheidsdiens te verbind. Behoort nooit vir gewone programme nodig te wees nie."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"bind aan \'n teksdiens"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n teksdiens (bv SpellCheckerService) te bind. Dit moet nooit vir normale programme nodig wees nie."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"bind aan \'n VPN-diens"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Laat die program toe om na die SD-kaart te skryf."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"verander/vee uit interne mediabergingsinhoud"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Laat die program toe om die inhoud van die interne mediaberging te verander."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"bestuur dokumentberging"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Laat die program toe om dokumentberging te bestuur."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"verkry toegang tot alle gebruikers se eksterne berging"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Laat die program toe om toegang tot eksterne berging vir alle gebruikers te verkry."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"lees die kaslêerstelsel"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Laat die program toe om die verstek houerdiens in te roep om inhoud te kopieer. Nie vir gebruik deur normale programme nie."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Roeteer media-uitvoer"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Laat \'n program toe om media-uitvoere na ander eksterne toestelle te roeteer."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Kry toegang tot keyguard se veilige berging"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Laat \'n program toe om toegang tot keyguard se veilige berging te kry."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Beheer wys en versteek van keyguard"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Laat \'n program toe om keyguard te beheer."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Raak twee keer vir zoembeheer"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Kon nie legstuk byvoeg nie."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Gaan"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Koppel tans..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Beskikbaar"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Nie beskikbaar nie"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"In gebruik"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ingeboude skerm"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI-skerm"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Oorlegger #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Verkeerde patroon"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Verkeerde wagwoord"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Verkeerde PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Probeer weer oor <xliff:g id="NUMBER">%d</xliff:g> sekondes."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Probeer weer oor <xliff:g id="NUMBER">%1$d</xliff:g> sekondes."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Teken jou patroon"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Voer SIM-PIN in"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Voer PIN in"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g> ."</string>
<string name="owner_name" msgid="2716755460376028154">"Eienaar"</string>
<string name="error_message_title" msgid="4510373083082500195">"Fout"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Hierdie program werk nie met rekeninge vir beperkte profiele nie"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Hierdie program werk nie met rekeninge vir beperkte profiele nie"</string>
<string name="app_not_found" msgid="3429141853498927379">"Geen program gevind om hierdie handeling te hanteer nie"</string>
<string name="revoke" msgid="5404479185228271586">"Herroep"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index c84c0e2..8c0da87 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"መተግበሪያው ተግባሮችን ወደ ቅድመ ገጹ እና ወደ ዳራው እንዲያንቀሳቅስ ይፈቅድለታል። መተግበሪያው ይህንን ያላንተ ግብዓት ሊያደርግ ይችላል።"</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"የአሂድ ትግበራዎች አቁም"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"ተግባሮችን ለማስወገድ እና መተግበሪያዎቻቸውን ለመግደል ለመተግበሪያ ይፈቅዳል። ጎጂ የሆኑ መተግበሪያዎች የሌሎችን መተግበሪያዎችን ባህሪ ሊያውኩ ይችላሉ።"</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"ማንኛውም እንቅስቃሴ ጀምር"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"መተግበሪያው ማንኛውም እንቅስቃሴ፣ የፍቃድ ጥበቃም ሆነ ወደ ውጭ የተላከበት ሁኔታ ሳይታይ፣ እንዲጀምር ይፈቅድለታል።"</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"የማያ ገጽ ተኳኋኝነት መድብ"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ያዡ ግቤት ስልቱን ወደ ከፍተኛ-ደረጃ በይነገጽ ለመጠረዝ ይፈቅዳሉ። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ከአንድ የተደራሽነት አገልግሎት ጋር እሰር"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ያዢው ወደ የአንድ ተደራሽነት አገልግሎት ከፍተኛ-ደረጃ በይነገጽ እንዲያስር ይፈቅድለታል። ለመደበኛ መተግበሪያዎች መቼም ቢሆን ሊያስፈልግ አይገባም።"</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"ለፅሁፍ አገልግሎት አሰረ"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"ያዡ ግቤት ለከፍተኛ-ደረጃ የፅሁፍ አገልግሎት ገፅታ ለመጠረዝ ይፈቅዳል። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"ለVPN አገልግሎት ተገዛ"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"መተግበሪያውን ወደ SD ካርድ ለመፃፍ ይፈቅዳል።"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"የውስጥ ማህደረ መረጃ ማከማቻ ይዘቶችን ቀይር/ሰርዝ"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"የውስጥ ማህደረ መረጃ ማከማቻ ይዘትን ለመቀየር ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"የሰነድ ማከማቻን ያስተዳድሩ"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"መተግበሪያው የሰነድ ማከማቻን እንዲያስተዳድር ይፈቅድለታል።"</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"የሁሉም ተጠቃሚዎች ውጫዊ ማከማቻውን ይደርስበታል"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"መተግበሪያውን የሁሉም ተጠቃሚዎች ውጫዊ ማከማቻውን እንዲደርስ ይፈቅድለታል።"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"የመሸጎጫ ስርዓተ ፋይል ድረስ"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ይዘትን ለመቅዳት ነባሪ መያዣ አገልግሎት እንዲያስነሳ ለመተግበሪው ይፈቅዳሉ፡፡ ለመደበኛ መተግበሪያዎች ለመጠቀም አይሆንም፡፡"</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"የሚዲያ ውፅአት መንገድ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"አንድ መተግበሪያ የሚዲያ ውፅአትን ወደ ሌላ ውጫዊ መሳሪያ እንዲመራ ይፈቅድለታል።"</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ደህንነቱ በቁልፍ የተጠበቀ ማከማቻን ይድረሱ"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"አንድ መተግበሪያ ደህንነቱ በቁልፍ የተጠበቀ ማከማቻ እንዲደርስ ያስችለዋል።"</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"የቁልፍ መጠበቂያውን ማሳየት እና መደበቅ ይቆጣጠሩ"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"አንድ መተግበሪያ የቁልፍ መጠበቂያውን እንዲቆጣጠር ያስችለዋል።"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ለአጉላ መቆጣጠሪያ ሁለት ጊዜ ነካ አድርግ"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ምግብር ማከል አልተቻለም።"</string>
<string name="ime_action_go" msgid="8320845651737369027">"ሂድ"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"በማገናኘት ላይ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"የሚገኙ"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"አይገኝም"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"በጥቅም ላይ"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"ውስጥ የተሰራ ማያ ገጽ"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI ማያ ገጽ"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"ተደራቢ #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"የተሳሳተ ስርዓተ ጥለት"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"የተሳሳተ ይለፍ ቃል"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"የተሳሳተ ፒን"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"በ<xliff:g id="NUMBER">%d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"በ<xliff:g id="NUMBER">%1$d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"ስርዓተ ጥለትዎን ይሳሉ"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"የሲም ፒን ያስገቡ"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"ፒን ያስገቡ"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"የአሁኑ ተጠቃሚ <xliff:g id="NAME">%1$s</xliff:g>።"</string>
<string name="owner_name" msgid="2716755460376028154">"ባለቤት"</string>
<string name="error_message_title" msgid="4510373083082500195">"ስህተት"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"ይህ መተግበሪያ የተገደቡ መገለጫዎች መለያዎችን አይደግፍም"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"ይህ መተግበሪያ የተገደቡ መገለጫዎች መለያዎችን አይደግፍም"</string>
<string name="app_not_found" msgid="3429141853498927379">"ይህን እርምጃ የሚያከናውን ምንም መተግበሪያ አልተገኘም"</string>
<string name="revoke" msgid="5404479185228271586">"ሻር"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index d694917..1820689 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"للسماح للتطبيق بنقل المهام إلى المقدمة والخلفية. وقد يجري التطبيق ذلك بدون إذنك."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"إيقاف التطبيقات التي قيد التشغيل"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"للسماح للتطبيق بإزالة المهام وإنهاء تطبيقاتها. قد تعطل التطبيقات الضارة عمل التطبيقات الأخرى."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"بدء أي نشاط"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"للسماح للتطبيق ببدء أي نشاط، بغض النظر عن حماية الإذن أو حالة التصدير."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"تعيين توافق الشاشة"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لأسلوب الإدخال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"الالتزام بخدمة إمكانية الدخول"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة إمكانية الدخول. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"الالتزام بخدمة إدخال النصوص"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة إدخال النصوص (على سبيل المثال، SpellCheckerService). لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"الالتزام بخدمة VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"للسماح للتطبيق بالكتابة إلى بطاقة SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"تعديل/حذف محتويات وحدة تخزين الوسائط الداخلية"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"للسماح للتطبيق بتعديل محتويات وحدة تخزين الوسائط الداخلية."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"إدارة السعة التخزينية للمستند"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"السماح للتطبيق بإدارة السعة التخزينية للمستند."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"الوصول إلى سعة التخزين الخارجية لجميع المستخدمين"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"للسماح للتطبيق بالدخول إلى سعة التخزين الخارجية لجميع المستخدمين."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"الدخول إلى نظام ملفات ذاكرة التخزين المؤقت"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"للسماح باستدعاء خدمة الحاوية الافتراضية لنسخ المحتوى. ليس للاستخدام بواسطة التطبيقات العادية."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"توجيه إخراج الوسائط"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"للسماح للتطبيق بتوجيه إخراج الوسائط إلى أجهزة خارجية أخرى."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"الدخول إلى التخزين المحمي بقفل المفاتيح"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"السماح لأحد التطبيقات بالدخول إلى التخزين المحمي بقفل المفاتيح."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"التحكم في عرض وإخفاء قفل المفاتيح"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"للسماح لأحد التطبيقات بالتحكم في قفل المفاتيح."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"المس مرتين للتحكم في التكبير/التصغير"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"تعذرت إضافة أداة."</string>
<string name="ime_action_go" msgid="8320845651737369027">"تنفيذ"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"جارٍ الاتصال..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"متاح"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"غير متاح"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"قيد الاستخدام"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"شاشة مدمجة"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"شاشة HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"المركب #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"نقش خاطئ"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"كلمة مرور خاطئة"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"رقم تعريف شخصي خاطئ"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"حاول مرة أخرى خلال <xliff:g id="NUMBER">%d</xliff:g> ثانية."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"حاول مرة أخرى خلال <xliff:g id="NUMBER">%1$d</xliff:g> ثانية."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"ارسم نقشك"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"أدخل رقم التعريف الشخصي لبطاقة SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"أدخل رقم التعريف الشخصي"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"المستخدم الحالي <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"المالك"</string>
<string name="error_message_title" msgid="4510373083082500195">"خطأ"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"هذا التطبيق لا يتوافق مع حسابات الملفات الشخصية المقيدة"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"لا يتوافق هذا التطبيق مع حسابات الملفات الشخصية المقيدة"</string>
<string name="app_not_found" msgid="3429141853498927379">"لم يتم العثور على تطبيق يمكنه التعامل مع هذا الإجراء."</string>
<string name="revoke" msgid="5404479185228271586">"إلغاء"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index fe11bb6..becac6c 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Дазваляе прыкладанню перамяшчаць заданні на пярэдні план і ў фон. Шкоднасныя прыкладанні могуць прымусова рабіць сябе асноўнымі без вашага ведама."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"спыніць запушчаныя прыкладанні"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Дазваляе прыкладанням выдаляць заданні і спыняць прыкладанні, якія іх выкарыстоўваюць. Шкоднасныя прыкладаннi могуць перашкодзiць працы іншых прыкладанняў."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"пачынаць любы працэс"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Дазваляе прыкладанню пачынаць любы працэс, незалежна ад абароны дазволам ці стану экспарту."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"усталяваць сумяшчальнасць экранаў"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Дазваляе ўладальніку прывязвацца да інтэрфейсу верхняга ўзроўню метада ўводу. Не патрабуецца для звычайных прыкладанняў."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"прывязацца да службы доступу"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Дазваляе ўладальніку прывязвацца да інтэрфейсу верхняга ўзроўню службы доступу. Не патрабуецца для звычайных прыкладанняў."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"звязаць з тэкставай службай"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Дазваляе ўладальніку прывязвацца да інтэрфейсу верхняга ўзроўню тэкставай паслугі (напрыклад, SpellCheckerService). Ніколі не патрабуецца для звычайных прыкладанняў."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"звязвацца з VPN сэрвісам"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дазваляе прыкладанням запісваць на SD-карту."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"змяніць/выдаліць унутраныя носьбіты змесціва"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дазваляе прыкладанням змяняць змесціва ўнутранай памяці."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"кіраваць сховішчам для дакументаў"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Дазваляе прыкладанню кіраваць сховішчам для дакументаў."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"доступ да знешніх захоўвання для ўсіх карыстальнікаў"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Дазваляе ўсiм карыстальнiкам прыкладання атрымлiваць доступ да знешнега сховiшча"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"доступ да файлавай сістэмы кэша"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Дазваляе прыкладанням выклікаць службу захавання па змаўчанні для капіявання змесціва. Не выкарыстоўваецца звычайнымі прыкладаннямі."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Маршрутны мультымедыйны выхад"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Дазваляе прыкладанням маршрутызаваць мультымедыйны выхад на iншыя знешнiя прылады."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Доступ да блакіроўкі клавіятуры бяспечнага сховішча"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Дазваляе прыкладанню атрымліваць доступ да блакіроўкі клавіятуры бяспечнага сховішча."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Кiраванне адлюстраваннем і схаваннем блакiроўкi клавіятуры"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Дазваляе прыкладанню кiраваць блакiроўкай клавiятуры."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Двойчы дакраніцеся, каб змянiць маштаб"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Немагчыма дадаць віджэт."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Пачаць"</string>
@@ -1441,6 +1459,8 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Падключэнне..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Даступна"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Недаступны"</string>
+ <!-- no translation found for media_route_status_in_use (4533786031090198063) -->
+ <skip />
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Убудаваны экран"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Экран HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Оверлей # <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1473,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Няправільна ключ"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Няправiльны пароль"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Няправільны PIN-код"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Паўтарыце спробу праз <xliff:g id="NUMBER">%d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Паўтарыце спробу праз <xliff:g id="NUMBER">%1$d</xliff:g> с."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Намалюйце ключ"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Увядзіце PIN-код SIM-карты"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Увядзіце PIN-код"</string>
@@ -1493,7 +1513,97 @@
<string name="user_switched" msgid="3768006783166984410">"Бягучы карыстальнік <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Уладальнік"</string>
<string name="error_message_title" msgid="4510373083082500195">"Памылка"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Гэтае прыкладанне не падтрымлівае уліковыя запісы для карыстальнікаў з абмежаваннямi"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Гэта прыкладанне не падтрымлівае ўліковыя запісы для профiляў з абмежаваннямі"</string>
<string name="app_not_found" msgid="3429141853498927379">"Прыкладанне для гэтага дзеяння не знойдзенае"</string>
<string name="revoke" msgid="5404479185228271586">"Ануляваць"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 76cab7a..a4fa7f0 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Разрешава на приложението да прехвърля задачи на преден и на заден план. То може да направи това без вашето потвърждение."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"спиране на изпълняваните приложения"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Разрешава на приложението да премахва задачи и да прекратява приложенията им. Злонамерените приложения могат да нарушат поведението на други приложения."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"започване на дейности"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Разрешава на приложението да започва дейности независимо от защитата на базата на разрешения или състоянието при експортиране."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"задаване на съвместимост на екрана"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на метод на въвеждане. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"обвързване с услуга за достъпност"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на услуга за достъпност. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"обвързване с текстова услуга"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на текстова услуга (напр. SpellCheckerService). Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"обвързване с услуга за VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Разрешава на приложението да записва върху SD картата."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"пром./изтр. на съдърж. на вътр. мултим. хранил."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Разрешава на приложението да променя съдържанието на вътрешното мултимедийно хранилище."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"управл. на хранил. с документи"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Разрешава на приложението да управлява хранилището с документи."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"достъп до външ. хранилище за всички потребители"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Разрешава на приложението достъп до външното хранилище за всички потребители."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"достъп до файловата система на кеша"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Разрешава на приложението да извиква стандартната услуга на контейнера, за да се копира съдържание. Не е предназначено за нормални приложения."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Насочване на изходящата мултимедия"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Разрешава на приложението да насочва изходящата мултимедия към други външни устройства."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Достъп до надеждното хранилище, свързано с функцията за защита на клавишите"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Позволява на приложението да осъществява достъп до надеждното хранилище, свързано с функцията за защита на клавишите."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Контролиране на показването и скриването на функцията за защита на клавишите"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Разрешава на приложението да контролира функцията за защита на клавишите."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Докоснете двукратно за управление на промяната на мащаба"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Приспособлението не можа да бъде добавено."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Старт"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Установява се връзка..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Налице"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Не е налице"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Използва се"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Вграден екран"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Екран „HDMI“"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Наслагване №<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Грешна фигура"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Грешна парола"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Грешен ПИН код"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Опитайте отново след <xliff:g id="NUMBER">%d</xliff:g> секунди."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Опитайте отново след <xliff:g id="NUMBER">%1$d</xliff:g> секунди."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Начертайте фигурата си"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Въведете ПИН кода за SIM картата"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Въведете ПИН код"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Текущ потребител <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Собственик"</string>
<string name="error_message_title" msgid="4510373083082500195">"Грешка"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Това приложение не поддържа профили за потребителски профили с ограничена функционалност"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Това приложение не поддържа профили за потребителски профили с ограничена функционалност"</string>
<string name="app_not_found" msgid="3429141853498927379">"Няма намерено приложение за извършване на това действие"</string>
<string name="revoke" msgid="5404479185228271586">"Отмяна"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 8c82e3a..05965b3 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permet que l\'aplicació desplaci tasques en primer o segon pla. L\'aplicació pot fer-ho sense que tu ho indiquis."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"atura les aplicacions que s\'estan executant"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Permet que l\'aplicació elimini tasques i finalitzi les seves aplicacions. Les aplicacions malicioses poden alterar el comportament d\'altres aplicacions."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"iniciar qualsevol activitat"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Permet que l\'aplicació iniciï qualsevol activitat, amb independència de la protecció del permís o de l\'estat exportat."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"definició de la compatibilitat de pantalla"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permet que el titular vinculi a la interfície de nivell superior d\'un mètode d\'entrada. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"vincular amb un servei d\'accessibilitat"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permet vincular amb la interfície de nivell superior d\'un servei d\'accessibilitat. Les aplicacions normals no haurien de necessitar-ho."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"vincula a un servei de text"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Permet al titular vincular amb la interfície de nivell superior d\'un servei de text (per exemple, SpellCheckerService). Les aplicacions normals mai no ho haurien de necessitar."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"vincula a un servei de VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet a l\'aplicació escriure a la targeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Canvia/esborra emmagatz. intern"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permet que l\'aplicació modifiqui el contingut de l\'emmagatzematge multimèdia intern."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"gestió emmagatzematge docum."</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Permet que l\'aplicació gestioni l\'emmagatzematge de documents."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"accedeix a l\'emmagatzematge extern per a tots els usuaris"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permet que l\'aplicació accedeixi a l\'emmagatzematge extern per a tots els usuaris."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accedir al sistema de fitxers de la memòria cau"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Permet invocar el servei de contenidor predeterminat per copiar contingut. No indicat per a les aplicacions normals."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Indicació de ruta de sortida de contingut multimèdia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permet que una aplicació indiqui la ruta de sortida de contingut multimèdia a altres dispositius externs."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Accedeix a l\'emmagatzematge protegit per contrasenya"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Permet que una aplicació accedeixi a l\'emmagatzematge protegit per contrasenya."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Controla si es mostra o s\'amaga el bloqueig de les tecles."</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permet que una aplicació controli el bloqueig de les tecles."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toca dos cops per controlar el zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"No s\'ha pogut afegir el widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Vés"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"S\'està connectant..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"No disponible"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"En ús"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Pantalla integrada"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Pantalla HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposa #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Patró incorrecte"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Contrasenya incorrecta"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorrecte"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER">%d</xliff:g> segons."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER">%1$d</xliff:g> segons."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Dibuixa el patró"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introdueix el PIN de la SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Introdueix el PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Usuari actual: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Propietari"</string>
<string name="error_message_title" msgid="4510373083082500195">"Error"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"L\'aplicació no és compatible amb comptes de perfils restringits."</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"L\'aplicació no és compatible amb comptes de perfils restringits"</string>
<string name="app_not_found" msgid="3429141853498927379">"No s\'ha trobat cap aplicació per processar aquesta acció"</string>
<string name="revoke" msgid="5404479185228271586">"Revoca"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index a465d7c..9f3a090 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Umožňuje aplikaci přesunout úlohy na popředí nebo pozadí. Aplikace tak může činit bez vašeho zásahu."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"zastavení činnosti aplikací"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Umožňuje aplikaci odstranit úlohy a ukončit jejich aplikace. Škodlivé aplikace mohou narušit chování ostatních aplikací."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"zahájení libovolné činnosti"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Umožňuje aplikaci zahájit libovolnou aktivitu bez ohledu na ochranu pomocí oprávnění či exportovaný stav."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nastavit kompatibilitu obrazovky"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Umožňuje držiteli vázat se na nejvyšší úroveň rozhraní pro zadávání dat. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"navázat se na službu usnadnění přístupu"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Umožňuje držiteli navázat se na nejvyšší úroveň rozhraní služby usnadnění přístupu. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"navázat se na textovou službu"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Umožňuje držiteli připojit se k nejvyšší úrovni rozhraní textové služby (např. SpellCheckerService). Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"navázat se na službu VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikaci zapisovat na kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Upravit/smazat interní úlož."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Umožňuje aplikaci upravovat obsah interního úložiště médií."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"spravovat úložiště dokumentů"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Umožňuje aplikaci spravovat úložiště dokumentů."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"přístup k externímu úložišti všech uživatelů"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Umožňuje aplikaci přistupovat k externímu úložišti pro všechny uživatele."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"přistupovat do souborového systému mezipaměti"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Umožňuje aplikaci dát výchozí službě kontejneru příkaz ke zkopírování obsahu. Toto oprávnění není určeno pro běžné aplikace."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Směrování výstupu médií"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Umožňuje aplikaci směrovat výstup médií do dalších externích zařízení."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Přístup k bezpečnému úložišti keyguard"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Umožňuje aplikaci přístup k bezpečnému úložišti keyguard."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Ovládání zobrazování a skrývání zámku obrazovky"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Umožňuje aplikaci ovládat zámek obrazovky."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dvojitým dotykem můžete ovládat přiblížení"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget nelze přidat."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Přejít"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Připojování…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Dostupná"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Není k dispozici"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Používá se"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Integrovaná obrazovka"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Obrazovka HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Překryvná vrstva č. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Nesprávné gesto"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Nesprávné heslo"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Nesprávný kód PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Zkuste to znovu za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Zkuste to znovu za <xliff:g id="NUMBER">%1$d</xliff:g> s."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Nakreslete gesto"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Zadejte kód PIN SIM karty"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Zadejte kód PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Aktuální uživatel je <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Vlastník"</string>
<string name="error_message_title" msgid="4510373083082500195">"Chyba"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Tato aplikace nepodporuje účty pro omezené profily."</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Tato aplikace nepodporuje účty pro omezené profily"</string>
<string name="app_not_found" msgid="3429141853498927379">"Aplikace potřebná k provedení této akce nebyla nalezena"</string>
<string name="revoke" msgid="5404479185228271586">"Zrušit"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 6c9a5f3..80e14c8 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Tillader, at appen kan flytte opgaver til forgrunden og baggrunden. Appen kan gøre dette uden din bekræftelse."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"stoppe kørsel af apps"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Tillader, at en app kan fjerne opgaver og lukke deres apps. Ondsindede apps kan forstyrre adfærden for andre apps."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"starte en aktivitet"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Tillader, at appen starter en hvilken som helst aktivitet, uanset tilladelsesbeskyttelse eller eksporteret tilstand."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"indstil skærmens kompatibilitet"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Tillader, at brugeren kan forpligter sig til en inputmetodes grænseflade på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"bind dig til en tilgængelighedstjeneste"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Tillader, at brugeren binder sig til en grænseflade for en tilgængelighedstjeneste på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"forpligte sig til en sms-tjeneste"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Tillader, at ejeren kan binde en teksttjenestes grænseflade (f. eks. SpellCheckerService) på øverste niveau. Dette bør aldrig være nødvendigt til normale apps."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"bind til en VPN-tjeneste"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillader, at appen kan skrive til SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Rediger/slet internt medielagringsindhold"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Tillader, appen kan ændre indholdet af det interne medielager."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"administrer dokumentlagring"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Tillader, at appen kan administrere dokumentlagring."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"få adgang til alle brugeres eksterne lagre"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Tillader, at appen får adgang til eksterne lagre for alle brugere."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"få adgang til cache-filsystemet"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Tillader, at appen kan benytte standardlagertjenesten til at kopiere indhold. Anvendes ikke af almindelige apps."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Viderefør medieoutput"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Tillader, at en applikation viderefører medieoutput til andre eksterne enheder."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Få adgang nøglebeskyttet lager"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Tillader, at en applikation får adgang til et nøglebeskyttet lager."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Administrer, om nøglebeskyttelse skal vises eller skjules"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Tillader, at en applikation styrer nøglebeskyttelsen."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Tryk to gange for zoomstyring"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget kunne ikke tilføjes."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Gå"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Opretter forbindelse..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tilgængelig"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Ikke tilgængelig"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"I brug"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Indbygget skærm"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI-skærm"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlejring nr. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Forkert mønster"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Forkert adgangskode"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Forkert pinkode"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Prøv igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Prøv igen om <xliff:g id="NUMBER">%1$d</xliff:g> sekunder."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Tegn dit mønster"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Indtast pinkode til SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Indtast pinkode"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Nuværende bruger <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Ejer"</string>
<string name="error_message_title" msgid="4510373083082500195">"Fejl"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Denne applikation understøtter ikke konti for begrænsede profiler"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Denne applikation understøtter ikke konti for begrænsede profiler"</string>
<string name="app_not_found" msgid="3429141853498927379">"Der blev ikke fundet nogen applikation, der kan håndtere denne handling"</string>
<string name="revoke" msgid="5404479185228271586">"Tilbagekald"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 50a7c8c..be81e63 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Ermöglicht der App, Aufgaben in den Vorder- und Hintergrund zu verschieben, ohne dass dazu ein Eingreifen Ihrerseits notwendig ist."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"Aktive Apps beenden"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Ermöglicht der App, Aufgaben zu entfernen und die entsprechenden Apps zu beenden. Schädliche Apps können das Verhalten anderer Apps stören."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"Beliebige Aktivität starten"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Ermöglicht der App, ungeachtet der Berechtigungen oder des Exportstatus beliebige Aktivitäten zu starten"</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Bildschirmkompatibilität festlegen"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Ermöglicht dem Halter, sich an die Oberfläche einer Eingabemethode auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"An eine Bedienungshilfe binden"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Ermöglicht dem Halter, sich an die Oberfläche einer Bedienungshilfe auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"An einen Textdienst binden"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Ermöglicht dem Halter, sich an die Oberfläche eines Textdienstes auf oberster Ebene zu binden, z. B. eines Rechtschreibprüfungsdienstes. Sollte nie für normale Apps benötigt werden."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"An einen VPN-Dienst binden"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ermöglicht der App, auf die SD-Karte zu schreiben"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Internen Medienspeicher ändern/löschen"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ermöglicht der App, den Inhalt des internen Medienspeichers zu ändern"</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"Dokumentenspeicher verwalten"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"App kann Dokumentenspeicher verwalten"</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"Auf externen Speicher aller Nutzer zugreifen"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Ermöglicht der App, auf externen Speicher aller Nutzer zuzugreifen."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"Auf das Cache-Dateisystem zugreifen"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Ermöglicht der App das Aufrufen des Standard-Containerdienstes zum Kopieren von Inhalten. Nicht für normale Apps vorgesehen."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Medienausgabe umleiten"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Ermöglicht einer App, die Medienausgabe auf andere externe Geräte umzuleiten."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Zugriff auf mit Keyguard geschützten Speicher"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Ermöglicht einer App den Zugriff auf mit Keyguard geschützten Speicher"</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Anzeige und Ausblenden des Keyguard steuern"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Apps können den Keyguard steuern."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Für Zoomeinstellung zweimal berühren"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget konnte nicht hinzugefügt werden."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Los"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Verbindung wird hergestellt..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Verfügbar"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Nicht verfügbar"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"In Verwendung"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Integrierter Bildschirm"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI-Bildschirm"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay-Nr. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Falsches Muster"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Falsches Passwort"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Falsche PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Versuchen Sie es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Versuchen Sie es in <xliff:g id="NUMBER">%1$d</xliff:g> Sekunden erneut."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Muster zeichnen"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM-PIN eingeben"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"PIN eingeben"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Aktueller Nutzer <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="owner_name" msgid="2716755460376028154">"Eigentümer"</string>
<string name="error_message_title" msgid="4510373083082500195">"Fehler"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Diese App unterstützt keine Konten für eingeschränkte Profile."</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Diese App unterstützt keine Konten für eingeschränkte Profile."</string>
<string name="app_not_found" msgid="3429141853498927379">"Für diese Aktion wurde keine App gefunden."</string>
<string name="revoke" msgid="5404479185228271586">"Aufheben"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 968aba9..961e15e 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Επιτρέπει στην εφαρμογή τη μετακίνηση εργασιών στο προσκήνιο και το παρασκήνιο. Η εφαρμογή μπορεί να το κάνει αυτό χωρίς να καταχωρίσετε δεδομένα εισόδου."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"διακοπή εκτέλεσης εφαρμογών"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Επιτρέπει στην εφαρμογή την κατάργηση ενεργειών και την απομάκρυνση των εφαρμογών τους. Τυχόν κακόβουλες εφαρμογές ενδέχεται να διαταράξουν τη λειτουργία άλλων εφαρμογών."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"έναρξη οποιασδήποτε δραστηριότητας"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Επιτρέπει στην εφαρμογή την έναρξη οποιασδήποτε δραστηριότητας, ανεξάρτητα από την προστασία αδειών ή την κατάσταση εξαγωγής."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ρύθμιση συμβατότητας οθόνης"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας μεθόδου εισόδου. Δεν απαιτείται για συνήθεις εφαρμογές."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"δέσμευση σε υπηρεσία προσβασιμότητας"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανώτατου επιπέδου μιας υπηρεσίας προσβασιμότητας. Δεν απαιτείται σε κανονικές εφαρμογές."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"δέσμευση σε υπηρεσία ανταλλαγής μηνυμάτων"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Επιτρέπει στον κάτοχο τη σύνδεση με τη διεπαφή ανωτέρου επιπέδου μιας υπηρεσίας ανταλλαγής μηνυμάτων (π.χ. SpellCheckerService). Δεν είναι απαραίτητο για κανονικές εφαρμογές."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"δέσμευση σε υπηρεσία VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Επιτρέπει στην εφαρμογή την εγγραφή στην κάρτα SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"τροπ./διαγ. περ. απ. εσ. μνήμ."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Επιτρέπει στην εφαρμογή να τροποποιήσει τα περιεχόμενα των εσωτερικών μέσων αποθήκευσης."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"διαχείριση αποθ.χώρου εγγράφων"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Επιτρέπει στην εφαρμογή τη διαχείριση του αποθηκευτικού χώρου εγγράφων."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"πρόσβ.εξωτ.χωρ. αποθ. όλων των χρηστ."</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Επιτρέπει στην εφαρμογή να αποκτήσει πρόσβαση σε εξωτερικό χώρο αποθήκευσης για όλους τους χρήστες."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"πρόσβαση στο σύστημα αρχείων προσωρινής μνήμης"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Επιτρέπει στην εφαρμογή την κλήση της προεπιλεγμένης υπηρεσίας κοντέινερ για την αντιγραφή περιεχομένου. Δεν χρησιμοποιείται από συνήθεις εφαρμογές."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Διαγραφή διαδρομής δεδομένων εξόδου μέσων"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Επιτρέπει σε μια εφαρμογή τη διαγραφή διαδρομής δεδομένων εξόδου μέσων σε άλλες εξωτερικές συσκευές."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Πρόσβαση στον ασφαλή αποθηκευτικό χώρο με κλείδωμα"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Επιτρέπει σε μια εφαρμογή να αποκτήσει πρόσβαση στον ασφαλή αποθηκευτικό χώρο με κλείδωμα."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Έλεγχος εμφάνισης και απόκρυψης κλειδώματος πληκτρολογίου"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Επιτρέπει σε μια εφαρμογή τον έλεγχο του κλειδώματος πληκτρολογίου."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Αγγίξτε δύο φορές για έλεγχο εστίασης"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Δεν ήταν δυνατή η προσθήκη του γραφικού στοιχείου."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Μετάβαση"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Σύνδεση…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Διαθέσιμη"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Μη διαθέσιμο"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Σε χρήση"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ενσωματωμένη οθόνη"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Οθόνη HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Επικάλυψη #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Εσφαλμένο μοτίβο"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Εσφαλμένος κωδικός πρόσβασης"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Εσφαλμένος κωδικός PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Δοκιμάστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Δοκιμάστε ξανά σε <xliff:g id="NUMBER">%1$d</xliff:g> δευτερόλεπτα."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Σχεδιάστε το μοτίβο σας"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Εισαγωγή PIN SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Πληκτρολογήστε το PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Τρέχων χρήστης <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Κάτοχος"</string>
<string name="error_message_title" msgid="4510373083082500195">"Σφάλμα"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Αυτή η εφαρμογή δεν υποστηρίζει λογαριασμούς για περιορισμένα προφίλ"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Αυτή η εφαρμογή δεν υποστηρίζει λογαριασμούς για περιορισμένα προφίλ"</string>
<string name="app_not_found" msgid="3429141853498927379">"Δεν υπάρχει εφαρμογή για τη διαχείριση αυτής της ενέργειας"</string>
<string name="revoke" msgid="5404479185228271586">"Ανάκληση"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index a37c945..4825085 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Allows the app to move tasks to the foreground and background. The app may do this without your input."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"stop running apps"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Allows the app to remove tasks and kill their apps. Malicious apps may disrupt the behaviour of other apps."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"start any activity"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Allows the app to start any activity, regardless of permission protection or exported state."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"set screen compatibility"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Allows the holder to bind to the top-level interface of an input method. Should never be needed for normal apps."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"bind to an accessibility service"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Allows the holder to bind to the top-level interface of an accessibility service. Should never be needed for normal apps."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"bind to a text service"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Allows the holder to bind to the top-level interface of a text service (e.g. SpellCheckerService). Should never be needed for normal applications."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"bind to a VPN service"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Allows the app to write to the SD card."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modify/delete internal media storage contents"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Allows the app to modify the contents of the internal media storage."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"manage document storage"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Allows the app to manage document storage."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"access external storage of all users"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Allows the app to access external storage for all users."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"access the cache file system"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Allows the app to invoke default container service to copy content. Not for use by normal apps."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Route media output"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Allows an application to route media output to other external devices."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Access keyguard secure storage"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Allows an application to access keyguard secure storage."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Control displaying and hiding keyguard"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Allows an application to control keyguard."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Touch twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Go"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Connecting..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Available"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Not available"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"In use"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Built-in Screen"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI Screen"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Wrong Pattern"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Wrong Password"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Wrong PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Try again in <xliff:g id="NUMBER">%1$d</xliff:g> seconds."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Draw your pattern"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Enter SIM PIN"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Enter PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Current user <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Owner"</string>
<string name="error_message_title" msgid="4510373083082500195">"Error"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"This application does not support accounts for restricted profiles"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"This app doesn\'t support accounts for restricted profiles"</string>
<string name="app_not_found" msgid="3429141853498927379">"No application found to handle this action"</string>
<string name="revoke" msgid="5404479185228271586">"Revoke"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index a39e61f..615fcbe 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite que la aplicación mueva tareas a segundo o a primer plano. La aplicación puede utilizar este permiso para realizar estos movimientos sin indicártelo."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"detener las aplicaciones en ejecución"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite que la aplicación elimine tareas y cierre sus aplicaciones. Las aplicaciones malintencionadas pueden usar este permiso para interferir en el comportamiento de otras aplicaciones."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"Iniciar cualquier actividad"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Permite a la aplicación iniciar una actividad, sin importar si fue exportada ni si se encuentra protegida por permisos."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Definir compatibilidad de pantalla"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite al propietario vincularse a la interfaz de nivel superior de un método de entrada. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"vincular a un servicio de accesibilidad"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite al propietario vincularse a la interfaz de nivel superior de un servicio de accesibilidad. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"vincular a un servicio de texto"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite al titular vincularse a la interfaz de nivel superior de un servicio de texto (p. ej., SpellCheckerService). Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"vincular con un servicio de VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Admite que la aplicación escriba en la tarjeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar/eliminar los contenidos del almacenamientos de medios internos"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que la aplicación modifique el contenido del almacenamiento de medios interno."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"Administrar almac. de documen."</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Permite que la aplicación administre el almacenamiento de documentos."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"acceder almacenamiento externo"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite que la aplicación acceda al almacenamiento externo de todos los usuarios."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"Acceder al sistema de archivos caché"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Permite que la aplicación ejecute el servicio de contenedor predeterminado para que copie contenido. Las aplicaciones normales no deben utilizar este permiso."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Dirigir salida de medios"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite que la aplicación dirija salidas de medios a otros dispositivos externos."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Acceder al almacenamiento seguro de bloqueos"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Permite que una aplicación acceda al almacenamiento seguro de bloqueos."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Controlar cuándo se muestra y se oculta el bloqueo"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite que una aplicación controle los bloqueos."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toca dos veces para acceder al control de zoom."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"No se pudo agregar el widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Conectando..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"No disponible"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"En uso"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Pantalla integrada"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Pantalla HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposición #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Patrón incorrecto"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Contraseña incorrecta"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorrecto"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Vuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Vuelve a intentarlo en <xliff:g id="NUMBER">%1$d</xliff:g> segundos."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Dibuja tu patrón."</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ingresa el PIN de la tarjeta SIM."</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Ingresa el PIN."</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="owner_name" msgid="2716755460376028154">"Propietario"</string>
<string name="error_message_title" msgid="4510373083082500195">"Error"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Esta aplicación no admite cuentas de perfiles restringidos."</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Esta aplicación no admite cuentas de perfiles restringidos."</string>
<string name="app_not_found" msgid="3429141853498927379">"No se encontró una aplicación para manejar esta acción."</string>
<string name="revoke" msgid="5404479185228271586">"Revocar"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 8b666d8..2dbe795 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite que la aplicación mueva tareas a segundo o a primer plano. La aplicación puede utilizar este permiso para realizar estos movimientos sin que se lo indique el usuario."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"detener aplicaciones en ejecución"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite que la aplicación termine tareas y cierre sus aplicaciones. Las aplicaciones malintencionadas pueden usar este permiso para interferir en el comportamiento de otras aplicaciones."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"iniciar una actividad"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Permite que la aplicación inicie una actividad, independientemente de la protección de permisos o de si está exportada."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"establecer compatibilidad de pantalla"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite que se enlace con la interfaz de nivel superior de un método de introducción de texto. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"enlazar con un servicio de accesibilidad"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite enlazar con la interfaz de nivel superior de un servicio de accesibilidad. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"enlazar con un servicio de texto"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite enlazar con la interfaz de nivel superior de un servicio de texto (por ejemplo, SpellCheckerService). Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"enlazar con un servicio VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que la aplicación escriba en la tarjeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar o eliminar el contenido del almacenamiento de medios interno"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que la aplicación modifique el contenido del almacenamiento multimedia interno."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"administrar el almacenamiento de documentos"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Permite que la aplicación administre el almacenamiento de documentos."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"acceder al almacenamiento externo de todos los usuarios"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite que la aplicación acceda al almacenamiento externo de todos los usuarios."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"acceder al sistema de archivos almacenado en caché"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Permite que la aplicación ejecute el servicio de contenedor predeterminado para copiar contenido. Las aplicaciones normales no deben usar este permiso."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Dirigir salida de medio"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite que la aplicación dirija salidas de medios a otros dispositivos externos."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Acceder al almacenamiento seguro de bloqueos"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Permite que una aplicación acceda al almacenamiento seguro de bloqueos."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Controlar cuándo se muestra y se oculta el bloqueo"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite que una aplicación controle los bloqueos."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toca dos veces para acceder al control de zoom."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"No se ha podido añadir el widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Conectando..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"No disponible"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"En uso"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Pantalla integrada"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Pantalla HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposición #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"El patrón es incorrecto"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Contraseña incorrecta"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorrecto"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Inténtalo de nuevo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Inténtalo de nuevo en <xliff:g id="NUMBER">%1$d</xliff:g> segundos."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Dibuja tu patrón de desbloqueo."</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduce el PIN de la tarjeta SIM."</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Introduce el PIN."</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="owner_name" msgid="2716755460376028154">"Propietario"</string>
<string name="error_message_title" msgid="4510373083082500195">"Error"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Esta aplicación no admite cuentas de perfiles restringidos"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Esta aplicación no admite cuentas de perfiles restringidos"</string>
<string name="app_not_found" msgid="3429141853498927379">"No se ha encontrado ninguna aplicación que pueda realizar esta acción."</string>
<string name="revoke" msgid="5404479185228271586">"Revocar"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 92a1e93..cd3d97c 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Võimaldab rakendusel teisaldada ülesanded esiplaanile ja taustale. Rakendus võib seda teha teie sisendita."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"käitatud rakenduste peatamine"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Võimaldab rakendusel eemaldada ülesanded ja peatada nende rakendused. Pahatahtlikud rakendused võivad häirida teiste rakenduste käitumist."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"mis tahes toimingu alustamine"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Võimaldab rakendusel käivitada mis tahes toimingu loa kaitsest või eksporditud olekust sõltumata."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"kuva ühilduvuse seadmine"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lubab omanikul siduda sisestusmeetodi ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"sidumine juurdepääsuteenusega"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lubab omanikul luua sideme juurdepääsuteenuse ülataseme liidesega. Tavarakenduste puhul ei tohiks seda kunagi vaja minna."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"tekstiteenusega sidumine"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Võimaldab omanikul siduda tekstiteenuse (nt SpellCheckerService) ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"seo VPN-teenusega"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Võimaldab rakendusel kirjutada SD-kaardile."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"sisemälu sisu muutm./kustut."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Võimaldab rakendusel muuta sisemise andmekandja sisu."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"dokumendi talletuse haldamine"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Lubab rakendusel hallata dokumendi talletamist."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"juurdepääs välismäluseadmele (kõikidele kasutajatele)"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Võimaldab rakenduse kõikidel kasutajatel pöörduda välismäluseadme poole."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"juurdepääs vahemälu failisüsteemile"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Võimaldab rakendusel võtta sisu kopeerimiseks appi vaikekonteinerteenuse. Mitte kasutada tavarakenduste puhul."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Meediaväljundi teekonna koostamine"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Võimaldab rakendusel koostada teekonna meediaväljundist teistesse välistesse seadmetesse."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Juurdepääs võtmekaitsega turvalisele talletusruumile"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Lubab rakendusel hankida juurdepääsu võtmekaitsega turvalisele talletusruumile."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Võtmekaitse kuvamise ja peitmise juhtimine"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Lubab rakendusel võtmekaitset juhtida."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Suumi juhtimiseks puudutage kaks korda"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidinat ei saanud lisada."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Mine"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Ühendan..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Saadaval"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Ei ole saadaval"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Kasutusel"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Sisseehitatud ekraan"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI-ekraan"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Ülekate nr .<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Vale muster"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Vale parool"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Vale PIN-kood"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Proovige uuesti <xliff:g id="NUMBER">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Proovige uuesti <xliff:g id="NUMBER">%1$d</xliff:g> sekundi pärast."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Joonistage oma muster"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Sisestage SIM-i PIN-kood"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Sisestage PIN-kood"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Praegune kasutaja <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Omanik"</string>
<string name="error_message_title" msgid="4510373083082500195">"Viga"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"See rakendus ei toeta piiratud profiilide kontosid"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"See rakendus ei toeta piiratud profiilide kontosid"</string>
<string name="app_not_found" msgid="3429141853498927379">"Selle toimingu käsitlemiseks ei leitud ühtegi rakendust"</string>
<string name="revoke" msgid="5404479185228271586">"Tühista"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 459e1c3..d4b1cb7 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"به برنامه اجازه میدهد تا کارها را به پیشزمینه و پسزمینه منتقل کند. برنامه ممکن است بدون دخالت شما این کار را انجام دهد."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"متوقف کردن برنامههای در حال اجرا"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"به برنامه اجازه میدهد تا کارها را حذف کند و برنامههای آنها را متوقف کند. برنامههای مخرب میتوانند در اجرای برنامههای دیگر اختلال ایجاد کنند."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"شروع هر نوع فعالیت"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"به برنامه اجازه میدهد هر فعالیتی را شروع کند بدون اینکه وضعیت صادرشده یا حفاظت با مجوز در نظر گرفته شود."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"تنظیم سازگاری با صفحهٔ نمایش"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"به دارنده این دستگاه اجازه میدهد تا به رابط سطح بالای یک روش ورودی متصل شود. این ویژگی هیچگاه برای برنامههای معمولی ضروری نمیباشد."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"اتصال به سرویس دسترسی"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"به دارنده اجازه میدهد که به رابط سطح بالای سرویس دسترسی متصل شود. هرگز برای برنامههای معمولی مورد نیاز نیست."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"اتصال به یک سرویس متنی"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"به دارنده اجازه میدهد خود را به یک رابط سطح بالای خدمات متنی مرتبط کند (برای مثال SpellCheckerService). هرگز برای برنامههای عادی لازم نیست."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"اتصال به یک سرویس VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"به برنامه اجازه میدهد تا در کارت SD بنویسد."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"تغییر/حذف محتواهای حافظه رسانه داخلی"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"به برنامه اجازه میدهد تا محتویات حافظه رسانه داخلی را تغییر دهد."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"مدیریت فضای ذخیرهسازی اسناد"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"به برنامه اجازه میدهد فضای ذخیرهسازی اسناد را مدیریت کند."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"دسترسی به دستگاه ذخیره خارجی تمام کاربران"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"به برنامه اجازه میدهد به دستگاه ذخیره خارجی برای همه کاربران دسترسی داشته باشد."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"دسترسی به سیستم فایل حافظهٔ پنهان"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"به برنامه اجازه میدهد تا سرویس پیشفرض را فراخوانی کند و محتوا را کپی کند. برای استفاده برنامههای عادی مورد نیاز نیست."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"تعیین مسیر خروجی رسانه"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"به یک برنامه اجازه میدهد خروجی رسانه را به دستگاههای خارجی دیگر تعیین مسیر کند."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"دسترسی به فضای ذخیرهسازی ایمن محافظ کلید"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"به یک برنامه کاربردی برای دسترسی به فضای ذخیرهسازی ایمن محافظ کلید اجازه میدهد."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"کنترل نمایش و پنهان کردن محافظ کلید"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"اجازه میدهد برنامهای محافظ کلید را کنترل کند."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"دوبار لمس کنید تا بزرگنمایی کنترل شود"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"افزودن ابزارک انجام نشد."</string>
<string name="ime_action_go" msgid="8320845651737369027">"برو"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"درحال اتصال…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"در دسترس"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"در دسترس نیست"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"در حال استفاده"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"صفحه نمایش از خود"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"صفحه HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"همپوشانی #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"الگوی اشتباه"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"گذرواژه اشتباه"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"پین اشتباه"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"پس از <xliff:g id="NUMBER">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"پس از <xliff:g id="NUMBER">%1$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"الگوی خود را رسم کنید"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"پین سیم کارت را وارد کنید"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"پین را وارد کنید"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"کاربر کنونی <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"دارنده"</string>
<string name="error_message_title" msgid="4510373083082500195">"خطا"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"این برنامه از حسابهای متعلق به نمایههای محدود پشتیبانی نمیکند"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"این برنامه از حسابهای متعلق به نمایههای محدود پشتیبانی نمیکند"</string>
<string name="app_not_found" msgid="3429141853498927379">"برنامهای برای انجام این عملکرد موجود نیست"</string>
<string name="revoke" msgid="5404479185228271586">"لغو"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 9740070..bded5cc 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Antaa sovelluksen siirtää tehtäviä etualalle ja taustalle. Sovellus ei tarvitse toimiin käyttäjän lupaa."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"käynnissä olevien sovellusten pysäyttäminen"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Antaa sovelluksen poistaa tehtäviä ja lopettaa niiden sovelluksia. Haitalliset sovellukset voivat häiritä muiden sovellusten toimintaa."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"käynnistä mikä tahansa toiminto"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Antaa sovelluksen käynnistää minkä tahansa toiminnon käyttölupasuojauksesta tai viennin tilasta huolimatta."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"aseta näytön yhteensopivuus"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Antaa sovelluksen sitoutua syötetavan ylätason käyttöliittymään. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"sitoudu esteettömyyspalveluun"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Antaa sovelluksen sitoutua esteettömyyspalvelun ylemmän tason käyttöliittymään. Ei tavallisten sovelluksien käyttöön."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"tekstipalveluun sitoutuminen"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Antaa sovelluksen sitoutua tekstipalvelun (kuten SpellCheckerServicen) ylätason liittymään. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"sitoudu VPN-palveluun"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Antaa sovelluksen kirjoittaa SD-kortille."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"muokkaa/poista sisäisen säilytystilan sisältöä"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Antaa sovelluksen muokata sisäisen tallennustilan sisältöä."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"hallinnoi dokum. tallennusta"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Antaa sovelluksen hallinnoida dokumenttien tallennusta."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"käyttää kaikkien käyttäjien ulk. tallennustilaa"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Sallii sovelluksen käyttää ulkoista tallennustilaa kaikille käyttäjille."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"käytä välimuistin tiedostojärjestelmää"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Antaa sovelluksen kutsua oletussäilöpalvelua sisällön kopioimiseen. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Median reititys"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Antaa sovelluksen reitittää mediaa muihin ulkoisiin laitteisiin."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Salasanalla suojatun tallennustilan hallinta"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Sallii sovelluksen käyttää salasanalla suojattua tallennustilaa."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Hallinnoi näppäinvahdin näyttämistä ja piilottamista"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Antaa sovelluksen hallita näppäinvahtia."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Ohjaa zoomausta napauttamalla kahdesti"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widgetin lisääminen epäonnistui."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Siirry"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Yhdistetään..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Käytettävissä"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Ei käytettävissä"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Käytössä"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Yhdysrakenteinen näyttö"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI-ruutu"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Peittokuva # <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Väärä kuvio"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Väärä salasana"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Väärä PIN-koodi"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Yritä uudelleen <xliff:g id="NUMBER">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Yritä uudelleen <xliff:g id="NUMBER">%1$d</xliff:g> sekunnin kuluttua."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Piirrä kuvio"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Anna SIM-kortin PIN-koodi"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Anna PIN-koodi"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Nykyinen käyttäjä: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Omistaja"</string>
<string name="error_message_title" msgid="4510373083082500195">"Virhe"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Tämä sovellus ei tue rajoitettujen profiilien tilejä"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Tämä sovellus ei tue rajoitettujen profiilien tilejä"</string>
<string name="app_not_found" msgid="3429141853498927379">"Tätä toimintoa käsittelevää sovellusta ei löydy"</string>
<string name="revoke" msgid="5404479185228271586">"Peruuta"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index a1c795f..549c4ae 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permet à l\'application de déplacer les tâches au premier plan et en arrière-plan. L\'application peut procéder à ces opérations sans votre intervention."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"arrêter les applications en cours d\'exécution"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Permet à l\'application de supprimer des tâches et de fermer les applications qui les exécutent. Des applications malveillantes peuvent exploiter cette fonctionnalité pour perturber le comportement des autres applications."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"démarrer n\'importe quelle activité"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Permet à l\'application de démarrer n\'importe quelle activité, quels que soient l\'état exporté ou le degré de protection appliqué à l\'autorisation."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"définir la compatibilité de l\'écran"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un mode de saisie. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"associer à un service d\'accessibilité"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service d\'accessibilité. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"associer à un service de texte"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Permet à l\'application de s\'associer à l\'interface de haut niveau d\'un service de texte (par exemple, SpellCheckerService). Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"associer à un service VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet à l\'application de modifier le contenu de la carte SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Modifier/Supprimer contenu mémoire interne"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permet à l\'application de modifier le contenu de la mémoire de stockage multimédia interne."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"gérer stockage des documents"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Permet à l\'application de gérer le stockage des documents."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"Accès stock. ext. tous utilis."</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permet à l\'application d\'accéder à la mémoire de stockage externe pour tous les utilisateurs."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accéder au système de fichiers en cache"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Permet à l\'application d\'invoquer le service de conteneur par défaut pour copier du contenu. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Diriger la sortie multimédia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permet à une application de diriger la sortie multimédia vers d\'autres appareils externes."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Accéder au stockage sécurisé keyguard"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Permet à une application d\'accéder au stockage sécurisé keyguard."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Contrôler l\'affichage et le masquage de la protection des touches"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permet à une application de contrôler la protection des touches."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Appuyez deux fois pour régler le zoom."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Impossible d\'ajouter le widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"OK"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Connexion en cours..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Indisponible"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"En cours d\'utilisation"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Écran intégré"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Écran HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposition n° <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Schéma incorrect."</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Mot de passe incorrect."</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Code PIN incorrect."</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Réessayez dans <xliff:g id="NUMBER">%1$d</xliff:g> secondes."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Dessinez votre schéma."</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Saisissez le code PIN de la carte SIM."</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Saisissez le code PIN."</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Utilisateur actuel : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="owner_name" msgid="2716755460376028154">"Propriétaire"</string>
<string name="error_message_title" msgid="4510373083082500195">"Erreur"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Les comptes des profils limités ne sont pas acceptés pour cette application."</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Les comptes des profils en accès limité ne sont pas compatibles avec cette application."</string>
<string name="app_not_found" msgid="3429141853498927379">"Aucune application trouvée pour gérer cette action."</string>
<string name="revoke" msgid="5404479185228271586">"Révoquer"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index a2180a8..768dbd1 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"एप्लिकेशन को कार्यों को अग्रभूमि और पृष्ठभूमि पर ले जाने देता है. एप्लिकेशन आपके इनपुट के बिना यह कर सकता है."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"चलने वाले एप्लिकेशन रोकें"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"किसी एप्लिकेशन को कार्यों को निकालने और उनके एप्लिकेशन समाप्त करने देता है. दुर्भावनापूर्ण एप्लिकेशन अन्य एप्लिकेशन का व्यवहार बाधित कर सकते हैं."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"कोई गतिविधि प्रारंभ करें"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"अनुमति सुरक्षा या निर्यात की स्थिति पर ध्यान दिए बिना, एप्लिकेशन को कोई गतिविधि प्रारंभ करने देता है."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"स्क्रीन संगतता सेट करें"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"धारक को किसी इनपुट विधि के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"पहुंच-योग्यता सेवा से आबद्ध करें"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"धारक को किसी पहुंच-योग्यता सेवा के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"किसी पाठ सेवा पर बने रहें"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"धारक को किसी पाठ सेवा (उदा. SpellCheckerService) के शीर्ष-स्तर इंटरफ़ेस पर आबद्ध होने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"किसी VPN सेवा से आबद्ध करें"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"एप्लिकेशन को SD कार्ड पर लिखने देता है."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"आंतरिक मीडिया संग्रहण सामग्रियों को बदलें/हटाएं"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"एप्लिकेशन को आंतरिक मीडिया संग्रहण की सामग्री को संशोधित करने देता है."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"दस्तावेज़ संग्रहण प्रबंधित करें"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"एप्लिकेशन को दस्तावेज़ संग्रहण प्रबंधित करने की अनुमति दें."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"सभी उपयोगकर्ताओं के बाहरी संग्रहण तक पहुंचें"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"एप्लिकेशन को सभी उपयोगकर्ताओं के बाहरी संग्रहण तक पहुंचने दें."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"कैश फ़ाइल सिस्टम में पहंचे"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"एप्लिकेशन को सामग्री की प्रतिलिपि बनाने के लिए डिफ़ॉल्ट कंटेनर सेवा शुरू करने देता है. सामान्य एप्लिकेशन द्वारा उपयोग करने के लिए नहीं."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"मीडिया आउटपुट को रूट करें"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"एप्लिकेशन को मीडिया आउटपुट को अन्य बाहरी उपकरणों पर रूट करने देता है."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"कीगार्ड सुरक्षित संग्रहण एक्सेस करें"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"एप्लिकेशन को कीगार्ड सुरक्षित संग्रहण एक्सेस करने देती है."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"कीगार्ड दिखाना और छिपाना नियंत्रित करें"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"एप्लिकेशन को कीगार्ड नियंत्रित करने देती है."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ज़ूम नियंत्रण के लिए दो बार स्पर्श करें"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"विजेट नहीं जोड़ा जा सका."</string>
<string name="ime_action_go" msgid="8320845651737369027">"जाएं"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"कनेक्ट हो रहा है..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"उपलब्ध"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"उपलब्ध नहीं"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"उपयोग में"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"अंतर्निहित स्क्रीन"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI स्क्रीन"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"ओवरले #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"गलत प्रतिमान"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"गलत पासवर्ड"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"गलत PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%1$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"अपना प्रतिमान आरेखित करें"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"सिम PIN डालें"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"PIN डालें"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"वर्तमान उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"स्वामी"</string>
<string name="error_message_title" msgid="4510373083082500195">"त्रुटि"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"यह एप्लिकेशन प्रतिबंधित प्रोफ़ाइल के खातों का समर्थन नहीं करता है"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"यह एप्लिकेशन प्रतिबंधित प्रोफ़ाइल के खातों का समर्थन नहीं करता है"</string>
<string name="app_not_found" msgid="3429141853498927379">"इस कार्यवाही को प्रबंधित करने के लिए कोई एप्लिकेशन नहीं मिला"</string>
<string name="revoke" msgid="5404479185228271586">"निरस्त करें"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 733a0e3..730fb21 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Aplikaciji omogućuje premještanje zadataka u prednji plan ili pozadinu. Aplikacija to može napraviti bez vašeg naloga."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"zaustavljanje pokrenutih aplikacija"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Omogućuje aplikaciji uklanjanje zadataka i uklanjanje njihovih aplikacija. Zlonamjerne aplikacije mogu poremetiti rad drugih aplikacija."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"započni bilo kakvu aktivnost"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Omogućuje aplikaciji da započne bilo koju aktivnost, bez obzira na zaštitu pomoću dozvola ili stanje izvoza."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"postavljanje kompatibilnosti sa zaslonom"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Nositelju omogućuje povezivanje sučelja najviše razine načina unosa. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"vezivanje uz uslugu dostupnosti"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Nositelju omogućuje vezanje uz sučelje najviše razine usluge dostupnosti. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"vezanje na tekstualnu uslugu"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Omogućuje korisniku povezivanje s najvišom razinom sučelja tekstualne usluge (npr. SpellCheckerService). Ne bi smjelo biti potrebno za normalne aplikacije."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"vezanje na VPN uslugu"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Aplikaciji omogućuje pisanje na SD karticu."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"izmijeni/izbriši sadržaj pohranjen na internim medijima"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Aplikaciji omogućuje izmjenu sadržaja pohranjenog na internim medijima."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"upravljanje pohr. dokumenata"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Omogućuje aplikaciji upravljanje pohranom dokumenata."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"pristup vanjskoj pohrani svima"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Omogućuje aplikaciji pristup vanjskoj pohrani za sve korisnike."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pristup sustavu datoteka predmemorije"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Omogućuje aplikaciji dozivanje usluge zadanog spremnika radi kopiranja sadržaja. Nije namijenjena uobičajenim aplikacijama."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Usmjeravanje medijskog izlaza"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Aplikaciji omogućuje usmjeravanje medijskog izlaza na druge vanjske uređaje."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Pristup zaključanoj sigurnoj pohrani"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Omogućuje aplikaciji pristupanje zaključanoj sigurnoj pohrani."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Upravljanje prikazivanjem i skrivanjem zaključavanja tipkovnice"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Omogućuje aplikaciji upravljanje zaključavanjem tipkovnice."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dodirnite dvaput za upravljanje zumiranjem"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget nije moguće dodati."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Idi"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Povezivanje..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Dostupno"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Nije dostupno"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"U upotrebi"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ugrađeni zaslon"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI zaslon"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Preklapanje br. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Pogrešan obrazac"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Pogrešna zaporka"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Pogrešan PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Pokušajte ponovo za <xliff:g id="NUMBER">%1$d</xliff:g> s."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Iscrtajte svoj obrazac"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Unesite PIN za SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Unesite PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Trenutačni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Vlasnik"</string>
<string name="error_message_title" msgid="4510373083082500195">"Pogreška"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Aplikacija ne podržava račune za ograničene profile"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Aplikacija ne podržava račune za ograničene profile"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nije pronađena aplikacija za upravljanje ovom radnjom"</string>
<string name="revoke" msgid="5404479185228271586">"Opozovi"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index dab8941..23f8e74 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Lehetővé teszi az alkalmazás számára, hogy feladatokat helyezzen át az előtérből a háttérbe és fordítva. Az alkalmazás ezt az Ön jóváhagyása nélkül is megteheti."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"futó alkalmazások leállítása"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Lehetővé teszi, hogy az alkalmazás feladatokat távolítson el és leállítsa azok alkalmazásait. Rosszindulatú alkalmazások megzavarhatják más alkalmazások viselkedését."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"bármely tevékenység elindítása"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Lehetővé teszi az alkalmazás számára bármely tevékenység elindítását az engedélyektől és exportált állapottól függetlenül."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Képernyő-kompatibilitás beállítása"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lehetővé teszi, hogy a tulajdonos kötelezővé tegye egy beviteli mód legfelső szintű felületét. A normál alkalmazásoknak erre soha nincs szüksége."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"csatlakozás egy kisegítő szolgáltatáshoz"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lehetővé teszi a használó számára, hogy csatlakozzon egy kisegítő szolgáltatás legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"csatlakozás szövegszolgáltatáshoz"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Lehetővé teszi, hogy a tulajdonos egy szöveges szolgáltatás felső szintjéhez kapcsolódjon (pl. SpellCheckerService). A normál alkalmazásoknak erre soha nincs szüksége."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"csatlakozás egy VPN-szolgáltatáshoz"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lehetővé teszi az alkalmazás számára, hogy írjon az SD-kártyára."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"belső tár tartalmának módosítása/törlése"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a belső háttértár tartalmát."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"dokumentumtárhely kezelése"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Lehetővé teszi az alkalmazás számára a dokumentumtárhely kezelését."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"hozzáf. minden felh. külső tár"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Minden felhasználó számára lehetővé teszi, hogy az alkalmazás hozzáférjen külső tárolókhoz."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"hozzáférés a gyorsítótár fájlrendszeréhez"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Lehetővé teszi az alkalmazás számára az alapértelmezett tárolószolgáltatás használatát tartalom másolásához. Normál alkalmazások nem használhatják."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Médiafájlok kimenetének irányítása"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Lehetővé teszi az alkalmazás számára, hogy más külső eszközökre irányítsa a médiafájlok lejátszását."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Hozzáférés a kóddal védett tárhelyhez"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Lehetővé teszi egy alkalmazás számára, hogy hozzáférjen a kóddal védett tárhelyhez."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Billentyűzár megjelenítésének és elrejtésének vezérlése"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Lehetővé teszi egy alkalmazás számára a billentyűzár vezérlését."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Érintse meg kétszer a nagyítás beállításához"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nem sikerült hozzáadni a modult."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Ugrás"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Kapcsolódás..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Elérhető"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Nem érhető el"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Használatban"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Beépített képernyő"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI-képernyő"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"<xliff:g id="ID">%1$d</xliff:g>. fedvény"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Helytelen minta"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Helytelen jelszó"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Helytelen PIN kód"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Próbálkozzon újra <xliff:g id="NUMBER">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Próbálkozzon újra <xliff:g id="NUMBER">%1$d</xliff:g> másodperc múlva."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Rajzolja le a mintát"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Adja meg a SIM kártya PIN kódját"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Adja meg a PIN kódot"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"<xliff:g id="NAME">%1$s</xliff:g> az aktuális felhasználó."</string>
<string name="owner_name" msgid="2716755460376028154">"Tulajdonos"</string>
<string name="error_message_title" msgid="4510373083082500195">"Hiba"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Ez az alkalmazás nem támogatja a korlátozott profilokkal rendelkező fiókokat"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Ez az alkalmazás nem támogatja a korlátozott profilokkal rendelkező fiókokat"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nincs megfelelő alkalmazás a művelet elvégzésére."</string>
<string name="revoke" msgid="5404479185228271586">"Visszavonás"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 3db9137..ee652c5 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Memungkinkan aplikasi memindah tugas ke latar depan dan latar belakang. Aplikasi dapat melakukannya tanpa masukan dari Anda."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"menghentikan apl yang berjalan"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Memungkinkan apl menghapus tugas dan menutup aplikasinya. Apl berbahaya dapat mengganggu perilaku apl lain."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"memulai aktivitas apa pun"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Mengizinkan aplikasi memulai aktivitas apa pun, terlepas dari perlindungan izin atau status yang diekspor."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"menyetel kompatibilitas layar"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu metode masukan. Tidak pernah diperlukan oleh apl normal."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"mengikat ke layanan aksesibilitas"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Mengizinkan pemegang untuk mengikat antarmuka tingkat tinggi dari suatu layanan. Tidak pernah diperlukan oleh aplikasi normal."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"mengikat ke layanan SMS"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu layanan teks (misal: SpellCheckerService). Tidak pernah diperlukan oleh apl normal."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"mengikat ke layanan VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Memungkinkan apl menulis ke kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ubah/hapus konten penyimpanan media internal"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Mengizinkan apl memodifikasi konten penyimpanan media internal."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"kelola penyimpanan dokumen"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Mengizinkan aplikasi mengelola penyimpanan dokumen."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"akses penyimpanan eksternal"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Izinkan aplikasi mengakses penyimpanan eksternal untuk semua pengguna."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"akses sistem file cache."</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Mengizinkan apl menjalankan layanan kontainer default untuk menyalin konten. Tidak untuk digunakan oleh apl normal."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Menentukan rute keluaran media"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Memungkinkan aplikasi menentukan rute keluaran media ke perangkat eksternal lainnya."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Mengakses pengaman penyimpanan aman"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Mengizinkan aplikasi mengakses pengaman penyimpanan aman."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Kontrol untuk menampilkan dan menyembunyikan pengaman"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Izinkan aplikasi untuk mengontrol pengaman."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Sentuh dua kali untuk mengontrol perbesar/perkecil"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Tidak dapat menambahkan widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Buka"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Menyambung..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tersedia"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Tidak tersedia"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Sedang digunakan"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Layar Bawaan"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Layar HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Hamparan #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Pola Salah"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Sandi Salah"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN Salah"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Coba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> detik."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Coba lagi dalam <xliff:g id="NUMBER">%1$d</xliff:g> detik."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Gambar pola Anda"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Masukkan PIN SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Masukkan PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Pengguna saat ini <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Pemilik"</string>
<string name="error_message_title" msgid="4510373083082500195">"Kesalahan"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Aplikasi ini tidak mendukung akun untuk profil yang dibatasi"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Aplikasi ini tidak mendukung akun untuk profil yang dibatasi"</string>
<string name="app_not_found" msgid="3429141853498927379">"Tidak ada aplikasi yang ditemukan untuk menangani tindakan ini"</string>
<string name="revoke" msgid="5404479185228271586">"Cabut"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 416c534..4059ad5 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Consente all\'applicazione di spostare attività in primo piano e in background. L\'applicazione potrebbe farlo senza un tuo comando."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"interruzione applicazioni in esecuzione"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Consente all\'applicazione di rimuovere le attività e terminare le loro applicazioni. Le applicazioni dannose potrebbero interferire con il comportamento di altre applicazioni."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"inizio di un\'attività"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Consente all\'applicazione di iniziare un\'attività, indipendentemente dalla protezione delle autorizzazioni o dallo stato esportato."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"impostazione compatibilità schermo"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Consente l\'associazione di un metodo di inserimento all\'interfaccia principale. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"collegamento a un servizio di accessibilità"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Consente al titolare di collegarsi all\'interfaccia di primo livello di un servizio di accessibilità. Non dovrebbe essere mai necessaria per le normali applicazioni."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"associazione a un servizio di testo"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Consente al titolare di collegarsi all\'interfaccia di primo livello di un servizio di testo (ad esempio SpellCheckerService). Non dovrebbe essere mai necessaria per le normali applicazioni."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"associazione a un servizio VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Consente all\'applicazione di scrivere sulla scheda SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modifica/eliminaz. contenuti archivio media int."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Consente all\'applicazione di modificare i contenuti dell\'archivio media interno."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"gestione della memorizzazione dei documenti"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Consente all\'app di gestire la memorizzazione dei documenti."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"accesso memoria esterna utenti"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Consente all\'applicazione di accedere alla memoria esterna di tutti gli utenti."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accesso al filesystem nella cache"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Consente all\'applicazione di richiamare il servizio container predefinito per la copia di contenuti. Da non usare per normali applicazioni."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Indirizzamento uscita media"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Consente a un\'applicazione di indirizzare l\'uscita di media verso altri dispositivi esterni."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Accesso all\'archivio sicuro keyguard"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Consente a un\'applicazione di accedere all\'archivio sicuro keguard."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Controllo della visualizzazione e dell\'occultamento di keyguard"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Consente a un\'applicazione di controllare keguard."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Tocca due volte per il comando dello zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Aggiunta del widget non riuscita."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Vai"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Connessione..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponibile"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Non disponibili"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"In uso"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Schermo incorporato"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Schermo HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay n. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Sequenza sbagliata"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Password sbagliata"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN errato"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Riprova fra <xliff:g id="NUMBER">%d</xliff:g> secondi."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Riprova fra <xliff:g id="NUMBER">%1$d</xliff:g> secondi."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Inserisci la sequenza"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Inserisci il PIN della SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Inserisci PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Utente corrente <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Proprietario"</string>
<string name="error_message_title" msgid="4510373083082500195">"Errore"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Questa applicazione non supporta account relativi a profili con limitazioni"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Questa app non supporta account relativi a profili con limitazioni"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nessuna applicazione trovata in grado di gestire questa azione"</string>
<string name="revoke" msgid="5404479185228271586">"Revoca"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 7afb72c..6168fdf 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"מאפשר ליישום להעביר משימות לחזית ולרקע. היישום עשוי לעשות זאת ללא התערבותך."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"עצירת יישומים פעילים"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"הרשאה זו מאפשרת ליישום להסיר משימות ולסגור את היישומים שבהם הן פועלות. יישומים זדוניים עלולים לשבש את פעולתם של יישומים אחרים."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"התחלת פעילות מכל סוג שהוא"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"מאפשר ליישום להתחיל בפעילות מכל סוג שהוא, ללא התחשבות בהגנת הרשאות או במצב מיוצא."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"הגדרת תאימות מסך"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"מאפשר למשתמש לבצע איגוד לממשק ברמה עליונה של שיטת קלט. הרשאה זו לעולם אינה נחוצה ליישומים רגילים."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"הכפפה לשירות נגישות"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"מתיר לבעלים להכפיף לממשק ברמה העליונה של שירות זמינות. הרשאה זו אף פעם אינה אמורה להיות נחוצה ליישומים רגילים."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"הכפפה לשירות טקסט"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"מאפשר למשתמש ליצור איגוד לממשק הרמה העליונה של שירות טקסט (למשל, SpellCheckerService). הרשאה זו לעולם אינה נחוצה ליישומים רגילים."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"אגד לשירות VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"מאפשר ליישום לכתוב לכרטיס ה-SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"שנה/מחק תוכן של אחסון מדיה פנימי"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"מאפשר ליישום לשנות את התוכן של אמצעי האחסון הפנימי למדיה."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"ניהול של אחסון מסמכים"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"מאפשר ליישום לנהל אחסון מסמכים."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"גישה לאחסון חיצוני, כל המשתמשים"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"מאפשר ליישום לגשת לאחסון חיצוני עבור כל המשתמשים."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"גישה למערכת הקבצים בקובץ השמור"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"מאפשר ליישום להפעיל שירות גורם מכיל המוגדר כברירת מחדל להעתקת תוכן. לא מיועד לשימוש על ידי יישומים רגילים."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"ניתוב פלט מדיה"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"מאפשר ליישום לנתב פלט מדיה למכשירים חיצוניים אחרים."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"גישה לאחסון המוגן באמצעות מפתח"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"מאפשר ליישום לגשת לאחסון המוגן באמצעות מפתח."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"שלוט בהצגה והסתרה של מגן המקלדת"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"מאפשר ליישום לשלוט במגן המקלדת."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"גע פעמיים לבקרת מרחק מתצוגה"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"לא ניתן להוסיף widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"התחל"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"מתחבר..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"זמין"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"לא זמין"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"בשימוש"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"מסך מובנה"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"מסך HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"שכבת על #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"קו ביטול נעילה שגוי"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"סיסמה שגויה"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"מספר PIN שגוי"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"נסה שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"נסה שוב בעוד <xliff:g id="NUMBER">%1$d</xliff:g> שניות."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"שרטט את קו ביטול הנעילה"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"הזן מספר PIN ל-SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"הזן מספר PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"המשתמש הנוכחי <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"בעלים"</string>
<string name="error_message_title" msgid="4510373083082500195">"שגיאה"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"היישום הזה לא תומך בחשבונות עבור פרופילים מוגבלים"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"האפליקציה הזו לא תומכת בחשבונות עבור פרופילים מוגבלים"</string>
<string name="app_not_found" msgid="3429141853498927379">"לא נמצא יישום שתומך בפעולה זו"</string>
<string name="revoke" msgid="5404479185228271586">"בטל"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 340774c..eb1556d 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"タスクをフォアグラウンドやバックグラウンドに移動することをアプリに許可します。これにより、アプリがユーザーからの入力なしでこの処理を実行する可能性があります。"</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"実行中のアプリの停止"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"タスクの削除とアプリの終了をアプリに許可します。この許可を悪意のあるアプリケーションに利用されると、他のアプリの動作が妨害される恐れがあります。"</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"すべてのアクティビティの開始"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"権限による保護やエクスポートされた状態を問わず、すべてのアクティビティを開始することをアプリに許可します。"</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"画面互換性の設定"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"入力方法のトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ユーザー補助サービスにバインド"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ユーザー補助サービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"テキストサービスにバインド"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"テキストサービス(SpellCheckerServiceなど)のトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"VPNサービスにバインド"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SDカードへの書き込みをアプリに許可します。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"内部メディアストレージの内容の変更/削除"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"内部メディアストレージの内容を変更することをアプリに許可します。"</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"ドキュメントストレージの管理"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"ドキュメントストレージの管理をアプリに許可します。"</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"全ユーザー外部ストレージへのアクセス"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"すべてのユーザーの外部ストレージへのアクセスをアプリに許可します。"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"キャッシュファイルシステムにアクセス"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"デフォルトのコンテナサービスを呼び出してコンテンツをコピーすることをアプリに許可します。通常のアプリでは使用しません。"</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"メディア出力のルーティング"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"メディア出力を他の外部デバイスにルーティングすることをアプリに許可します。"</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"キーガードセキュアストレージへのアクセス"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"キーガードセキュアストレージへのアクセスをアプリに許可する"</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"キーガードの表示/非表示の制御"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"キーガードの制御をアプリに許可します。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ダブルタップでズームコントロール"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ウィジェットを追加できませんでした。"</string>
<string name="ime_action_go" msgid="8320845651737369027">"移動"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"接続中..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"利用できます"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"利用できません"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"使用中"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"内蔵スクリーン"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI画面"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"オーバーレイ第<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"パターンが正しくありません"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"パスワードが正しくありません"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PINが正しくありません"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%1$d</xliff:g>秒後にもう一度お試しください。"</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"パターンを入力"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM PINを入力"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"PINを入力"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"現在のユーザーは<xliff:g id="NAME">%1$s</xliff:g>です。"</string>
<string name="owner_name" msgid="2716755460376028154">"所有者"</string>
<string name="error_message_title" msgid="4510373083082500195">"エラー"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"このアプリでは制限付きプロフィールのアカウントはサポートしていません"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"このアプリでは制限付きプロフィールのアカウントはサポートしていません"</string>
<string name="app_not_found" msgid="3429141853498927379">"この操作を行うアプリが見つかりません"</string>
<string name="revoke" msgid="5404479185228271586">"取り消し"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 73cdb1c..8cd152d 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"앱이 사용자의 입력 없이 작업을 포그라운드나 백그라운드로 이동할 수 있도록 허용합니다."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"실행 중인 앱 중지"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"애플리케이션이 작업을 삭제하거나 다른 애플리케이션을 중지시킬 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션의 동작을 멈추게 할 수 있습니다."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"원하는 활동 시작"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"권한 보호나 내보낸 상태에 관계없이 앱이 활동을 시작하도록 합니다."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"화면 호환성 설정"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"권한을 가진 프로그램이 입력 방법에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"접근성 서비스와 연결"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"권한을 가진 프로그램이 접근성 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"텍스트 서비스 연결"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"권한을 가진 프로그램이 텍스트 서비스(예: SpellCheckerService)에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"VPN 서비스와 연결"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"앱이 SD 카드에 쓸 수 있도록 허용합니다."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"내부 미디어 저장소 콘텐츠 수정/삭제"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"앱이 내부 미디어 저장소의 콘텐츠를 수정할 수 있도록 허용합니다."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"문서 저장공간 관리"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"앱이 문서 저장공간을 관리할 수 있도록 허용합니다."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"모든 사용자의 외부 저장소에 액세스"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"앱이 모든 사용자의 외부 저장소에 액세스하도록 허용합니다."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"캐시 파일시스템 액세스"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"기본 컨테이너 서비스를 호출하여 콘텐츠를 복사할 수 있도록 허용합니다. 일반 앱에서는 사용하지 않습니다."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"미디어 출력 연결"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"앱이 미디어 출력을 기타 외부 기기에 연결할 수 있도록 허용합니다."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"키가드 보안 저장소 액세스"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"애플리케이션에서 키가드 보안 저장소에 액세스하도록 허용합니다."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"키가드 표시 및 숨기기 설정"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"애플리케이션에서 키가드를 제어하도록 허용합니다."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"확대/축소하려면 두 번 터치하세요."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"위젯을 추가할 수 없습니다."</string>
<string name="ime_action_go" msgid="8320845651737369027">"이동"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"연결 중..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"사용 가능"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"사용할 수 없음"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"사용 중"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"기본으로 제공되는 화면"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI 화면"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"<xliff:g id="ID">%1$d</xliff:g>번째 오버레이"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"잘못된 패턴"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"잘못된 비밀번호"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"잘못된 PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%1$d</xliff:g>초 후에 다시 시도해 주세요."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"패턴 그리기"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM PIN 입력"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"PIN 입력"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"현재 사용자는 <xliff:g id="NAME">%1$s</xliff:g>님입니다."</string>
<string name="owner_name" msgid="2716755460376028154">"소유자"</string>
<string name="error_message_title" msgid="4510373083082500195">"오류"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"이 애플리케이션은 제한된 프로필의 계정을 지원하지 않습니다."</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"이 앱은 제한된 프로필의 계정을 지원하지 않습니다."</string>
<string name="app_not_found" msgid="3429141853498927379">"이 작업을 처리하는 애플리케이션을 찾을 수 없습니다."</string>
<string name="revoke" msgid="5404479185228271586">"취소"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-land/alias.xml b/core/res/res/values-land/alias.xml
deleted file mode 100644
index eac5ece..0000000
--- a/core/res/res/values-land/alias.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/colors.xml
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- Alias used to reference one of two possible layouts in keyguard. -->
- <item type="layout" name="keyguard_eca">@android:layout/keyguard_emergency_carrier_area_empty</item>
-</resources>
diff --git a/core/res/res/values-land/arrays.xml b/core/res/res/values-land/arrays.xml
index 240b9e4..5602a1c 100644
--- a/core/res/res/values-land/arrays.xml
+++ b/core/res/res/values-land/arrays.xml
@@ -19,54 +19,4 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Resources for GlowPadView in LockScreen -->
- <array name="lockscreen_targets_when_silent">
- <item>@null</item>"
- <item>@drawable/ic_action_assist_generic</item>
- <item>@drawable/ic_lockscreen_soundon</item>
- <item>@drawable/ic_lockscreen_unlock</item>
- </array>
-
- <array name="lockscreen_target_descriptions_when_silent">
- <item>@null</item>
- <item>@string/description_target_search</item>
- <item>@string/description_target_soundon</item>
- <item>@string/description_target_unlock</item>
- </array>
-
- <array name="lockscreen_direction_descriptions">
- <item>@null</item>
- <item>@string/description_direction_up</item>
- <item>@string/description_direction_left</item>
- <item>@string/description_direction_down</item>
- </array>
-
- <array name="lockscreen_targets_when_soundon">
- <item>@null</item>
- <item>@drawable/ic_action_assist_generic</item>
- <item>@drawable/ic_lockscreen_silent</item>
- <item>@drawable/ic_lockscreen_unlock</item>
- </array>
-
- <array name="lockscreen_target_descriptions_when_soundon">
- <item>@null</item>
- <item>@string/description_target_search</item>
- <item>@string/description_target_silent</item>
- <item>@string/description_target_unlock</item>
- </array>
-
- <array name="lockscreen_targets_with_camera">
- <item>@null</item>
- <item>@drawable/ic_action_assist_generic</item>
- <item>@drawable/ic_lockscreen_camera</item>
- <item>@drawable/ic_lockscreen_unlock</item>
- </array>
-
- <array name="lockscreen_target_descriptions_with_camera">
- <item>@null</item>
- <item>@string/description_target_search</item>
- <item>@string/description_target_camera</item>
- <item>@string/description_target_unlock</item>
- </array>
-
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 593316f..ea10f2f 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Leidžiama programai perkelti užduotis į priekinį planą ir foną. Programa gali tai daryti be jūsų įsikišimo."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"sustabdyti vykdomas programas"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Leidžiama programai pašalinti užduotis ir panaikinti jų programas. Kenkėjiškos programos gali trikdyti kitų programų veikimą."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"pradėti bet kokią veiklą"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Programai leidžiama pradėti bet kokią veiklą, nepaisant leidimo apsaugos ar eksportuotos būsenos."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nustatyti ekrano suderinamumo režimą"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Leidžiama savininką susaistyti su įvesties metodo aukščiausio lygio sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"susisaistyti su pasiekiamumo paslauga"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Savininkui leidžiama susisaistyti su aukščiausio lygio pasiekiamumo paslaugos sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"priskirti teksto paslaugą"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Leidžiama savininkui priskirti aukščiausio lygio teksto paslaugos (pvz., „SpellCheckerService“) sąsają. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"susaistyti su VPN paslauga"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Leidžiama programai rašyti į SD kortelę."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"keisti / ištr. vid. med. atm. tur."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Leidžiama programai keisti vidinės medijos saugyklos turinį."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"tvarkyti dokumentų saugyklą"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Leidžiama programai tvarkyti dokumentų saugyklą."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"pasiekti visų naud. išor. atm."</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Leidžiama programai pasiekti visų naudotojų išorinę atmintinę."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pasiekti talpyklos failų sistemą"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Leidžiama programai iškviesti numatytąją sudėtinio rodinio paslaugą, kad būtų kopijuojamas turinys. Neskirta naudoti įprastoms programoms."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Medijos išvesties nukreipimas"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Leidžiama programai nukreipti medijos išvestį į kitus išorinius įrenginius."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Pasiekti „KeyGuard“ saugyklą"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Programai leidžiama pasiekti „KeyGuard“ saugyklą."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Valdyti „KeyGuard“ rodymą ir slėpimą"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Programai leidžiama valdyti „KeyGuard“."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dukart palieskite, kad valdytumėte mastelio keitimą"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nepavyko pridėti."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Pradėti"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Jungiama..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Pasiekiama"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Nepasiekiama"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Naudojama"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Integruotas ekranas"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI ekranas"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Perdanga nr. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Netinkamas atrakinimo piešinys"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Netinkamas slaptažodis"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Netinkamas PIN kodas"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Bandyti dar kartą po <xliff:g id="NUMBER">%d</xliff:g> sek."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Bandyti dar kartą po <xliff:g id="NUMBER">%1$d</xliff:g> sek."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Nupieškite atrakinimo piešinį"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Įveskite SIM PIN kodą"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Įveskite PIN kodą"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Dabartinis naudotojas: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Savininkas"</string>
<string name="error_message_title" msgid="4510373083082500195">"Klaida"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Ši programa nepalaiko apribotų profilių paskyrų"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Ši programa nepalaiko apribotų profilių paskyrų"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nerasta programa šiam veiksmui apdoroti"</string>
<string name="revoke" msgid="5404479185228271586">"Anuliuoti"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 11e0116..3e3ab2c 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Ļauj lietotnei pārvietot uzdevumus priekšplānā un fonā. Lietotne var to izdarīt bez jūsu apstiprinājuma."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"apturēt izmantoto lietotņu darbību"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Ļauj lietotnei noņemt uzdevumus un pārtraukt to lietotņu darbību. Ļaunprātīgas lietotnes var traucēt citu lietotņu darbību."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"jebkuras darbības sākšana"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Ļauj lietotnei sākt jebkuru darbību neatkarīgi no atļaujas aizsardzības vai eksportētā stāvokļa."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Ekrāna saderības noteikšana"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Ļauj īpašniekam izveidot saiti ar ievades metodes augstākā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"saistīt ar pieejamības pakalpojumu"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Ļauj īpašniekam izveidot saiti ar pieejamības pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm šī atļauja nav nepieciešama."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"saistīt ar īsziņu pakalpojumu"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Ļauj īpašniekam veikt saistīšanu ar īsziņu pakalpojuma augstākā līmeņa saskarni (piem., SpellCheckerService). Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"saistīt ar VPN pakalpojumu"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ļauj lietotnei rakstīt SD kartē."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"pārv./dz.datu n.iekš.atm.sat."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ļauj lietotnei modificēt datu nesēja iekšējās atmiņas saturu."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"Dokumentu krātuves pārvaldība"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Ļauj lietotnei pārvaldīt dokumentu krātuvi."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"piekļ. visu liet. ārējai krāt."</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Ļauj lietotnei piekļūt visu lietotāju ārējai krātuvei."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"piekļūt kešatmiņas failu sistēmai"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Ļauj lietotnei izsaukt noklusējuma konteinera pakalpojumu, lai kopētu saturu. Atļauja neattiecas uz parastām lietotnēm."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Multivides datu izejas maršrutēšana"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Ļauj lietojumprogrammai maršrutēt multivides datu izeju uz citām ārējām ierīcēm."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Piekļūt krātuvei, kas aizsargāta ar atslēgu"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Ļauj lietojumprogrammai piekļūt krātuvei, kas aizsargāta ar atslēgu."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Pārvaldīt krātuves rādīšanu un paslēpšanu."</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Ļauj lietojumprogrammai pārvaldīt krātuvi."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Pieskarieties divreiz, lai kontrolētu tālummaiņu."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nevarēja pievienot logrīku."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Doties uz"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Notiek savienojuma izveide..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Pieejams"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Nav pieejams"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Tiek lietots"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Iebūvēts ekrāns"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI ekrāns"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Pārklājums Nr. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Nepareiza kombinācija"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Nepareiza parole"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Nepareizs PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER">%d</xliff:g> sekundēm."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER">%1$d</xliff:g> sekundēm."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Norādiet savu kombināciju"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ievadiet SIM kartes PIN"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Ievadiet PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Pašreizējais lietotājs: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Īpašnieks"</string>
<string name="error_message_title" msgid="4510373083082500195">"Kļūda"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Šajā lietojumprogrammā netiek atbalstīti ierobežotu profilu konti."</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Šajā lietotnē netiek atbalstīti ierobežotu profilu konti."</string>
<string name="app_not_found" msgid="3429141853498927379">"Netika atrasta neviena lietojumprogramma, kas var veikt šo darbību."</string>
<string name="revoke" msgid="5404479185228271586">"Atsaukt"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-mcc202/config.xml b/core/res/res/values-mcc202/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc202/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc204/config.xml b/core/res/res/values-mcc204/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc204/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc206/config.xml b/core/res/res/values-mcc206/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc206/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc208/config.xml b/core/res/res/values-mcc208/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc208/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc214/config.xml b/core/res/res/values-mcc214/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc214/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc216/config.xml b/core/res/res/values-mcc216/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc216/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc219/config.xml b/core/res/res/values-mcc219/config.xml
index 7ae82fa..80f4e58 100644
--- a/core/res/res/values-mcc219/config.xml
+++ b/core/res/res/values-mcc219/config.xml
@@ -29,4 +29,7 @@
<item>"96"</item>
</string-array>
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
</resources>
diff --git a/core/res/res/values-mcc222/config.xml b/core/res/res/values-mcc222/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc222/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc226/config.xml b/core/res/res/values-mcc226/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc226/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc228/config.xml b/core/res/res/values-mcc228/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc228/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc230/config.xml b/core/res/res/values-mcc230/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc230/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc231/config.xml b/core/res/res/values-mcc231/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc231/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc232/config.xml b/core/res/res/values-mcc232/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc232/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc234/config.xml b/core/res/res/values-mcc234/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc234/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc238/config.xml b/core/res/res/values-mcc238/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc238/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc240/config.xml b/core/res/res/values-mcc240/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc240/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc242/config.xml b/core/res/res/values-mcc242/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc242/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc244/config.xml b/core/res/res/values-mcc244/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc244/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc246/config.xml b/core/res/res/values-mcc246/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc246/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc247/config.xml b/core/res/res/values-mcc247/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc247/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc248/config.xml b/core/res/res/values-mcc248/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc248/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc260/config.xml b/core/res/res/values-mcc260/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc260/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc262/config.xml b/core/res/res/values-mcc262/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc262/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc268/config.xml b/core/res/res/values-mcc268/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc268/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc270/config.xml b/core/res/res/values-mcc270/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc270/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc272/config.xml b/core/res/res/values-mcc272/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc272/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc274/config.xml b/core/res/res/values-mcc274/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc274/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc278/config.xml b/core/res/res/values-mcc278/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc278/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc280/config.xml b/core/res/res/values-mcc280/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc280/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc284/config.xml b/core/res/res/values-mcc284/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc284/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc286/config.xml b/core/res/res/values-mcc286/config.xml
index d99d051..f73a523 100644
--- a/core/res/res/values-mcc286/config.xml
+++ b/core/res/res/values-mcc286/config.xml
@@ -61,4 +61,7 @@
to enable use of the new Release 9 tables for Indic languages. -->
<!-- <integer-array name="config_sms_enabled_locking_shift_tables"></integer-array> -->
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
</resources>
diff --git a/core/res/res/values-mcc293/config.xml b/core/res/res/values-mcc293/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc293/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc294/config.xml b/core/res/res/values-mcc294/config.xml
new file mode 100644
index 0000000..8d6d3b1
--- /dev/null
+++ b/core/res/res/values-mcc294/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc310/config.xml b/core/res/res/values-mcc310/config.xml
deleted file mode 100644
index df398f9..0000000
--- a/core/res/res/values-mcc310/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">false</bool>
-
-</resources>
diff --git a/core/res/res/values-mcc311/config.xml b/core/res/res/values-mcc311/config.xml
deleted file mode 100644
index df398f9..0000000
--- a/core/res/res/values-mcc311/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">false</bool>
-
-</resources>
diff --git a/core/res/res/values-mcc312/config.xml b/core/res/res/values-mcc312/config.xml
deleted file mode 100644
index df398f9..0000000
--- a/core/res/res/values-mcc312/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">false</bool>
-
-</resources>
diff --git a/core/res/res/values-mcc313/config.xml b/core/res/res/values-mcc313/config.xml
deleted file mode 100644
index df398f9..0000000
--- a/core/res/res/values-mcc313/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">false</bool>
-
-</resources>
diff --git a/core/res/res/values-mcc314/config.xml b/core/res/res/values-mcc314/config.xml
deleted file mode 100644
index df398f9..0000000
--- a/core/res/res/values-mcc314/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">false</bool>
-
-</resources>
diff --git a/core/res/res/values-mcc315/config.xml b/core/res/res/values-mcc315/config.xml
deleted file mode 100644
index df398f9..0000000
--- a/core/res/res/values-mcc315/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">false</bool>
-
-</resources>
diff --git a/core/res/res/values-mcc316/config.xml b/core/res/res/values-mcc316/config.xml
deleted file mode 100644
index df398f9..0000000
--- a/core/res/res/values-mcc316/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">false</bool>
-
-</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 4a0017f..9260061 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Membenarkan apl memindahkan tugasan ke latar depan dan latar belakang. Apl boleh melakukan ini tanpa input anda."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"hentikan apl yang sedang dijalankan"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Membenarkan apl untuk mengalih keluar tugasan dan melupuskan aplnya. Apl hasad boleh mengganggu tingkah laku apl lain."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"mulakan sebarang aktiviti"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Membenarkan apl untuk memulakan apa-apa aktiviti, tanpa mengira perlindungan kebenaran atau keadaan eksport."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"tetapkan keserasian skrin"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi kaedah input itu. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"terikat kepada perkhidmatan yang boleh diakses"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan yang boleh diakses. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"terikat kepada perkhidmatan teks"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Membenarkan pemegang mengikat kepada antara muka peringkat atasan perkhidmatan teks(mis. PerkhidmatanPenyemakEjaan). Tidak seharusnya diperlukan untuk apl biasa."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"terikat kepada perkhidmatan VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Membenarkan apl menulis ke kad SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ubh suai/pdm kdg strn mdia dlm"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Membenarkan apl mengubah suai kandungan storan media dalaman."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"urus storan dokumen"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Membenarkan apl mengurus storan dokumen."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"mengakses storan luaran untuk semua pengguna"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Membenarkan apl untuk mengakses storan luaran untuk semua pengguna."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"akses sistem fail cache"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Membenarkan apl untuk menggunakan perkhidmatan bekas lalai untuk menyalin kandungan. Bukan untuk digunakan oleh apl biasa."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Buat laluan output media"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Membenarkan apl untuk membuat laluan output media ke peranti luaran lain."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Akses storan selamat pengawal kekunci"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Membenarkan aplikasi mengakses storan selamat pengawal kekunci."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Kawal paparkan dan sembunyikan pengawal kekunci"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Membenarkan aplikasi untuk mengawal pengawal kekunci."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Sentuh dua kali untuk mendapatkan kawalan zum"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Tidak dapat menambahkan widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Pergi"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Menyambung..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tersedia"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Tidak tersedia"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Sedang digunakan"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Skrin Terbina Dalam"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Skrin HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Tindih #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Corak Salah"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Kata Laluan Salah"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN salah"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Cuba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> saat."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Cuba lagi dalam <xliff:g id="NUMBER">%1$d</xliff:g> saat."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Lukiskan corak anda"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Masukkan PIN SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Masukkan PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Pengguna semasa <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Pemilik"</string>
<string name="error_message_title" msgid="4510373083082500195">"Ralat"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Apl ini tidak menyokong akaun untuk profil yang disekat"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Apl ini tidak menyokong akaun untuk profil yang disekat"</string>
<string name="app_not_found" msgid="3429141853498927379">"Tidak menemui aplikasi untuk mengendalikan tindakan ini"</string>
<string name="revoke" msgid="5404479185228271586">"Batalkan"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 74d7702..b998377 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Lar appen flytte oppgaver til forgrunnen eller bakgrunnen. Appen kan gjøre dette uten instruksjoner fra deg."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"avslutte apper som kjører"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Lar appen fjerne oppgaver og avslutte apper. Ondsinnede apper kan forstyrre atferden til andre apper."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"igangsetting av aktiviteter"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Tillater at appen kan starte en aktivitet, uavhengig av tillatelsesbeskyttelse og eksportstatus."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"angi skjermkompatibilitet"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lar innehaveren binde det øverste nivået av grensesnittet til en inndatametode. Skal aldri være nødvendig for normale apper."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"binde seg til en tilgjengelighetstjeneste"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Gir innehaveren tillatelse til å bindes til det øverste nivået av grensesnittet for en tilgjengelighetstjeneste. Skal aldri være nødvendig for vanlige apper."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"binde til en teksttjeneste"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Lar innehaveren binde seg til øverste grensesnittnivå for en teksttjeneste (f.eks. SpellCheckerService). Skal aldri være nødvendig for vanlige apper."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"binde deg til en VPN-tjeneste"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lar appen skrive til SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"endre eller slette innhold på interne medier"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Lar appen endre innholdet i det interne lagringsmediet."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"administrere dokumentlagring"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Lar appen administrere dokumentlagring."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"åpne eksternlagring for alle brukere"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Tillater appen å åpne eksternlagring for alle brukere."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"tilgang til bufrede filer"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Lar appen påkalle standard meldingsbeholdertjeneste for kopiering av innhold. Ikke beregnet på vanlige apper."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Videresending av medieutdata"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Lar en app videresende medieutdata til andre eksterne enheter."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Bruk av sikker lagring via keyguard."</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Lar en app bruke sikker lagring via keyguard."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Kontrollér om tastelåsen er skjult eller vist"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Tillater at en app kontrollerer tastelåsen."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Trykk to ganger for zoomkontroll"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Kunne ikke legge til modulen."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Utfør"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Kobler til ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tilgjengelig"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Ikke tilgjengelig"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"I bruk"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Innebygd skjerm"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI-skjerm"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlegg #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Feil mønster"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Feil passord"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Feil PIN-kode"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Prøv på nytt om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Prøv på nytt om <xliff:g id="NUMBER">%1$d</xliff:g> sekunder."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Tegn mønsteret ditt"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Skriv inn PIN-koden for SIM-kortet"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Skriv inn PIN-koden"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Gjeldende bruker: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Eier"</string>
<string name="error_message_title" msgid="4510373083082500195">"Feil"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Denne appen støtter ikke kontoer for begrensede profiler"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Denne appen støtter ikke kontoer for begrensede profiler"</string>
<string name="app_not_found" msgid="3429141853498927379">"Finner ingen apper som kan utføre denne handlingen"</string>
<string name="revoke" msgid="5404479185228271586">"Opphev"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index bbdfed3..af72700 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Hiermee kan de app taken naar de voor- en achtergrond verplaatsen. De app kan dit doen zonder om uw bevestiging te vragen."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"actieve apps stoppen"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Hiermee kan de app taken verwijderen en apps sluiten. Schadelijke apps kunnen het gedrag van andere apps verstoren."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"elke activiteit starten"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Toestaan dat de app elke activiteit start, ongeacht rechtenbeveiliging of geëxporteerde status."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"schermcompatibiliteit instellen"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Hiermee kan de houder zich verbinden met de hoofdinterface van een invoermethode. Nooit vereist voor normale apps."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"koppelen aan een toegankelijkheidsservice"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Hiermee wordt de houder toegestaan verbinding te maken met de hoofdinterface van een toegankelijkheidsservice. Nooit vereist voor normale apps."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"koppelen aan een sms-service"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Hiermee kan de gebruiker koppelen met de hoofdinterface van een tekstservice (zoals SpellCheckerService). Dit is niet nodig voor normale apps."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"koppelen aan een VPN-service"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Hiermee kan de app schrijven naar de SD-kaart."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"inh. mediaopsl. wijz./verw."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Hiermee kan de app de inhoud van de interne mediaopslag aanpassen."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"documentopslag beheren"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Hiermee kan de app documentopslag beheren."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"toegang tot externe opslag van alle gebruikers"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Hiermee krijgt de app toegang tot externe opslag van alle gebruikers."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"het cachebestandssysteem openen"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Hiermee kan de app de standaard containerservice aanroepen om inhoud te kopiëren. Niet voor gebruik door normale apps."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Media-uitvoer aansturen"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Hiermee kan een app media-uitvoer naar andere externe apparaten doorsturen."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Toegang tot opslag met toetsbeveiliging"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Hiermee krijgt een app toegang tot opslag met toetsbeveiliging."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Weergeven en verbergen van toetsbeveiliging beheren"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Staat toe dat een app de toetsbeveiliging beheert."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Raak twee keer aan voor zoomregeling"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Kan widget niet toevoegen."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Ga"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Verbinden..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Beschikbaar"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Niet beschikbaar"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"In gebruik"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ingebouwd scherm"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI-scherm"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Onjuist patroon"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Onjuist wachtwoord"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Onjuiste pincode"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Probeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Probeer het over <xliff:g id="NUMBER">%1$d</xliff:g> seconden opnieuw."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Teken uw patroon"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Geef de pincode van de simkaart op"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Pincode opgeven"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Eigenaar"</string>
<string name="error_message_title" msgid="4510373083082500195">"Fout"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Deze app biedt geen ondersteuning voor accounts voor beperkte profielen"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Deze app biedt geen ondersteuning voor accounts voor beperkte profielen"</string>
<string name="app_not_found" msgid="3429141853498927379">"Er is geen app gevonden om deze actie uit te voeren"</string>
<string name="revoke" msgid="5404479185228271586">"Intrekken"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index e4e82f9..588df6f 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Pozwala aplikacji na przenoszenie zadań między tłem a pierwszym planem. Aplikacja może to robić bez Twojego udziału."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"zatrzymywanie uruchomionych aplikacji"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Umożliwia aplikacji usuwanie zadań i kończenie powiązanych z nimi aplikacji. Złośliwe aplikacje mogą zakłócić działanie innych aplikacji."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"rozpoczynanie dowolnej czynności"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Zezwala aplikacji na rozpoczynanie dowolnej czynności niezależnie od ochrony uprawnień lub stanu eksportu."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ustaw zgodność ekranu"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Pozwala na powiązanie wybranego sposobu wprowadzania tekstu z interfejsem najwyższego poziomu. To uprawnienie nie powinno być nigdy wymagane przez zwykłe aplikacje."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"tworzenie powiązania z usługą ułatwień dostępu"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Zezwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi ułatwień dostępu. Nieprzeznaczone dla zwykłych aplikacji."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"tworzenie powiązania z usługą tekstową"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Pozwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi tekstowej (np. SpellCheckerService). Nie powinno być nigdy potrzebne w przypadku zwykłych aplikacji."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"tworzenie powiązania z usługą VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pozwala aplikacji na zapis na karcie SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modyfikowanie/usuwanie zawartości pamięci wew."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Pozwala aplikacji na modyfikowanie zawartości pamięci wewnętrznej."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"zarządzaj przechowywaniem dokumentów"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Pozwala aplikacji zarządzać przechowywaniem dokumentów."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"dostęp do zewnętrznej pamięci wszystkich"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Pozwala aplikacji na dostęp do zewnętrznej pamięci masowej dla wszystkich użytkowników."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"dostęp do systemu plików pamięci podręcznej"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Pozwala aplikacji na wywoływanie domyślnej usługi kontenera w celu skopiowania zawartości. Nieprzeznaczone dla zwykłych aplikacji."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Kierowanie wyjścia multimediów"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Pozwala aplikacji na kierowanie wyjściowych danych multimedialnych do innych urządzeń zewnętrznych."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Dostęp do bezpiecznego magazynu kluczy"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Zezwala aplikacji na dostęp do bezpiecznego magazynu kluczy."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Kontroluj wyświetlanie i ukrywanie zabezpieczenia kluczami"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Umożliwia aplikacji kontrolowanie zabezpieczenia kluczami."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dotknij dwukrotnie, aby sterować powiększeniem."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nie można dodać widżetu."</string>
<string name="ime_action_go" msgid="8320845651737369027">"OK"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Łączę..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Dostępne"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Niedostępne"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"W użyciu"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Wbudowany ekran"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Ekran HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Nakładka nr <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Nieprawidłowy wzór"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Nieprawidłowe hasło"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Nieprawidłowy PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Spróbuj ponownie za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Spróbuj ponownie za <xliff:g id="NUMBER">%1$d</xliff:g> s."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Narysuj wzór"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Podaj PIN karty SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Podaj PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Bieżący użytkownik: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Właściciel"</string>
<string name="error_message_title" msgid="4510373083082500195">"Błąd"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Ta aplikacja nie obsługuje kont w przypadku profili z ograniczeniami"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Ta aplikacja nie obsługuje kont w profilach z ograniczeniami"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nie znaleziono aplikacji do obsługi tej akcji"</string>
<string name="revoke" msgid="5404479185228271586">"Cofnij"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-port/alias.xml b/core/res/res/values-port/alias.xml
deleted file mode 100644
index bf3eecb..0000000
--- a/core/res/res/values-port/alias.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/colors.xml
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- Alias used to reference one of two possible layouts in keyguard. -->
- <item type="layout" name="keyguard_eca">@android:layout/keyguard_emergency_carrier_area</item>
-</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index e5c48bb..4d22c52 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite que a aplicação mova tarefas para primeiro e segundo plano. A aplicação poderá fazê-lo sem qualquer entrada do utilizador."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"parar aplicações em execução"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite que a aplicação remova tarefas e elimine as respetivas aplicações. As aplicações maliciosas podem perturbar o comportamento de outras aplicações."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"iniciar qualquer atividade"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Permite que a aplicação inicie qualquer atividade, independentemente da proteção de permissão ou do estado exportado."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"definir compatibilidade de ecrã"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite ao titular vincular-se à interface de nível superior de um método de entrada. Nunca deve ser necessário para aplicações normais."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"vincular a um serviço de acessibilidade"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite que o titular vincule a interface de nível superior de um serviço de acessibilidade. Nunca deverá ser necessário para aplicações normais."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"vincular a um serviço de texto"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite ao titular ligar-se à interface de nível superior de um serviço de texto (por exemplo SpellCheckerService). Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"vincular a um serviço VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que a aplicação escreva no cartão SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modif./elim. armaz. interno"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que a aplicação modifique o conteúdo de armazenamento de suportes internos."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"gerir o armaz. de documentos"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Permite que a aplicação faça a gestão do armazenamento de documentos."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"aceder ao armazenamento externo de todos os utilizadores"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite que a aplicação aceda ao armazenamento externo para todos os utilizadores."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"aceder ao sistema de ficheiros da cache"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Permite à aplicação invocar o serviço de contentor predefinido para copiar conteúdo. Não se destina a ser utilizado por aplicações normais."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Encaminhar saída de som multimédia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite que a aplicação encaminhe a saída de som multimédia para outros dispositivos externos."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Aceder ao armazenamento seguro de proteção de teclado"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Permite a uma aplicação aceder ao armazenamento seguro de proteção de teclado."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Controlar apresentação e ocultação de proteção de teclado"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite que uma aplicação controle a proteção de teclado."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toque duas vezes para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Não foi possível adicionar widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"A ligar..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponível"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Não disponível"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Em utilização"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ecrã Integrado"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Ecrã HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Sobreposição #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Sequência Incorreta"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Palavra-passe Incorreta"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN Incorreto"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Tente novamente dentro de <xliff:g id="NUMBER">%1$d</xliff:g> segundos."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe a sua sequência"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduzir PIN do cartão SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Introduzir PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"<xliff:g id="NAME">%1$s</xliff:g> do utilizador atual."</string>
<string name="owner_name" msgid="2716755460376028154">"Proprietário"</string>
<string name="error_message_title" msgid="4510373083082500195">"Erro"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Esta aplicação não suporta contas de perfis restritos"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Esta aplicação não suporta contas de perfis restritos"</string>
<string name="app_not_found" msgid="3429141853498927379">"Não foram encontradas aplicações para executar esta ação"</string>
<string name="revoke" msgid="5404479185228271586">"Revogar"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index f0f3d60..d9a997e 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite que o aplicativo mova tarefas para o primeiro plano e o plano de fundo, sem sua intervenção."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"parar os aplicativos em execução"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite que um aplicativo remova tarefas e elimine seus aplicativos. Aplicativos maliciosos podem interferir no comportamento de outros aplicativos."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"iniciar qualquer atividade"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Permite que o aplicativo inicie qualquer atividade, independentemente da permissão ou do estado exportado."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"definir a compatibilidade de tela"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite que o proprietário utilize a interface de nível superior de um método de entrada. Nunca deve ser necessário para aplicativos normais."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"usar um serviço de acessibilidade"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite que o proprietário use a interface de nível superior de um serviço de acessibilidade. Nunca deve ser necessário para aplicativos comuns."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"sujeitar-se a um serviço de texto"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite que o proprietário utilize interface de nível superior de um serviço de texto (por exemplo, SpellCheckerService). Nunca deve ser necessário para aplicativos normais."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"se ligam a um serviço de VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que o aplicativo grave em seu cartão SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar/excluir conteúdos de armazenamento de mídia internos"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que o aplicativo modifique o conteúdo da mídia de armazenamento interno."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"gerenciar armaz. de documentos"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Permitir que o aplicativo gerencie o armazenamento de documentos."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"acessar arm. ext. dos usuários"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite que o aplicativo acesse o armazenamento externo para todos os usuários."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"acessar o sistema de arquivos de cache"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Permite que o aplicativo invoque serviços contêiner padrão para copiar conteúdo. Não deve ser usado em aplicativos normais."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Rotear saída de mídia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite que um aplicativo faça o roteamento de saída de mídia para outros dispositivos externos."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Acessar o armazenamento seguro do bloqueio de teclado"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Permite que o aplicativo acesse o armazenamento seguro do bloqueio de teclado."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Controlar a exibição e ocultação do bloqueio de tela"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite que o aplicativo controle o bloqueio de teclado."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toque duas vezes para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Não foi possível adicionar widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Conectando..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponível"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Não disponível"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Em uso"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Tela integrada"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Tela HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Sobreposição nº <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Padrão incorreto"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Senha incorreta"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorreto"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Tente novamente em <xliff:g id="NUMBER">%1$d</xliff:g> segundos."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe seu padrão"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Digite o PIN do cartão SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Digite o PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Usuário atual <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Proprietário"</string>
<string name="error_message_title" msgid="4510373083082500195">"Erro"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Este aplicativo não suporta contas para perfis restritos"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Este aplicativo não suporta contas para perfis restritos"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nenhum aplicativo encontrado para executar a ação"</string>
<string name="revoke" msgid="5404479185228271586">"Revogar"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index cad9d1a..c5b4fd1 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -414,6 +414,10 @@
<skip />
<!-- no translation found for permdesc_removeTasks (1394714352062635493) -->
<skip />
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<!-- no translation found for permlab_startAnyActivity (2918768238045206456) -->
<skip />
<!-- no translation found for permdesc_startAnyActivity (997823695343584001) -->
@@ -553,6 +557,14 @@
<skip />
<!-- no translation found for permdesc_bindAccessibilityService (7034615928609331368) -->
<skip />
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<!-- no translation found for permlab_bindTextService (7358378401915287938) -->
<skip />
<!-- no translation found for permdesc_bindTextService (8151968910973998670) -->
@@ -1025,6 +1037,10 @@
<skip />
<!-- no translation found for permdesc_mediaStorageWrite (8189160597698529185) -->
<skip />
+ <!-- no translation found for permlab_manageDocs (5778318598448849829) -->
+ <skip />
+ <!-- no translation found for permdesc_manageDocs (8704323176914121484) -->
+ <skip />
<!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
<skip />
<!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
@@ -1982,6 +1998,14 @@
<skip />
<!-- no translation found for permdesc_route_media_output (4932818749547244346) -->
<skip />
+ <!-- no translation found for permlab_access_keyguard_secure_storage (7565552237977815047) -->
+ <skip />
+ <!-- no translation found for permdesc_access_keyguard_secure_storage (5866245484303285762) -->
+ <skip />
+ <!-- no translation found for permlab_control_keyguard (172195184207828387) -->
+ <skip />
+ <!-- no translation found for permdesc_control_keyguard (3043732290518629061) -->
+ <skip />
<!-- no translation found for tutorial_double_tap_to_zoom_message_short (4070433208160063538) -->
<skip />
<!-- no translation found for gadget_host_error_inflating (4882004314906466162) -->
@@ -2319,6 +2343,8 @@
<skip />
<!-- no translation found for media_route_status_not_available (6739899962681886401) -->
<skip />
+ <!-- no translation found for media_route_status_in_use (4533786031090198063) -->
+ <skip />
<!-- no translation found for display_manager_built_in_display_name (2583134294292563941) -->
<skip />
<!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
@@ -2424,10 +2450,100 @@
<skip />
<!-- no translation found for error_message_title (4510373083082500195) -->
<skip />
- <!-- no translation found for app_no_restricted_accounts (4011285085817350390) -->
+ <!-- no translation found for app_no_restricted_accounts (5739463249673727736) -->
<skip />
<!-- no translation found for app_not_found (3429141853498927379) -->
<skip />
<!-- no translation found for revoke (5404479185228271586) -->
<skip />
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 14f9644..a058815 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite aplicaţiei să mute activităţile în prim-plan şi în fundal. Aplicaţia poate face acest lucru fără aportul dvs."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"oprire aplicaţii care rulează"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite aplicaţiei să elimine sarcini şi să închidă aplicaţiile corespunzătoare acestora. Aplicaţiile rău intenţionate pot perturba comportamentul altor aplicaţii."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"începe orice activitate"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Permite aplicaţiei să înceapă orice activitate, indiferent de protecţia permisiunii şi de starea de export."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"setaţi compatibilitatea ecranului"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unei metode de introducere. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"conectare la un serviciu de accesibilitate"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unui serviciu de accesibilitate. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"conectare la un serviciu text"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite proprietarului să se conecteze la o interfaţă de nivel superior a unui serviciu text (de ex., SpellCheckerService). Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"conectare la un serviciu VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite aplicaţiei să scrie pe cardul SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modif./şterg. conţinutul media stocat intern"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite aplicaţiei să modifice conţinutul stocării media interne."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"gestionare stocare documente"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Permite aplicației să gestioneze stocarea documentelor."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"acces. stoc. ext. pt. toţi utilizat."</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite aplicaţiei să acceseze stocarea externă pentru toţi utilizatorii."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accesare sistem de fişiere cache"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Permite aplicaţiei să invoce serviciul containerului prestabilit pentru a copia conţinutul. Nu se utilizează de aplicaţiile obişnuite."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Direcţionează rezultatele media"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite unei aplicaţii să direcţioneze rezultate media către alte dispozitive externe."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Accesează stocarea securizată când tastatura este blocată"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Permite unei aplicații să acceseze stocarea securizată când tastatura este blocată."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Stabilește afișarea și ascunderea blocării tastaturii"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite unei aplicații să controleze blocarea tastaturii."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Atingeţi de două ori pentru a mări/micşora"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nu s-a putut adăuga widgetul."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Accesaţi"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Se conectează..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponibilă"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Indisponibilă"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"În uz"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ecran încorporat"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Ecran HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Suprapunerea <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Model greşit"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Parolă greşită"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Cod PIN greşit"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Încercaţi din nou peste <xliff:g id="NUMBER">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Încercaţi din nou peste <xliff:g id="NUMBER">%1$d</xliff:g> (de) secunde."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Desenaţi modelul"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduceţi codul PIN al cardului SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Introduceţi codul PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Utilizator curent: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Proprietar"</string>
<string name="error_message_title" msgid="4510373083082500195">"Eroare"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Această aplicație nu acceptă conturi pentru profilurile cu permisiuni limitate"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Această aplicație nu acceptă conturi pentru profilurile cu permisiuni limitate"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nicio aplicație pentru gestionarea acestei acțiuni"</string>
<string name="revoke" msgid="5404479185228271586">"Revocați"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 81366f3..b742263 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Приложение сможет переключать активный и фоновый режимы выполнения задач без вашего ведома."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"Остановка работающих приложений"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Приложение сможет удалять задачи и собственные программы. Вредоносное ПО при этом сможет нарушать работу других приложений."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"Совершать любые действия"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Приложение сможет совершать любые действия независимо от наличия разрешений и состояния."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Установка режима совместимости"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Приложение сможет подключаться к базовому интерфейсу системы ввода. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"Подключение к службе специальных возможностей"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Приложение сможет подключаться к базовому интерфейсу службы специальных возможностей. Это разрешение не используется обычными приложениями."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"Подключение к службе текстовых сообщений"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Позволяет подключаться к базовому интерфейсу службы текстовых сообщений (например, SpellCheckerService). Это разрешение не используется обычными приложениями."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"Подключение к VPN-службе"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Приложение сможет записывать данные на SD-карту."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Доступ к данным мультимедиа"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Приложение сможет изменять контент внутреннего хранилища мультимедиа."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"управлять хранением документов"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Приложение сможет управлять хранением документов."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"Доступ к внешним накопителям из всех аккаунтов"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Приложение сможет обращаться к внешним накопителям из всех аккаунтов."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"Доступ к файловой системе кэша"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Приложение сможет вызывать службу контейнеров по умолчанию для копирования данных. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Перенаправление мультимедийных данных"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Приложение сможет направлять поток мультимедиа на другие внешние устройства."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Доступ к хранилищу ключей"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Приложение сможет получить доступ к хранилищу ключей."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Управлять отображением хранилища ключей"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Приложение сможет управлять хранилищем ключей."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Нажмите дважды для изменения масштаба"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не удалось добавить виджет."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Выбрать"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Подключение..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Доступен"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Недоступные"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Используется"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Встроенный экран"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Экран HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Наложение № <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Неправильный графический ключ"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Неправильный пароль"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Неправильный PIN-код"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Повторите попытку через <xliff:g id="NUMBER">%d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Повторите попытку через <xliff:g id="NUMBER">%1$d</xliff:g> сек."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Введите графический ключ"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Введите PIN-код SIM-карты"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Введите PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Выбран аккаунт пользователя <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Владелец"</string>
<string name="error_message_title" msgid="4510373083082500195">"Ошибка"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Это приложение не поддерживается в аккаунтах для профилей с ограниченным доступом"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Это приложение не поддерживается в аккаунтах для профилей с ограниченным доступом"</string>
<string name="app_not_found" msgid="3429141853498927379">"Невозможно обработать это действие"</string>
<string name="revoke" msgid="5404479185228271586">"Отменить"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index b7dcd07..f6a62cd 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Umožňuje aplikácii presunúť úlohy do popredia alebo do pozadia. Aplikácia tak môže urobiť bez vášho zásahu."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"zastaviť spustené aplikácie"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Umožňuje aplikácii odstrániť úlohy a ukončiť ich aplikácie. Škodlivé aplikácie môžu narušiť správanie iných aplikácií."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"spustiť ľubovoľnú aktivitu"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Umožňuje aplikácii spustiť ľubovoľnú aktivitu bez ohľadu na ochranu povolení alebo exportovaný stav."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nastaviť kompatibilitu obrazovky"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania metódy vstupu. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"viazať na službu zjednodušeného ovládania"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania služby zjednodušeného ovládania. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"väzba na textovú službu"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania textovej služby (napr. SpellCheckerService). Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"Zaviazať k službe VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikácii zápis na kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"úprava alebo odstránenie obsahu interného ukladacieho priestoru média"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Umožňuje aplikácii zmeniť obsah interného ukladacieho priestoru média."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"správa ukladacieho priestoru dokumentov"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Umožňuje aplikácii spravovať ukladací priestor dokumentov."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"pristupovať k externému ukladaciemu priestoru pre všetkých používateľov"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Umožňuje aplikácii pristupovať k externému ukladaciemu priestoru pre všetkých používateľov."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pristupovať do súborového systému vyrovnávacej pamäte"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Umožňuje volať predvolenú službu kontajnera na skopírovanie obsahu. Bežné aplikácie toto nastavenie nepoužívajú."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Smerovanie výstupu médií"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Umožňuje aplikácii smerovať výstup médií do ďalších externých zariadení."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Prístup k ukladaciemu priestoru zabezpečenému technológiou keyguard"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Umožňuje aplikácii získať prístup k ukladaciemu priestoru zabezpečenému technológiou keyguard."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Ovládanie zobrazenia alebo skrytia technológie keyguard"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Umožňuje aplikácii ovládať technológiu keyguard."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Ovládacie prvky lupy zobrazíte dvojitým dotknutím"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Miniaplikáciu sa nepodarilo pridať."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Hľadať"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Prebieha pripájanie…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"K dispozícii"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Nie je k dispozícii"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Používa sa"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Vstavaná obrazovka"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Obrazovka HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Prekrytie č. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Nesprávny vzor"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Nesprávne heslo"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Nesprávny kód PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Skúste to znova o <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Skúste to znova o <xliff:g id="NUMBER">%1$d</xliff:g> s."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Nakreslite svoj vzor"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Zadajte kód PIN karty SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Zadajte kód PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Aktuálny používateľ je <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Vlastník"</string>
<string name="error_message_title" msgid="4510373083082500195">"Chyba"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Táto aplikácia nepodporuje účty pre profily s obmedzením"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Táto aplikácia nepodporuje účty pre profily s obmedzením"</string>
<string name="app_not_found" msgid="3429141853498927379">"Aplikácia potrebná na spracovanie tejto akcie sa nenašla"</string>
<string name="revoke" msgid="5404479185228271586">"Odvolať"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 11dc13d..ffbae22 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Aplikaciji omogoča premikanje opravil v ospredje in ozadje. Aplikacija lahko to naredi brez vašega nadzora."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"ustavitev programov, ki se izvajajo"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Programu omogoča odstranjevanje opravil in zapiranje njihovih programov. Zlonamerni programi lahko motijo delovanje drugih programov."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"zagon poljubne dejavnosti"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Omogoča aplikaciji zagon poljubne dejavnosti, ne glede na zaščito dovoljenj ali izvoženo stanje."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nastavitev združljivosti zaslona"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lastniku omogoča, da se poveže z vmesnikom načina vnosa najvišje ravni. Tega nikoli ni treba uporabiti za navadne programe."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"povezovanje s storitvijo za ljudi s posebnimi potrebami"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lastniku omogoča povezovanje z vmesnikom najvišje ravni storitve za ljudi s posebnimi potrebami. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"poveži z besedilno storitvijo"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Dovoljuje, da se lastnik poveže z vmesnikom besedilne storitve najvišje ravni (npr. SpellCheckerService). Tega nikoli ni treba uporabiti za navadne programe."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"povezava s storitvijo navideznega zasebnega omrežja"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Programu omogoča pisanje na kartico SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"spreminjanje/brisanje vsebine notranje shrambe nosilca podatkov"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Programu omogoča spreminjanje vsebine notranje shrambe nosilca podatkov."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"upravljanje shranjevanja dokumentov"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Aplikaciji omogoči upravljanje shranjevanja dokumentov."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"dostop do zunanje naprave za shranjevanje za vse uporabnike"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Aplikaciji omogoča dostop do zunanje naprave za shranjevanje za vse uporabnike."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"dostop do datotečnega sistema predpomnilnika"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Programu omogoča pozivanje privzete storitve vsebnika, da kopira vsebino. Ni za uporabo z navadnimi programi."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Preusmeritev predstavnosti"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Aplikaciji omogoča preusmerjanje predstavnosti v druge zunanje naprave."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Dostop do varne shrambe Keyguard."</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Aplikaciji omogoča dostop do varne shrambe Keyguard."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Nadzira prikaz in skrivanje zaklepanja tipkovnice"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Aplikaciji omogoča nadzor zaklepanja tipkovnice."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dvakrat se dotaknite za nadzor povečave/pomanjšave"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Pripomočka ni bilo mogoče dodati."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Pojdi"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Vzpostavljanje povezave ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Na voljo"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Ni na voljo"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"V uporabi"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Vgrajen zaslon"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Zaslon HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Prekrivanje #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Napačen vzorec"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Napačno geslo"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Napačen PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Čez <xliff:g id="NUMBER">%d</xliff:g> sekund poskusite znova."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Čez <xliff:g id="NUMBER">%1$d</xliff:g> sekund poskusite znova."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Narišite vzorec"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Vnesite PIN za kartico SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Vnesite PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Trenutni uporabnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Lastnik"</string>
<string name="error_message_title" msgid="4510373083082500195">"Napaka"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Ta aplikacija ne podpira računov za profile z omejitvami"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Ta aplikacija ne podpira računov za profile z omejitvami"</string>
<string name="app_not_found" msgid="3429141853498927379">"Najdena ni bila nobena aplikacija za izvedbo tega dejanja"</string>
<string name="revoke" msgid="5404479185228271586">"Prekliči"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index d0ebddd..7465798 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Дозвољава апликацији да премешта задатке у први план и у позадину. Апликација може да ради ово без вашег уноса."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"заустављање покренутих апликација"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Дозвољава апликацији да уклања задатке и уништава њихове апликације. Злонамерне апликације могу да поремете понашање других апликација."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"покретање било које активности"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Омогућава да апликација покрене било коју активност, без обзира на заштиту дозволе или стање извоза."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"подешавање компатибилности екрана"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Омогућава да се власник обавеже на интерфејс методе уноса највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"повезивање са услугом приступачности"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Дозвољава власнику да се повеже са интерфејсом услуге приступачности највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"обавезивање на текстуалну услугу"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Омогућава власнику да се обавеже на интерфејс текстуалне услуге највишег нивоа (нпр. SpellCheckerService). Обичне апликације никада не би требало да је користе."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"везивање за VPN услугу"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозвољава апликацији да уписује податке на SD картицу."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"измена/брисање интерне меморије медија"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дозвољава апликацији да мења садржај интерне меморије медијума."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"управ. складиштењем докумената"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Дозвољава апликацији да управља складиштењем докумената."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"приступ спољној меморији свих корисника"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Дозвољава апликацији да приступа спољној меморији за све кориснике."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"приступ систему датотека кеша"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Дозвољава апликацији да од подразумеване услуге контејнера захтева да копира садржај. Не користе је уобичајене апликације."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Усмеравање излаза медија"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Дозвољава апликацији да усмерава излаз медија на друге спољне уређаје."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Приступај безбедној меморији заштићеној шифром"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Дозвољава апликацији да приступа безбедној меморији заштићеној шифром."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Контролиши приказивање и скривање заштите шифром"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Дозвољава апликацији да контролише заштиту шифром."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Додирните двапут да бисте контролисали зум"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Није могуће додати виџет."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Иди"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Повезивање..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Доступна"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Нису доступне"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"У употреби"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Уграђени екран"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI екран"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Постављени елемент бр. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Погрешан шаблон"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Погрешна лозинка"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Погрешан PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Покушајте поново за <xliff:g id="NUMBER">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Покушајте поново за <xliff:g id="NUMBER">%1$d</xliff:g> секунде(и)."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Нацртајте шаблон"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Унесите PIN SIM картице"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Унесите PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Актуелни корисник <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Власник"</string>
<string name="error_message_title" msgid="4510373083082500195">"Грешка"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Ова апликација не подржава налоге за ограничене профиле"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Ова апликација не подржава налоге за ограничене профиле"</string>
<string name="app_not_found" msgid="3429141853498927379">"Није пронађена ниједна апликација која би могла да обави ову радњу"</string>
<string name="revoke" msgid="5404479185228271586">"Опозови"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 80ab2ae..5e39d66 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Tillåter att appen flyttar aktiviteter till förgrunden eller bakgrunden. Appen kan göra detta utan åtgärd från dig."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"avsluta appar som körs"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Tillåter att appen tar bort uppgifter och avslutar appar. Skadliga appar kan störa funktionen i andra appar."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"starta alla aktiviteter"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Appen tillåts starta alla aktiviteter oavsett behörighetsskydd eller exportstatus."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ange skärmkompatibilitet"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en inmatningsmetod. Ska inte behövas för vanliga appar."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"bind till en tillgänglighetstjänst"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en tillgänglighetstjänst. Ska inte behövas för vanliga appar."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"bind till en texttjänst"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Tillåter innehavaren att binda mot den högsta gränssnittsnivån i en texttjänst (t.ex. SpellCheckerService). Bör aldrig behövas för normala appar."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"bind till en VPN-tjänst"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillåter att appen skriver till SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ändra/ta bort innehåll"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Tillåter att appen ändrar innehållet på den interna lagringsenheten."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"hantera dokumentlagring"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Tillåter att appen hanterar dokumentlagring."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"åtkomst till lagringsutrymme"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Tillåter att appen får åtkomst till en extern lagringsenhet för alla användare."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"åtkomst till cachefilsystemet"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Tillåter att appen kopierar innehåll genom att standardbehållartjänsten startas. Används inte av vanliga appar."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Omdirigera medieuppspelning"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Tillåter att appen omdirigerar medieuppspelningar till andra externa enheter."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Åtkomst till säkert keyguard-lagringsutrymme"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Tillåter att en app får åtkomst till säkert keyguard-lagringsutrymme."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Kontrollera hur knapplåset visas och döljs"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Tillåter att en app kontrollerar knapplåsfunktionen."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Tryck två gånger för zoomkontroll"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Det gick inte att lägga till widgeten."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Kör"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Ansluter ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tillgängliga"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Ej tillgängligt"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Används"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Inbyggd skärm"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI-skärm"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Överlagring #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Fel grafiskt lösenord"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Fel lösenord"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Fel PIN-kod"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Försök igen om <xliff:g id="NUMBER">%1$d</xliff:g> sekunder."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Rita ditt grafiska lösenord"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ange PIN-kod för SIM-kortet"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Ange PIN-kod"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Nuvarande användare: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Ägare"</string>
<string name="error_message_title" msgid="4510373083082500195">"Fel"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Den här appen stöder inte konton för begränsade profiler"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Den här appen stöder inte konton för begränsade profiler"</string>
<string name="app_not_found" msgid="3429141853498927379">"Ingen app som kan hantera åtgärden hittades"</string>
<string name="revoke" msgid="5404479185228271586">"Återkalla"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index b066d3a..29ef9b3 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Inaruhusu programu kusongesha kazi hadi kwenye mandhari-mbele na mandari nyuma. Programu inaweza kufanya haya bila ya maingizo yako."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"Komesha programu zinazoendeshwa"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Huruhusu programu kuondoa majukumu na kuua programu zao. Programu hasidi zinaweza kutatiza tabia ya programu zingine."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"anzisha shughuli yoyote"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Huruhusu programu kuanzisha shughuli yoyote, pasi kujali ulinzi wa idhini au hali ya nje."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"weka utangamano wa skrini"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Inaruhusu mmiliki kushurutisha kwenye kusano ya kiwango cha juu ya mbinu ya ingizo. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"funga kwa huduma ya ufikiaji"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Inamuruhusu mmiliki kufunga kipengee kinachojitokeza katika nyanja mbalimbali za kiwango cha juu cha huduma ya afikiaji. Hapaswi kuhitajika kwa programu za kawaida."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"Imefungwa kwa huduma ya maandishi"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Inaruhusu kishikiliaji kushurutisha kusano ya kiwango cha juu ya huduma ya matini(k.m.SpellCheckerService). Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"funga kwa huduma ya VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Inaruhusu programu kuandikia kadi ya SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"badilisha/futa maudhui ya hifadhi ya media ya ndani."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Inaruhusu programu kurekebisha maudhui ya hifadhi ya ndani vyombo vya habari."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"dhibiti hifadhi ya hati"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Huruhusu programu kudhibiti hifadhi ya hati."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"Fikia hifadhi ya nje ya watumiaji wote"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Inaruhusu programu kufikia hifadhi ya nje kwa watumiaji wote."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"fikia faili za mfumo za kache"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Inaruhusu programu kuomba huduma ya chombo chaguo-msingi kunakili maudhui. Si ya matumizi na programu za kawiada."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Fuatalia utoaji wa habari"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Inaruhusu programu kufuatilia utoaji wa habari kwa vifaa vingine vya nje."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Kufikia hifadhi salama ya ufunguo wa ulinzi"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Inaruhusu programu kufikia hifadhi salama ya ufunguo wa ulinzi."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Dhibiti uonyeshaji na ufichaji wa kilinda-funguo"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Huruhusu programu kudhibiti kilinda-funguo."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Gusa mara mbili kwa udhibiti cha kuza"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Haikuweza kuongeza wijeti."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Nenda"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Inaunganisha..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Inapatikana"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Haipatikani"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Inatumika"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Skrini Iliyojengwa ndani"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Skrini ya HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Uwekeleaji #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Mchoro Usio sahihi"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Nenosiri Lisilo sahihi"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN isiyo sahihi"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Jaribu tena baada ya sekunde <xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Jaribu tena baada ya sekunde <xliff:g id="NUMBER">%1$d</xliff:g>."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Chora ruwaza yako"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ingiza PIN ya SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Ingiza PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Mtumiaji wa sasa <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Mmiliki"</string>
<string name="error_message_title" msgid="4510373083082500195">"Hitilafu"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Programu hii haiwezi kutumiwa na akaunti za wasifu zilizowekewa vikwazo"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Programu hii haiwezi kutumiwa na akaunti za wasifu zilizowekewa vikwazo"</string>
<string name="app_not_found" msgid="3429141853498927379">"Hakuna programu iliyopatikana ili kushughulikia kitendo hiki"</string>
<string name="revoke" msgid="5404479185228271586">"Batilisha"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-sw600dp-land/arrays.xml b/core/res/res/values-sw600dp-land/arrays.xml
index 5550216..5602a1c 100644
--- a/core/res/res/values-sw600dp-land/arrays.xml
+++ b/core/res/res/values-sw600dp-land/arrays.xml
@@ -19,54 +19,4 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Resources for GlowPadView in LockScreen -->
- <array name="lockscreen_targets_when_silent">
- <item>@drawable/ic_lockscreen_unlock</item>
- <item>@null</item>
- <item>@drawable/ic_lockscreen_soundon</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_target_descriptions_when_silent">
- <item>@string/description_target_unlock</item>
- <item>@null</item>
- <item>@string/description_target_soundon</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_direction_descriptions">
- <item>@string/description_direction_right</item>
- <item>@null</item>
- <item>@string/description_direction_left</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_targets_when_soundon">
- <item>@drawable/ic_lockscreen_unlock</item>
- <item>@null</item>
- <item>@drawable/ic_lockscreen_silent</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_target_descriptions_when_soundon">
- <item>@string/description_target_unlock</item>
- <item>@null</item>
- <item>@string/description_target_silent</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_targets_with_camera">
- <item>@drawable/ic_lockscreen_unlock</item>
- <item>@drawable/ic_action_assist_generic</item>
- <item>@drawable/ic_lockscreen_camera</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_target_descriptions_with_camera">
- <item>@string/description_target_unlock</item>
- <item>@string/description_target_search</item>
- <item>@string/description_target_camera</item>
- <item>@null</item>
- </array>
-
</resources>
diff --git a/core/res/res/values-sw600dp/alias.xml b/core/res/res/values-sw600dp/alias.xml
deleted file mode 100644
index bf3eecb..0000000
--- a/core/res/res/values-sw600dp/alias.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/colors.xml
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- Alias used to reference one of two possible layouts in keyguard. -->
- <item type="layout" name="keyguard_eca">@android:layout/keyguard_emergency_carrier_area</item>
-</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 3825b40..9087783 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"อนุญาตให้แอปพลิเคชันย้ายงานไปยังส่วนหน้าและพื้นหลัง แอปพลิเคชันอาจดำเนินการโดยไม่รอคำสั่งจากคุณ"</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"หยุดแอปพลิเคชันที่ทำงานอยู่"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"อนุญาตให้แอปพลิเคชันลบงานออกและยุติแอปพลิเคชันต่างๆ ของงานนั้น แอปพลิเคชันที่เป็นอันตรายอาจทำให้แอปพลิเคชันอื่นๆ ทำงานได้ไม่ถูกต้อง"</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"เริ่มต้นกิจกรรมใดๆ"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"อนุญาตให้แอปพลิเคชันเริ่มกิจกรรม ไม่ว่าการอนุญาตหรือสถานะที่ส่งออกจะเป็นอย่างไรก็ตาม"</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ตั้งค่าความเข้ากันได้ของหน้าจอ"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"อนุญาตให้ผู้ใช้เชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของวิธีการป้อนข้อมูล ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"เชื่อมโยงกับบริการการเข้าถึง"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"อนุญาตให้เจ้าของเชื่อมโยงกับส่วนติดต่อระดับบนสุดของบริการการเข้าถึง ซึ่งแอปพลิเคชันทั่วไปไม่จำเป็นต้องใช้"</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"เชื่อมโยงกับบริการข้อความ"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"อนุญาตให้ผู้ใช้เชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของบริการข้อความ (เช่น บริการเครื่องตรวจตัวสะกด) ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"เชื่อมโยงกับบริการ VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"อนุญาตให้แอปพลิเคชันเขียนลงบนการ์ด SD"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"แก้/ลบเนื้อหาข้อมูลสื่อภายใน"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"อนุญาตให้แอปพลิเคชันแก้ไขเนื้อหาของที่เก็บข้อมูลสื่อภายใน"</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"จัดการที่เก็บเอกสาร"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"อนุญาตให้แอปจัดการที่เก็บเอกสาร"</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"เข้าถึงที่จัดเก็บภายนอกของทุกคน"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"อนุญาตให้แอปพลิเคชันเข้าถึงที่จัดเก็บข้อมูลภายนอกสำหรับผู้ใช้ทั้งหมด"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"เข้าถึงระบบไฟล์แคช"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"อนุญาตให้แอปพลิเคชันเรียกใช้บริการที่เก็บค่าเริ่มต้นเพื่อคัดลอกเนื้อหา ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"กำหนดเส้นทางเอาต์พุตของสื่อ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"อนุญาตให้แอปพลิเคชันกำหนดเส้นทางเอาต์พุตของสื่อไปยังอุปกรณ์ภายนอกอื่นๆ"</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"เข้าถึงพื้นที่จัดเก็บที่รักษาความปลอดภัยด้วยคีย์การ์ด"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"อนุญาตให้แอปพลิเคชันเข้าถึงพื้นที่จัดเก็บที่รักษาความปลอดภัยด้วยคีย์การ์ด"</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"ควบคุมการแสดงผลและการซ่อนตัวล็อกปุ่มกด"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"อนุญาตให้แอปพลิเคชันควบคุมตัวล็อกปุ่มกด"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"แตะสองครั้งเพื่อควบคุมการซูม"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ไม่สามารถเพิ่มวิดเจ็ต"</string>
<string name="ime_action_go" msgid="8320845651737369027">"ไป"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"กำลังเชื่อมต่อ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"พร้อมใช้งาน"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"ไม่พร้อมใช้งาน"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"กำลังใช้งาน"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"หน้าจอในตัว"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"หน้าจอ HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"การวางซ้อน #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"รูปแบบไม่ถูกต้อง"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"รหัสผ่านไม่ถูกต้อง"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN ไม่ถูกต้อง"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"ลองอีกครั้งในอีก <xliff:g id="NUMBER">%d</xliff:g> วินาที"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"ลองอีกครั้งในอีก <xliff:g id="NUMBER">%1$d</xliff:g> วินาที"</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"วาดรูปแบบของคุณ"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"ป้อน PIN ของซิม"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"ป้อน PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"ผู้ใช้ปัจจุบัน <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="owner_name" msgid="2716755460376028154">"เจ้าของ"</string>
<string name="error_message_title" msgid="4510373083082500195">"ข้อผิดพลาด"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"แอปพลิเคชันนี้ไม่สนับสนุนบัญชีที่มีโปรไฟล์ที่ถูกจำกัด"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"แอปนี้ไม่สนับสนุนบัญชีที่โปรไฟล์ถูกจำกัด"</string>
<string name="app_not_found" msgid="3429141853498927379">"ไม่พบแอปพลิเคชันสำหรับการทำงานนี้"</string>
<string name="revoke" msgid="5404479185228271586">"เพิกถอน"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 401fa42..d30fb77 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Pinapayagan ang app na maglipat ng mga gawain sa foreground at background. Maaari itong gawin ng app nang wala ng iyong input."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"ihinto ang pagpapatakbo ng apps"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Pinapayagan ang app na mag-alis ng mga gawain at i-off ang apps nito. Maaaring maantala ng nakakahamak na apps ang pagkilos ng iba pang apps."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"magsimula ng anumang aktibidad"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Binibigyang-daan ang app na magsimula ng anumang aktibidad, may proteksyon man ng pahintulot o na-export na katayuan."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"itakda ang pagkakatugma ng screen"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Pinapayagan ang may-hawak na sumailalim sa nangungunang interface ng pamamaraan ng pag-input. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"sumailalim sa isang serbisyo sa accessibility"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Binibigyang-daan ang may-ari na sumailalim sa nasa nangungunang antas na interface ng isang serbisyo sa accessibility. Hindi dapat kailanman kailanganin para sa normal na apps."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"sumailalim sa serbisyo ng teksto"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Pinapayagan ang may-hawak na sumailalim sa nangungunang antas na interface (hal. SpellCheckerService). Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"sumailalim sa isang serbisyo ng VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pinapayagan ang app na magsulat sa SD card."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"baguhin/tanggalin ang mga nilalaman ng panloob na imbakan ng media"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Pinapayagan ang app na baguhin ang mga nilalaman ng panloob na media storage."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"pamahalaan document storage"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Pinapayagan ang app na pamahalaan ang storage ng dokumento."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"i-access panlabas na storage ng user"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Pinapayagan ang app na mag-access ng panlabas na storage para sa lahat ng user."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"i-access ang cache filesystem"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Pinapayagan ang app na i-apela ang default na serbisyo ng container upang kopyahin ang nilalaman. Hindi para sa paggamit ng normal na apps."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"I-route ang output ng media"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Pinapayagan ang application na mag-route ng output ng media sa iba pang mga panlabas na device."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"I-access ang secure na storage ng keyguard"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Nagbibigay-daan sa isang application na i-access ang secure na storage ng keyguard."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Kontrolin ang pagpapakita at pagtago sa keyguard"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Pinapayagan ang isang application na kontrolin ang keyguard."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Pindutin nang dalawang beses para sa pagkontrol ng zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Hindi maidagdag ang widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Pumunta"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Kumukonekta..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Available"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Hindi available"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Ginagamit"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Built-in na Screen"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI Screen"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Maling Pattern"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Maling Password"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Maling PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Subukang muli sa loob ng <xliff:g id="NUMBER">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Subukang muli sa loob ng <xliff:g id="NUMBER">%1$d</xliff:g> (na) segundo."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Iguhit ang iyong pattern"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ilagay ang SIM PIN"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Ilagay ang PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Kasalukuyang user <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"May-ari"</string>
<string name="error_message_title" msgid="4510373083082500195">"Error"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Hindi sinusuportahan ng application na ito ang mga account para sa mga pinaghihigpitang profile"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Hindi sinusuportahan ng app na ito ang mga account para sa mga pinaghihigpitang profile"</string>
<string name="app_not_found" msgid="3429141853498927379">"Walang nakitang application na mangangasiwa sa pagkilos na ito"</string>
<string name="revoke" msgid="5404479185228271586">"Bawiin"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a6d7b2a..30691d9 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Uygulamaya, görevleri ön plana ve arka plana taşıma izni verir. Uygulama bunu sizden bir giriş olmadan yapabilir."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"çalışan uygulamaları durdur"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Uygulamaya, görevleri kaldırma ve bunlara ait uygulamaları kapatma izni verir. Kötü amaçlı uygulamalar diğer uygulamaların çalışmasını bozabilir."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"herhangi bir etkinlik başlat"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Uygulamaya, izin koruma veya dışa aktarma durumu ne olursa olsun bir etkinlik başlatma izni verir."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ekran uyumluluğunu ayarla"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Cihazın sahibine, bir giriş yönteminin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"erişilebilirlik hizmetine bağlan"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"İzin sahibine bir erişilebilirlik hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"kısa mesaj hizmetine bağla"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Cihazın sahibine, bir metin hizmetinin (ör. SpellCheckerService) en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerekmez."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"VPN hizmetine bağlan"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Uygulamaya, SD karta yazma izni verir."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"dahili medya depolama birimi içeriğini değiştir/sil"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Uygulamaya, dahili medya depolama içeriğini değiştirme izni verir."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"doküman deposunu yönet"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Uygulamaya, doküman deposunu yönetme izni verir."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"tüm kullanıcılar için harici depolama eriş"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Uygulamaya tüm kullanıcılar için harici depolamaya erişim izni verir."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"önbellek dosya sistemine eriş"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Uygulamaya, içerik kopyalamak için varsayılan kapsayıcı hizmetini çağırma izni verir. Normal uygulamaların kullanımına yönelik değildir."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Medya çıktısını yönlendir"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Uygulamaya medya çıktısını başka harici cihazlara yönlendirme izni verir."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Tuş kilitli güvenli depolamaya erişim"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Bir uygulamanın tuş kilitli güvenli depolamaya erişimine izin verir."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Tuş koruyucuyu görüntülemeyi ve gizlemeyi kontrol et"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Bir uygulamaya tuş koruyucuyu denetleme izni verir."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Yakınlaştırma denetimi için iki kez dokunun"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget eklenemedi."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Git"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Bağlanılıyor..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Kullanılabilir"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Yok"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Kullanımda"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Yerleşik Ekran"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI Ekran"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Yer Paylaşımı No. <xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Yanlış Desen"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Yanlış Şifre"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Yanlış PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g> saniye içinde yeniden deneyin."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%1$d</xliff:g> saniye içinde yeniden deneyin."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Deseninizi çizin"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM PIN kodunu girin"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"PIN\'i girin"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Geçerli kullanıcı: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Sahibi"</string>
<string name="error_message_title" msgid="4510373083082500195">"Hata"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Bu uygulama kısıtlanmış profillerin hesaplarını desteklemez"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Bu uygulama, kısıtlanmış profillerin hesaplarını desteklemez"</string>
<string name="app_not_found" msgid="3429141853498927379">"Bu eylemi gerçekleştirecek bir uygulama bulunamadı"</string>
<string name="revoke" msgid="5404479185228271586">"İptal et"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index d2798ad..db27c85 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Дозволяє програмі переміщувати завдання в активні чи фонові вікна. Програма може робити це без вашого відома."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"зупиняти запущені програми"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Дозволяє програмі видаляти завдання та примусово припиняти роботу відповідних програм. Шкідливі програми можуть переривати роботу інших програм."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"розпочинати будь-які дії"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Дозволяє програмі розпочинати будь-які дії, незалежно від захищеного дозволу або стану експорту."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"установити сумісність екрана"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Дозволяє власнику прив’язуватися до інтерфейсу верхнього рівня методу введення. Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"прив’язуватися до служби доступності"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня служби доступності. Ніколи не застосовується для звичайних програм."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"прив’язати до текстової служби"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня текстової служби (напр. SpellCheckerService). Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"прив’язуватися до служби VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозволяє програмі записувати на карту SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"змінювати/видаляти вміст внутр. сховища даних"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дозволяє програмі змінювати вміст внутрішнього сховища даних."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"керувати зберіганням"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Дозволяє програмі керувати зберіганням документів."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"доступ до зовн. пам’яті всіх корист."</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Дозволяє програмі отримувати доступ до зовнішньої пам’яті всіх користувачів."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"отр. дост. до файл. сист. кешу"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Дозволяє програмі викликати службу контейнерів за умовчанням для копіювання вмісту. Не для використання звичайними програмами."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Скеровувати вивід медіа-даних"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Дозволяє програмі скеровувати вивід медіа-даних на інші зовнішні пристрої."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Отримувати доступ до безпечного сховища через клавіатуру"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Дозволяє програмі отримувати доступ до безпечного сховища через клавіатуру."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Керувати відображенням і хованням клавіатури"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Дозволяє програмі керувати клавіатурою."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Двічі торкніться, щоб керувати масштабом"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не вдалося додати віджет."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Йти"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"З’єднання..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Доступно"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Недоступно"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Використовується"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Вбудований екран"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Екран HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Накладання №<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Неправильний ключ"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Неправильний пароль"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Неправильний PIN-код"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Повторіть спробу через <xliff:g id="NUMBER">%d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Повторіть спробу через <xliff:g id="NUMBER">%1$d</xliff:g> с."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Намалюйте ключ"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Введіть PIN-код SIM-карти"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Введіть PIN-код"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Поточний користувач: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Власник"</string>
<string name="error_message_title" msgid="4510373083082500195">"Помилка"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Ця програма не підтримує облікові записи для обмежених профілів"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Ця програма не підтримує облікові записи для обмежених профілів"</string>
<string name="app_not_found" msgid="3429141853498927379">"Не знайдено програму для обробки цієї дії"</string>
<string name="revoke" msgid="5404479185228271586">"Анулювати"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index d4d17a6..3002bcd 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Cho phép ứng dụng di chuyển công việc sang nền trước và nền sau. Ứng dụng có thể thực hiện việc này mà không cần bạn nhập."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"dừng các ứng dụng đang chạy"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Cho phép ứng dụng xóa công việc và loại bỏ các ứng dụng của chúng. Ứng dụng độc hại có thể làm gián đoạn hoạt động của các ứng dụng khác."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"bắt đầu mọi hoạt động"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Cho phép ứng dụng bắt đầu mọi hoạt động, bất kể tình trạng bảo vệ quyền hay trạng thái xuất."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"đặt độ tương thích màn hình"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của phương thức nhập. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"liên kết với dịch vụ truy cập"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ truy cập. Không cần thiết cho các ứng dụng thông thường."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"liên kết với dịch vụ văn bản"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ văn bản (ví dụ: SpellCheckerService). Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"liên kết với dịch vụ VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Cho phép ứng dụng ghi vào thẻ SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"sửa đổi/xóa nội dung trên bộ nhớ phương tiện cục bộ"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Cho phép ứng dụng sửa đổi nội dung của bộ lưu trữ phương tiện nội bộ."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"quản lý bộ nhớ tài liệu"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Cho phép ứng dụng quản lý bộ nhớ tài liệu."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"truy cập bộ nhớ ngoài của tất cả người dùng"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Cho phép ứng dụng truy cập bộ nhớ ngoài của tất cả người dùng."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"truy cập hệ thống tệp bộ nhớ cache"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Cho phép ứng dụng gọi ra dịch vụ bộ chứa mặc định để sao chép nội dung. Không dành cho ứng dụng thông thường."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Định tuyến thiết bị ra phương tiện"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Cho phép ứng dụng định tuyến thiết bị ra phương tiện đến các thiết bị bên ngoài khác."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Truy cập bộ nhớ an toàn khóa bàn phím"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Cho phép ứng dụng truy cập bộ nhớ an toàn khóa"</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Kiểm soát việc hiển thị và ẩn tính năng bảo vệ phím"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Cho phép ứng dụng kiểm soát tính năng bảo vệ phím."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Chạm hai lần để kiểm soát thu phóng"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Không thể thêm tiện ích."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Đến"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Đang kết nối..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Khả dụng"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Không khả dụng"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Đang được sử dụng"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Màn hình tích hợp"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Màn hình HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Lớp phủ #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Hình sai"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Mật khẩu sai"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN sai"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Hãy thử lại sau <xliff:g id="NUMBER">%d</xliff:g> giây."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Hãy thử lại sau <xliff:g id="NUMBER">%1$d</xliff:g> giây."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Vẽ hình của bạn"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Nhập PIN của SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Nhập PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Người dùng hiện tại <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Chủ sở hữu"</string>
<string name="error_message_title" msgid="4510373083082500195">"Lỗi"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Ứng dụng này không hỗ trợ tài khoản cho các tiểu sử bị hạn chế"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Ứng dụng này không hỗ trợ tài khoản đối với các tiểu sử bị hạn chế"</string>
<string name="app_not_found" msgid="3429141853498927379">"Không tìm thấy ứng dụng nào để xử lý tác vụ này"</string>
<string name="revoke" msgid="5404479185228271586">"Thu hồi"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 2c23f8f..52526de 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"允许该应用将任务移动到前台和后台。该应用可能不经您的命令自行执行此操作。"</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"停止正在运行的应用"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"允许该应用删除任务并终止这些任务的应用。恶意应用可以籍此影响其他应用的行为。"</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"启动任何活动"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"允许该应用启动任何活动(不考虑权限保护或导出状态)。"</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"设置屏幕兼容性"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"允许用户绑定至输入法的顶级接口。普通应用绝不需要此权限。"</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"绑定至辅助服务"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"允许应用绑定至辅助服务的顶级接口。普通应用绝不需要此权限。"</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"绑定至文字服务"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"允许用户绑定至文字服务(如 SpellCheckerService)的顶级接口。普通应用绝不需要此权限。"</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"绑定到 VPN 服务"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允许应用写入 SD 卡。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"修改/删除内部媒体存储设备的内容"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允许应用修改内部媒体存储设备的内容。"</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"管理文档存储空间"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"允许应用管理文档存储空间。"</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"访问所有用户的外部存储设备"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"允许应用访问所有用户的外部存储设备。"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"访问缓存文件系统"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"允许应用调用默认的容器服务,以便复制内容。普通应用不应使用此权限。"</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"更改媒体输出线路"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"允许该应用将媒体输出线路更改到其他外部设备。"</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"访问密钥保护安全存储空间"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"允许应用访问密钥保护安全存储空间。"</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"控制是显示还是隐藏锁屏"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"允许应用控制锁屏。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"触摸两次可进行缩放控制"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"无法添加小部件。"</string>
<string name="ime_action_go" msgid="8320845651737369027">"开始"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"正在连接..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"可连接"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"无法连接"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"正在使用"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"内置屏幕"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI 屏幕"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"叠加视图 #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"图案错误"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"密码错误"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN 有误"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"请在 <xliff:g id="NUMBER">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"请在 <xliff:g id="NUMBER">%1$d</xliff:g> 秒后重试。"</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"绘制您的图案"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"输入 SIM PIN"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"输入 PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"当前用户是<xliff:g id="NAME">%1$s</xliff:g>。"</string>
<string name="owner_name" msgid="2716755460376028154">"机主"</string>
<string name="error_message_title" msgid="4510373083082500195">"错误"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"此应用不支持受限个人资料的帐户"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"此应用不支持受限个人资料的帐户"</string>
<string name="app_not_found" msgid="3429141853498927379">"找不到可处理此操作的应用"</string>
<string name="revoke" msgid="5404479185228271586">"撤消"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6bf2bf9..922a4f5 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"允許應用程式將工作移至前景或背景。應用程式可以自行處理,不待您操作。"</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"停止執行中的應用程式"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"允許應用程式移除工作並終止執行工作的應用程式。請注意,惡意應用程式可能利用此功能干擾其他應用程式的行為。"</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"啟動任何活動"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"不論權限保護或匯出狀態為何,一律允許應用程式啟動任何活動。"</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"設定螢幕相容性"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"允許應用程式繫結至輸入法的頂層介面 (一般應用程式不需使用)。"</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"繫結至協助工具服務"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"允許應用程式繫結至協助工具服務的頂層介面 (一般應用程式不需使用)。"</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"繫結至文字服務"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"允許應用程式繫結至文字服務 (例如 SpellCheckerService) 的頂層介面 (一般應用程式不需使用)。"</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"繫結至 VPN 服務"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允許應用程式寫入 SD 卡。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"修改/刪除內部媒體儲存裝置內容"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允許應用程式修改內部媒體儲存空間的內容。"</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"管理文件儲存空間"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"允許應用程式管理文件儲存空間。"</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"存取外部儲存空間 (所有使用者)"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"允許應用程式存取外部儲存空間 (所有使用者)。"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"存取快取檔案系統"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"允許觸發預設容器服務,以便複製內容 (不建議一般應用程式使用)。"</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"轉送媒體輸出"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"允許應用程式將媒體輸出轉送至其他外部裝置。"</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"存取 Keyguard 安全儲存空間"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"允許應用程式存取 Keyguard 安全儲存空間。"</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"控制鍵盤鎖的顯示和隱藏"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"允許應用程式控制鍵盤鎖。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"輕觸兩下即可控制縮放"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"無法新增小工具。"</string>
<string name="ime_action_go" msgid="8320845651737369027">"開始"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"連線中…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"可以使用"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"無法使用"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"使用中"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"內建畫面"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI 螢幕"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"第 <xliff:g id="ID">%1$d</xliff:g> 個重疊效果"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"圖形錯誤"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"密碼錯誤"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN 錯誤"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"請在 <xliff:g id="NUMBER">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"請在 <xliff:g id="NUMBER">%1$d</xliff:g> 秒後再試一次。"</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"畫出圖形"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"輸入 SIM PIN"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"輸入 PIN"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"目前的使用者是 <xliff:g id="NAME">%1$s</xliff:g>。"</string>
<string name="owner_name" msgid="2716755460376028154">"擁有者"</string>
<string name="error_message_title" msgid="4510373083082500195">"錯誤"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"這個應用程式不支援設有限制的個人資料所屬帳戶"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"這個應用程式不支援設有限制的個人資料所屬帳戶"</string>
<string name="app_not_found" msgid="3429141853498927379">"找不到支援此操作的應用程式"</string>
<string name="revoke" msgid="5404479185228271586">"撤銷"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 28cefb8..3eaf19f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -279,6 +279,10 @@
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Ivumela uhlelo lokusebenza ukugudluza imisebenzi ngaphambili nangasemuva. Uhlelo lokusebenza lungenza lokhu ngaphandle kwakho."</string>
<string name="permlab_removeTasks" msgid="6821513401870377403">"misa izinsiza ezisebenzayo"</string>
<string name="permdesc_removeTasks" msgid="1394714352062635493">"Vumela ukuthi insiza isuse okumele kwenziwe ibulale nezinsiza zakho. Izinsiza eziwubungozi zingaphazamisa ukusebenza kwezinye izinsiza."</string>
+ <!-- no translation found for permlab_manageActivityStacks (7391191384027303065) -->
+ <skip />
+ <!-- no translation found for permdesc_manageActivityStacks (1615881933034084440) -->
+ <skip />
<string name="permlab_startAnyActivity" msgid="2918768238045206456">"qala noma imuphi umsebenzi"</string>
<string name="permdesc_startAnyActivity" msgid="997823695343584001">"Ivumela uhlelo lokusebenza ukuqala umsebenzi, ngaphandle kokuvukeleka kwemvume noma isimo sokukhishiwe."</string>
<string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"setha ukuhambelana kwesikrini"</string>
@@ -356,6 +360,14 @@
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Ivumela isimeli ukuhlanganisa uxhumano nomsebenzisi wezinga eliphezulu lendlela yokufaka. Ayisoze yadingeka kwizinhlelo ezivamile."</string>
<string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"hlanganisa kusevisi yokufinyeleleka"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Ivumela isibambi ukuhlanganisa uxhumo nomsebenzisi kwezinga eliphezulu lesevisi yesinqunjwana. Akusoze kwadingekela izinhlelo zokusebenza ezivamile."</string>
+ <!-- no translation found for permlab_bindPrintService (8462815179572748761) -->
+ <skip />
+ <!-- no translation found for permdesc_bindPrintService (7960067623209111135) -->
+ <skip />
+ <!-- no translation found for permlab_accessAllPrintJobs (1120792468465711159) -->
+ <skip />
+ <!-- no translation found for permdesc_accessAllPrintJobs (2978185311041864762) -->
+ <skip />
<string name="permlab_bindTextService" msgid="7358378401915287938">"bophezela kunsizakalo yombhalo"</string>
<string name="permdesc_bindTextService" msgid="8151968910973998670">"Ivumela umbambi ukuhlanganisa uxhumano nomsebenzisi kwezinga eliphezulu lwesixhumi esibonakalayo sensizakalo yombhalo(isb. InsizakaloYokuhlolaUkubhala). Akusoze kwadingeka kwezinhlelo zokusebenza ezivamile."</string>
<string name="permlab_bindVpnService" msgid="4708596021161473255">"hlanganisa kwinsizakalo ye-VPN"</string>
@@ -613,6 +625,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ivumela insiza ukuthi ibhalele ekhadini le-SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"guqula/susa okuqukethwe kwisitoreji semidiya yangaphakathi"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ivumela insiza ukuthi iguqule okuqukethwe kokulondoloza imidiya yangaphakathi."</string>
+ <string name="permlab_manageDocs" product="default" msgid="5778318598448849829">"phatha isitoreji sedokhumenti"</string>
+ <string name="permdesc_manageDocs" product="default" msgid="8704323176914121484">"Ivumela uhlelo lokusebenza ukuthi luphathe isitoreji sedokhumenti."</string>
<string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"ukufinyelela isilondolozi sangaphandle sabo bonke abasebenzisi"</string>
<string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Vumela uhlelo lokusebenza ukufinyelela isilondolozi sangaphandle kubo bonke abasebenzisi."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"finyelela kunqolobane yesistimu yefayela"</string>
@@ -1256,6 +1270,10 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"Ivumela insiza ukuthi inqabe okutholakala kukhona ukukopisha okuqukethwe. Akusetshenziswa izinsiza ezijwayelekile."</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"Yenza umzila wemidiya wokukhiphayo"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Ivumela uhlelo lokusebenza ukwenza umzila wokukhiphayo wemidiya kuya kumadivayisi angaphandle."</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"Finyelela kusitoreji esiqashwa ngesikhiya esiphephile"</string>
+ <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"Ivumela uhlelo lokusebenza ukuthi lufinyelele kusitoreji esiqashwa ngesikhiya esiphephile."</string>
+ <string name="permlab_control_keyguard" msgid="172195184207828387">"Lawula ukubonisa nokufihla ukhiye wokuqapha"</string>
+ <string name="permdesc_control_keyguard" msgid="3043732290518629061">"Ivumela uhlelo lokusebenza ukuthi lulawule ukhiye wokuqapha."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Thinta kabili ukulawula ukusondeza"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Yehlulekile ukwengeza i-widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Iya"</string>
@@ -1441,6 +1459,7 @@
<string name="media_route_status_connecting" msgid="6422571716007825440">"Iyaxhuma..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Kuyatholakala"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Ayitholakali"</string>
+ <string name="media_route_status_in_use" msgid="4533786031090198063">"Kuyasebenza"</string>
<string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Okwakhelwe ngaphakathi kwesikrini"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Isikrini se-HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Isendlalelo #<xliff:g id="ID">%1$d</xliff:g>"</string>
@@ -1453,7 +1472,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Iphatheni engalungile"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Iphasiwedi engalungile"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Iphinikhodi engalungile"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Zama futhi emasekhondini angu-<xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Zama futhi emasekhondini angu-<xliff:g id="NUMBER">%1$d</xliff:g>."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Dweba iphethini"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Faka iphinikhodi ye-SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Faka iphinikhodi"</string>
@@ -1493,7 +1512,97 @@
<string name="user_switched" msgid="3768006783166984410">"Umsebenzisi wamanje <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Umnikazi"</string>
<string name="error_message_title" msgid="4510373083082500195">"Iphutha"</string>
- <string name="app_no_restricted_accounts" msgid="4011285085817350390">"Lolu hlelo lokusebenza alusekeli ama-akhawunti wamaphrofayela akhawulelwe"</string>
+ <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Lolu hlelo lokusebenza alusekeli ama-akhawunti wamaphrofayela akhawulelwe"</string>
<string name="app_not_found" msgid="3429141853498927379">"Alukho uhlelo lokusebenza olutholakele lokuphatha lesi senzo"</string>
<string name="revoke" msgid="5404479185228271586">"Chitha"</string>
+ <!-- no translation found for mediaSize_iso_a0 (7875427489420821793) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a1 (3760734499050875356) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a2 (5973266378020144382) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a3 (1373407105687300884) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a4 (6689772807982597254) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a5 (5353549652015741040) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a6 (8585038048674911907) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a7 (6641836716963839119) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a8 (7571139437465693355) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a9 (1378455891957115079) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_a10 (2480747457429475344) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b0 (3965935097661108039) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b1 (2505753285010115437) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b2 (8763874709859458453) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b3 (4210506688191764076) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b4 (5749404165888526034) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b5 (7640627414621904733) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b6 (7342988864712748544) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b7 (5069844065235382429) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b8 (7316818922278779774) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b9 (5414727094026532341) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_b10 (5251253731832048185) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c0 (4003138342671964217) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c1 (1935188063393553008) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c2 (3197307969712069904) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c3 (4335826087321913508) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c4 (3745639598281015005) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c5 (8269457765822791013) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c6 (566666105260346930) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c7 (8678413180782608498) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c8 (8392376206627041730) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c9 (9191613372324845405) -->
+ <skip />
+ <!-- no translation found for mediaSize_iso_c10 (7327709699184920822) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_letter (4191805615829472953) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_gvrnmt_letter (7853382192649405507) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_legal (6697982988283823150) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_junior_legal (3727743969902758948) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_ledger (281871464896601236) -->
+ <skip />
+ <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
+ <skip />
+ <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
+ <skip />
+ <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
+ <skip />
+ <!-- no translation found for restr_pin_confirm_pin (2891475237150965991) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_doesnt_match (2224214190906994548) -->
+ <skip />
+ <!-- no translation found for restr_pin_error_too_short (8173982756265777792) -->
+ <skip />
+ <!-- no translation found for restr_pin_countdown:one (4835639969503729874) -->
+ <!-- no translation found for restr_pin_countdown:other (8030607343223287654) -->
</resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 146607e..ef30b98 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -346,75 +346,4 @@
<item>中文 (繁體)</item>
</string-array>
- <!-- Resources for GlowPadView in LockScreen -->
- <array name="lockscreen_targets_when_silent">
- <item>@drawable/ic_lockscreen_unlock</item>
- <item>@drawable/ic_action_assist_generic</item>
- <item>@drawable/ic_lockscreen_soundon</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_target_descriptions_when_silent">
- <item>@string/description_target_unlock</item>
- <item>@string/description_target_search</item>
- <item>@string/description_target_soundon</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_direction_descriptions">
- <item>@string/description_direction_right</item>
- <item>@string/description_direction_up</item>
- <item>@string/description_direction_left</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_targets_when_soundon">
- <item>@drawable/ic_lockscreen_unlock</item>
- <item>@drawable/ic_action_assist_generic</item>
- <item>@drawable/ic_lockscreen_silent</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_target_descriptions_when_soundon">
- <item>@string/description_target_unlock</item>
- <item>@string/description_target_search</item>
- <item>@string/description_target_silent</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_targets_with_camera">
- <item>@drawable/ic_lockscreen_unlock</item>
- <item>@drawable/ic_action_assist_generic</item>
- <item>@drawable/ic_lockscreen_camera</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_target_descriptions_with_camera">
- <item>@string/description_target_unlock</item>
- <item>@string/description_target_search</item>
- <item>@string/description_target_camera</item>
- <item>@null</item>
- </array>
-
- <array name="lockscreen_targets_unlock_only">
- <item>@*android:drawable/ic_lockscreen_unlock</item>
- </array>
-
- <array name="lockscreen_target_descriptions_unlock_only">
- <item>@*android:string/description_target_unlock</item>
- </array>
-
- <!-- list of 3- or 4-letter mnemonics for a 10-key numeric keypad -->
- <string-array translatable="false" name="lockscreen_num_pad_klondike">
- <item></item><!-- 0 -->
- <item></item><!-- 1 -->
- <item>ABC</item><!-- 2 -->
- <item>DEF</item><!-- 3 -->
- <item>GHI</item><!-- 4 -->
- <item>JKL</item><!-- 5 -->
- <item>MNO</item><!-- 6 -->
- <item>PQRS</item><!-- 7 -->
- <item>TUV</item><!-- 8 -->
- <item>WXYZ</item><!-- 9 -->
- </string-array>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4dcbaec..99f7e7a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2562,6 +2562,21 @@
<attr name="description" />
</declare-styleable>
+ <!-- Use <code>print-service</code> as the root tag of the XML resource that
+ describes an {@link android.printservice.PrintService} service, which is
+ referenced from its {@link android.printservice.PrintService#SERVICE_META_DATA}
+ meta-data entry. -->
+ <declare-styleable name="PrintService">
+ <!-- Fully qualified class name of an activity that allows the user to modify
+ the settings for this service. -->
+ <attr name="settingsActivity" />
+ <!-- Fully qualified class name of an activity that allows the user to manually
+ add printers to this print service. -->
+ <attr name="addPrintersActivity" format="string"/>
+ <!-- The vendor name if this print service is vendor specific. -->
+ <attr name="vendor" format="string"/>
+ </declare-styleable>
+
<declare-styleable name="ActionMenuItemView">
<attr name="minWidth" />
</declare-styleable>
@@ -4057,10 +4072,6 @@
<attr name="drawable" />
</declare-styleable>
- <declare-styleable name="MipmapDrawableItem">
- <attr name="drawable" />
- </declare-styleable>
-
<!-- Drawable used to rotate another drawable. -->
<declare-styleable name="RotateDrawable">
<attr name="visible" />
@@ -4408,6 +4419,28 @@
</declare-styleable>
<!-- ========================== -->
+ <!-- State class attributes -->
+ <!-- ========================== -->
+ <eat-comment />
+
+ <declare-styleable name="Scene">
+ <attr name="layout" />
+ </declare-styleable>
+
+ <declare-styleable name="Transition">
+ <attr name="duration" />
+ <attr name="startOffset" />
+ <attr name="interpolator" />
+ <attr name="targetID" format="reference" />
+ </declare-styleable>
+
+ <declare-styleable name="TransitionManager">
+ <attr name="transition" format="reference" />
+ <attr name="fromScene" format="reference" />
+ <attr name="toScene" format="reference" />
+ </declare-styleable>
+
+ <!-- ========================== -->
<!-- ValueAnimator class attributes -->
<!-- ========================== -->
<eat-comment />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e7f6c53..728b1b0 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1624,6 +1624,27 @@
case-sensitive, unlike the formal RFC. As a result,
schemes here should always use lower case letters.</em></p> -->
<attr name="scheme" format="string" />
+ <!-- Specify a URI scheme specific part that must exactly match, as per
+ {@link android.content.IntentFilter#addDataSchemeSpecificPart
+ IntentFilter.addDataSchemeSpecificPart()} with
+ {@link android.os.PatternMatcher#PATTERN_LITERAL}. -->
+ <attr name="ssp" format="string" />
+ <!-- Specify a URI scheme specific part that must be a prefix to match, as per
+ {@link android.content.IntentFilter#addDataSchemeSpecificPart
+ IntentFilter.addDataSchemeSpecificPart()} with
+ {@link android.os.PatternMatcher#PATTERN_PREFIX}. -->
+ <attr name="sspPrefix" format="string" />
+ <!-- Specify a URI scheme specific part that matches a simple pattern, as per
+ {@link android.content.IntentFilter#addDataSchemeSpecificPart
+ IntentFilter.addDataSchemeSpecificPart()} with
+ {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
+ Note that because '\' is used as an escape character when
+ reading the string from XML (before it is parsed as a pattern),
+ you will need to double-escape: for example a literal "*" would
+ be written as "\\*" and a literal "\" would be written as
+ "\\\\". This is basically the same as what you would need to
+ write if constructing the string in Java code. -->
+ <attr name="sspPattern" format="string" />
<!-- Specify a URI authority host that is handled, as per
{@link android.content.IntentFilter#addDataAuthority
IntentFilter.addDataAuthority()}.
@@ -1638,17 +1659,17 @@
<attr name="port" format="string" />
<!-- Specify a URI path that must exactly match, as per
{@link android.content.IntentFilter#addDataPath
- IntentFilter.addDataAuthority()} with
+ IntentFilter.addDataPath()} with
{@link android.os.PatternMatcher#PATTERN_LITERAL}. -->
<attr name="path" />
<!-- Specify a URI path that must be a prefix to match, as per
{@link android.content.IntentFilter#addDataPath
- IntentFilter.addDataAuthority()} with
+ IntentFilter.addDataPath()} with
{@link android.os.PatternMatcher#PATTERN_PREFIX}. -->
<attr name="pathPrefix" />
<!-- Specify a URI path that matches a simple pattern, as per
{@link android.content.IntentFilter#addDataPath
- IntentFilter.addDataAuthority()} with
+ IntentFilter.addDataPath()} with
{@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
Note that because '\' is used as an escape character when
reading the string from XML (before it is parsed as a pattern),
@@ -1781,4 +1802,12 @@
<!-- Concrete value to put for this named extra data. -->
<attr name="value" />
</declare-styleable>
+
+ <attr name="keyset" />
+ <declare-styleable name="PublicKey">
+ <attr name="value" />
+ </declare-styleable>
+ <declare-styleable name="KeySet">
+ <attr name="name" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4572f5b..da6dfd3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -123,6 +123,17 @@
of them. This should not normally be modified. -->
<bool name="config_closeDialogWhenTouchOutside">true</bool>
+ <!-- Device configuration indicating this is a device with limited RAM, so heavier-weight
+ features should be turned off. -->
+ <bool name="config_lowRamDevice">false</bool>
+
+ <!-- Device configuration indicating whether we should avoid using accelerated graphics
+ in certain places to reduce RAM footprint. This is ignored if config_lowRamDevice
+ is true (in that case this is assumed true as well). It can allow you to tune down
+ your device's memory use without going to the point of causing applications to turn
+ off features. -->
+ <bool name="config_avoidGfxAccel">false</bool>
+
<!-- The duration (in milliseconds) that the radio will scan for a signal
when there's no network connection. If the scan doesn't timeout, use zero -->
<integer name="config_radioScanningTimeout">0</integer>
@@ -1061,7 +1072,7 @@
<bool name="config_useDevInputEventForAudioJack">false</bool>
<!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
+ <bool name="config_safe_media_volume_enabled">false</bool>
<!-- Set to true if the wifi display supports compositing content stored
in gralloc protected buffers. For this to be true, there must exist
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 637128a5..00caac9 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -50,8 +50,12 @@
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
<dimen name="status_bar_edge_ignore">5dp</dimen>
- <!-- Size of the fastscroll hint letter -->
+ <!-- Minimum size of the fastscroll overlay -->
<dimen name="fastscroll_overlay_size">104dp</dimen>
+ <!-- Text size of the fastscroll overlay -->
+ <dimen name="fastscroll_overlay_text_size">24sp</dimen>
+ <!-- Padding of the fastscroll overlay -->
+ <dimen name="fastscroll_overlay_padding">16dp</dimen>
<!-- Width of the fastscroll thumb -->
<dimen name="fastscroll_thumb_width">64dp</dimen>
<!-- Height of the fastscroll thumb -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 22ef31b..3ab8825 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2053,4 +2053,20 @@
<public type="style" name="Theme.DeviceDefault.NoActionBar.Overscan" id="0x010301df" />
<public type="style" name="Theme.DeviceDefault.Light.NoActionBar.Overscan" id="0x010301e0" />
+<!-- ===============================================================
+ Resources added in version 19 of the platform
+ =============================================================== -->
+ <eat-comment />
+
+ <public type="attr" name="keyset" />
+ <public type="attr" name="targetID" />
+ <public type="attr" name="fromScene" />
+ <public type="attr" name="toScene" />
+ <public type="attr" name="transition" />
+ <public type="attr" name="ssp" />
+ <public type="attr" name="sspPrefix" />
+ <public type="attr" name="sspPattern" />
+ <public type="attr" name="addPrintersActivity" />
+ <public type="attr" name="vendor" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ef0f01d..0080ad9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -712,6 +712,15 @@
tasks and kill their apps. Malicious apps may disrupt
the behavior of other apps.</string>
+ <!-- [CHAR LIMIT=NONE] Title of an application permission, allowing an application to create,
+ change, remove activity stacks. -->
+ <string name="permlab_manageActivityStacks">manage activity stacks</string>
+ <!-- [CHAR LIMIT=NONE] Description of an application permission, allowing an application to create,
+ change, remove activity stacks. -->
+ <string name="permdesc_manageActivityStacks">Allows the app to add, remove, and
+ modify the activity stacks in which other apps run. Malicious apps may disrupt
+ the behavior of other apps.</string>
+
<!-- Title of an application permission, allowing an application to start any activity, regardless of permission protection or exported state. -->
<string name="permlab_startAnyActivity">start any activity</string>
<!-- Description of an application permission, allowing an application to start any activity, regardless of permission protection or exported state. -->
@@ -970,6 +979,18 @@
interface of an accessibility service. Should never be needed for normal apps.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_bindPrintService">bind to a print service</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bindPrintService">Allows the holder to bind to the top-level
+ interface of a print service. Should never be needed for normal apps.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_accessAllPrintJobs">access all print jobs</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_accessAllPrintJobs">Allows the holder to access print jobs
+ created by another app. Should never be needed for normal apps.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_bindTextService">bind to a text service</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_bindTextService">Allows the holder to bind to the top-level
@@ -1797,6 +1818,11 @@
<string name="permdesc_mediaStorageWrite" product="default">Allows the app to modify the contents of the internal media storage.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <string name="permlab_manageDocs" product="default">manage document storage</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_manageDocs" product="default">Allows the app to manage document storage.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
<string name="permlab_sdcardAccessAll">access external storage of all users</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_sdcardAccessAll">Allows the app to access external storage for all users.</string>
@@ -3469,6 +3495,16 @@
<!-- Description of an application permission that lets an application route media output. -->
<string name="permdesc_route_media_output">Allows an application to route media output to other external devices.</string>
+ <!-- Title of an application permission that lets an application access keyguard secure storage. -->
+ <string name="permlab_access_keyguard_secure_storage">Access keyguard secure storage</string>
+ <!-- Description of an application permission that lets an application access keyguard secure storage. -->
+ <string name="permdesc_access_keyguard_secure_storage">Allows an application to access keguard secure storage.</string>
+
+ <!-- Title of an application permission that lets it control keyguard. -->
+ <string name="permlab_control_keyguard">Control displaying and hiding keyguard</string>
+ <!-- Description of an application permission that lets it control keyguard. -->
+ <string name="permdesc_control_keyguard">Allows an application to control keguard.</string>
+
<!-- Shown in the tutorial for tap twice for zoom control. -->
<string name="tutorial_double_tap_to_zoom_message_short">Touch twice for zoom control</string>
@@ -3955,6 +3991,9 @@
<!-- Status message for remote routes that are not available for connection right now -->
<string name="media_route_status_not_available">Not available</string>
+ <!-- Status message for a remote route that is in use (and thus unavailabe) right now -->
+ <string name="media_route_status_in_use">In use</string>
+
<!-- Display manager service -->
<!-- Name of the built-in display. [CHAR LIMIT=50] -->
@@ -3988,7 +4027,7 @@
<!-- Message shown when user enters wrong PIN -->
<string name="kg_wrong_pin">Wrong PIN</string>
<!-- Countdown message shown after too many failed unlock attempts -->
- <string name="kg_too_many_failed_attempts_countdown">Try again in <xliff:g id="number">%d</xliff:g> seconds.</string>
+ <string name="kg_too_many_failed_attempts_countdown">Try again in <xliff:g id="number">%1$d</xliff:g> seconds.</string>
<!-- Instructions for using the pattern unlock screen -->
<string name="kg_pattern_instructions">Draw your pattern</string>
<!-- Instructions for using the SIM PIN unlock screen -->
@@ -4108,8 +4147,109 @@
<!-- Error message title [CHAR LIMIT=35] -->
<string name="error_message_title">Error</string>
<!-- Message informing user that app is not permitted to access accounts. [CHAR LIMIT=none] -->
- <string name="app_no_restricted_accounts">This application does not support accounts for restricted profiles</string>
+ <string name="app_no_restricted_accounts">This app doesn\'t support accounts for restricted profiles</string>
<!-- Message informing user that the requested activity could not be found [CHAR LIMIT=none] -->
<string name="app_not_found">No application found to handle this action</string>
<string name="revoke">Revoke</string>
+
+ <!-- Printing -->
+
+ <!-- ISO A0 media size: 33.11" × 46.81" -->
+ <string name="mediaSize_iso_a0">ISO A0</string>
+ <!-- ISO A1 media size: 23.39" × 33.11" -->
+ <string name="mediaSize_iso_a1">ISO A1</string>
+ <!-- ISO A2 media size: 16.54" x 23.39" -->
+ <string name="mediaSize_iso_a2">ISO A2</string>
+ <!-- ISO A3 media size: 11.69" x 16.54" -->
+ <string name="mediaSize_iso_a3">ISO A3</string>
+ <!-- ISO A4 media size: 8.27" x 11.69" -->
+ <string name="mediaSize_iso_a4">ISO A4</string>
+ <!-- ISO A5 media size: 5.83" x 8.27" -->
+ <string name="mediaSize_iso_a5">ISO A5</string>
+ <!-- ISO A6 media size: 4.13" x 5.83" -->
+ <string name="mediaSize_iso_a6">ISO A6</string>
+ <!-- ISO A7 media size: 2.91" x 4.13" -->
+ <string name="mediaSize_iso_a7">ISO A7</string>
+ <!-- ISO A8 media size: 2.05" x 2.91" -->
+ <string name="mediaSize_iso_a8">ISO A8</string>
+ <!-- ISO A9 media size: 1.46" x 2.05" -->
+ <string name="mediaSize_iso_a9">ISO A9</string>
+ <!-- ISO A10 media size: 1.02" x 1.46" -->
+ <string name="mediaSize_iso_a10">ISO A10</string>
+
+ <!-- ISO B0 media size: 39.37" x 55.67" -->
+ <string name="mediaSize_iso_b0">ISO B0</string>
+ <!-- ISO B1 media size: 27.83" x 39.37" -->
+ <string name="mediaSize_iso_b1">ISO B1</string>
+ <!-- ISO B2 media size - 19.69" x 27.83" -->
+ <string name="mediaSize_iso_b2">ISO B2</string>
+ <!-- ISO B3 media size: 13.90" x 19.69" -->
+ <string name="mediaSize_iso_b3">ISO B3</string>
+ <!-- ISO B4 media size: 9.84" x 13.90" -->
+ <string name="mediaSize_iso_b4">ISO B4</string>
+ <!-- ISO B5 media size: 6.93" x 9.84" -->
+ <string name="mediaSize_iso_b5">ISO B5</string>
+ <!-- ISO B6 media size: 4.92" x 6.93" -->
+ <string name="mediaSize_iso_b6">ISO B6</string>
+ <!-- ISO B7 media size: 3.46" x 4.92" -->
+ <string name="mediaSize_iso_b7">ISO B7</string>
+ <!-- ISO B8 media size: 2.44" x 3.46" -->
+ <string name="mediaSize_iso_b8">ISO B8</string>
+ <!-- ISO B9 media size: 1.73" x 2.44" -->
+ <string name="mediaSize_iso_b9">ISO B9</string>
+ <!-- ISO B10 media size: 1.22" x 1.73" -->
+ <string name="mediaSize_iso_b10">ISO B10</string>
+
+ <!-- ISO C0 media size: 36.10" x 51.06" -->
+ <string name="mediaSize_iso_c0">ISO C0</string>
+ <!-- ISO C1 media size: 25.51" x 36.10" -->
+ <string name="mediaSize_iso_c1">ISO C1</string>
+ <!-- ISO C2 media size: 18.03" x 25.51" -->
+ <string name="mediaSize_iso_c2">ISO C2</string>
+ <!-- ISO C3 media size: 12.76" x 18.03" -->
+ <string name="mediaSize_iso_c3">ISO C3</string>
+ <!-- ISO C4 media size: 9.02" x 12.76" -->
+ <string name="mediaSize_iso_c4">ISO C4</string>
+ <!-- ISO C5 media size: 6.38" x 9.02" -->
+ <string name="mediaSize_iso_c5">ISO C5</string>
+ <!-- ISO C6 media size: 4.49" x 6.38" -->
+ <string name="mediaSize_iso_c6">ISO C6</string>
+ <!-- ISO C7 media size: 3.19" x 4.49" -->
+ <string name="mediaSize_iso_c7">ISO C7</string>
+ <!-- ISO ISO C8 media size: 2.24" x 3.19" -->
+ <string name="mediaSize_iso_c8">ISO C8</string>
+ <!-- ISO ISO C9 media size: 1.57" x 2.24" -->
+ <string name="mediaSize_iso_c9">ISO C9</string>
+ <!-- ISO C10 media size: 1.10" x 1.57" -->
+ <string name="mediaSize_iso_c10">ISO C10</string>
+
+ <!-- North America Letter media size: 8.5" × 11" -->
+ <string name="mediaSize_na_letter">Letter</string>
+ <!-- North America Government Letter media size: 8.0" × 10.5" -->
+ <string name="mediaSize_na_gvrnmt_letter">Government Letter</string>
+ <!-- North America Legal media size: 8.5" × 14" -->
+ <string name="mediaSize_na_legal">Legal</string>
+ <!-- North America Junior Legal media size: 8.0" × 5.0" -->
+ <string name="mediaSize_na_junior_legal">Junior Legal</string>
+ <!-- North America Ledger media size: 17" × 11" -->
+ <string name="mediaSize_na_ledger">Ledger</string>
+ <!-- North America Tabloid media size: 11" × 17" -->
+ <string name="mediaSize_na_tabloid">Tabloid</string>
+
+ <!-- PIN creation dialog message [CHAR LIMIT=none] -->
+ <string name="restr_pin_create_pin">Create a PIN for modifying restrictions</string>
+ <!-- PIN entry dialog label for PIN [CHAR LIMIT=none] -->
+ <string name="restr_pin_enter_pin">Enter PIN</string>
+ <!-- PIN entry dialog label for PIN confirmation [CHAR LIMIT=none] -->
+ <string name="restr_pin_confirm_pin">Confirm PIN</string>
+ <!-- PIN entry dialog error when PINs are not the same [CHAR LIMIT=none] -->
+ <string name="restr_pin_error_doesnt_match">PINs don\'t match. Try again.</string>
+ <!-- PIN entry dialog error when PIN is too short [CHAR LIMIT=none] -->
+ <string name="restr_pin_error_too_short">PIN is too short. Must be at least 4 digits.</string>
+ <!-- PIN entry dialog countdown message for next chance to enter the PIN [CHAR LIMIT=none] -->
+ <!-- Phrase describing a time duration using seconds [CHAR LIMIT=16] -->
+ <plurals name="restr_pin_countdown">
+ <item quantity="one">Incorrect PIN. Try again in 1 second.</item>
+ <item quantity="other">Incorrect PIN. Try again in <xliff:g id="count">%d</xliff:g> seconds.</item>
+ </plurals>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index f494d8c..bd82f35 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -138,6 +138,12 @@
<item name="windowExitAnimation">@anim/submenu_exit</item>
</style>
+ <!-- {@hide} -->
+ <style name="Animation.ToastBar">
+ <item name="windowEnterAnimation">@anim/toast_bar_enter</item>
+ <item name="windowExitAnimation">@anim/toast_bar_exit</item>
+ </style>
+
<style name="Animation.TypingFilter">
<item name="windowEnterAnimation">@anim/grow_fade_in_center</item>
<item name="windowExitAnimation">@anim/shrink_fade_out_center</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9c46f38..b58fcfc 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -38,16 +38,12 @@
<java-symbol type="id" name="action_menu_presenter" />
<java-symbol type="id" name="action_mode_close_button" />
<java-symbol type="id" name="activity_chooser_view_content" />
- <java-symbol type="id" name="albumart" />
<java-symbol type="id" name="alertTitle" />
<java-symbol type="id" name="allow_button" />
<java-symbol type="id" name="alwaysUse" />
<java-symbol type="id" name="amPm" />
<java-symbol type="id" name="authtoken_type" />
<java-symbol type="id" name="back_button" />
- <java-symbol type="id" name="btn_next" />
- <java-symbol type="id" name="btn_play" />
- <java-symbol type="id" name="btn_prev" />
<java-symbol type="id" name="button_bar" />
<java-symbol type="id" name="buttonPanel" />
<java-symbol type="id" name="by_common" />
@@ -217,6 +213,10 @@
<java-symbol type="id" name="sms_short_code_remember_undo_instruction" />
<java-symbol type="id" name="breadcrumb_section" />
<java-symbol type="id" name="action_bar_spinner" />
+ <java-symbol type="id" name="pin_message" />
+ <java-symbol type="id" name="pin1_text" />
+ <java-symbol type="id" name="pin2_text" />
+ <java-symbol type="id" name="pin_error_message" />
<java-symbol type="attr" name="actionModeShareDrawable" />
<java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -243,6 +243,7 @@
<java-symbol type="bool" name="action_bar_embed_tabs" />
<java-symbol type="bool" name="action_bar_embed_tabs_pre_jb" />
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
+ <java-symbol type="bool" name="config_avoidGfxAccel" />
<java-symbol type="bool" name="config_allowActionMenuItemTextWithIcon" />
<java-symbol type="bool" name="config_bluetooth_address_validation" />
<java-symbol type="bool" name="config_bluetooth_sco_off_call" />
@@ -250,6 +251,7 @@
<java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
<java-symbol type="bool" name="config_enable_emergency_call_while_sim_locked" />
<java-symbol type="bool" name="config_enable_puk_unlock_screen" />
+ <java-symbol type="bool" name="config_lowRamDevice" />
<java-symbol type="bool" name="config_mms_content_disposition_support" />
<java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" />
<java-symbol type="bool" name="config_sip_wifi_only" />
@@ -313,9 +315,10 @@
<java-symbol type="dimen" name="dropdownitem_icon_width" />
<java-symbol type="dimen" name="dropdownitem_text_padding_left" />
<java-symbol type="dimen" name="fastscroll_overlay_size" />
+ <java-symbol type="dimen" name="fastscroll_overlay_text_size" />
+ <java-symbol type="dimen" name="fastscroll_overlay_padding" />
<java-symbol type="dimen" name="fastscroll_thumb_height" />
<java-symbol type="dimen" name="fastscroll_thumb_width" />
- <java-symbol type="dimen" name="fastscroll_thumb_width" />
<java-symbol type="dimen" name="password_keyboard_spacebar_vertical_correction" />
<java-symbol type="dimen" name="search_view_preferred_width" />
<java-symbol type="dimen" name="textview_error_popup_default_width" />
@@ -572,26 +575,6 @@
<java-symbol type="string" name="keyboardview_keycode_enter" />
<java-symbol type="string" name="keyboardview_keycode_mode_change" />
<java-symbol type="string" name="keyboardview_keycode_shift" />
- <java-symbol type="string" name="keyguard_accessibility_add_widget" />
- <java-symbol type="string" name="keyguard_accessibility_camera" />
- <java-symbol type="string" name="keyguard_accessibility_expand_lock_area" />
- <java-symbol type="string" name="keyguard_accessibility_face_unlock" />
- <java-symbol type="string" name="keygaurd_accessibility_media_controls" />
- <java-symbol type="string" name="keyguard_accessibility_pattern_area" />
- <java-symbol type="string" name="keyguard_accessibility_pattern_unlock" />
- <java-symbol type="string" name="keyguard_accessibility_password_unlock" />
- <java-symbol type="string" name="keyguard_accessibility_pin_unlock" />
- <java-symbol type="string" name="keyguard_accessibility_slide_area" />
- <java-symbol type="string" name="keyguard_accessibility_slide_unlock" />
- <java-symbol type="string" name="keyguard_accessibility_status" />
- <java-symbol type="string" name="keyguard_accessibility_user_selector" />
- <java-symbol type="string" name="keyguard_accessibility_widget" />
- <java-symbol type="string" name="keyguard_accessibility_widget_deleted" />
- <java-symbol type="string" name="keyguard_accessibility_widget_empty_slot" />
- <java-symbol type="string" name="keyguard_accessibility_widget_reorder_start" />
- <java-symbol type="string" name="keyguard_accessibility_widget_reorder_end" />
- <java-symbol type="string" name="keyguard_accessibility_unlock_area_collapsed" />
- <java-symbol type="string" name="keyguard_accessibility_unlock_area_expanded" />
<java-symbol type="string" name="kilobyteShort" />
<java-symbol type="string" name="last_month" />
<java-symbol type="string" name="launchBrowserDefault" />
@@ -601,9 +584,6 @@
<java-symbol type="string" name="lockscreen_access_pattern_start" />
<java-symbol type="string" name="lockscreen_emergency_call" />
<java-symbol type="string" name="lockscreen_return_to_call" />
- <java-symbol type="string" name="lockscreen_transport_pause_description" />
- <java-symbol type="string" name="lockscreen_transport_play_description" />
- <java-symbol type="string" name="lockscreen_transport_stop_description" />
<java-symbol type="string" name="low_memory" />
<java-symbol type="string" name="media_bad_removal" />
<java-symbol type="string" name="media_checking" />
@@ -878,6 +858,7 @@
<java-symbol type="string" name="media_route_status_connecting" />
<java-symbol type="string" name="media_route_status_available" />
<java-symbol type="string" name="media_route_status_not_available" />
+ <java-symbol type="string" name="media_route_status_in_use" />
<java-symbol type="string" name="owner_name" />
<java-symbol type="string" name="config_chooseAccountActivity" />
<java-symbol type="string" name="config_chooseTypeAndAccountActivity" />
@@ -886,6 +867,46 @@
<java-symbol type="string" name="action_bar_home_description_format" />
<java-symbol type="string" name="action_bar_home_subtitle_description_format" />
<java-symbol type="string" name="wireless_display_route_description" />
+ <java-symbol type="string" name="mediaSize_iso_a0" />
+ <java-symbol type="string" name="mediaSize_iso_a1" />
+ <java-symbol type="string" name="mediaSize_iso_a2" />
+ <java-symbol type="string" name="mediaSize_iso_a3" />
+ <java-symbol type="string" name="mediaSize_iso_a4" />
+ <java-symbol type="string" name="mediaSize_iso_a5" />
+ <java-symbol type="string" name="mediaSize_iso_a6" />
+ <java-symbol type="string" name="mediaSize_iso_a7" />
+ <java-symbol type="string" name="mediaSize_iso_a8" />
+ <java-symbol type="string" name="mediaSize_iso_a9" />
+ <java-symbol type="string" name="mediaSize_iso_a10" />
+ <java-symbol type="string" name="mediaSize_iso_b0" />
+ <java-symbol type="string" name="mediaSize_iso_b1" />
+ <java-symbol type="string" name="mediaSize_iso_b2" />
+ <java-symbol type="string" name="mediaSize_iso_b3" />
+ <java-symbol type="string" name="mediaSize_iso_b4" />
+ <java-symbol type="string" name="mediaSize_iso_b5" />
+ <java-symbol type="string" name="mediaSize_iso_b6" />
+ <java-symbol type="string" name="mediaSize_iso_b7" />
+ <java-symbol type="string" name="mediaSize_iso_b8" />
+ <java-symbol type="string" name="mediaSize_iso_b9" />
+ <java-symbol type="string" name="mediaSize_iso_b10" />
+ <java-symbol type="string" name="mediaSize_iso_c0" />
+ <java-symbol type="string" name="mediaSize_iso_c1" />
+ <java-symbol type="string" name="mediaSize_iso_c2" />
+ <java-symbol type="string" name="mediaSize_iso_c3" />
+ <java-symbol type="string" name="mediaSize_iso_c4" />
+ <java-symbol type="string" name="mediaSize_iso_c5" />
+ <java-symbol type="string" name="mediaSize_iso_c6" />
+ <java-symbol type="string" name="mediaSize_iso_c7" />
+ <java-symbol type="string" name="mediaSize_iso_c8" />
+ <java-symbol type="string" name="mediaSize_iso_c9" />
+ <java-symbol type="string" name="mediaSize_iso_c10" />
+ <java-symbol type="string" name="mediaSize_na_letter" />
+ <java-symbol type="string" name="mediaSize_na_gvrnmt_letter" />
+ <java-symbol type="string" name="mediaSize_na_legal" />
+ <java-symbol type="string" name="mediaSize_na_junior_legal" />
+ <java-symbol type="string" name="mediaSize_na_ledger" />
+ <java-symbol type="string" name="mediaSize_na_tabloid" />
+ <java-symbol type="string" name="restr_pin_enter_pin" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
<java-symbol type="plurals" name="abbrev_in_num_hours" />
@@ -908,6 +929,7 @@
<java-symbol type="plurals" name="num_hours_ago" />
<java-symbol type="plurals" name="num_minutes_ago" />
<java-symbol type="plurals" name="num_seconds_ago" />
+ <java-symbol type="plurals" name="restr_pin_countdown" />
<java-symbol type="array" name="carrier_properties" />
<java-symbol type="array" name="config_data_usage_network_types" />
@@ -1019,21 +1041,17 @@
<java-symbol type="drawable" name="text_select_handle_left" />
<java-symbol type="drawable" name="text_select_handle_middle" />
<java-symbol type="drawable" name="text_select_handle_right" />
+ <java-symbol type="drawable" name="toast_bar_bg" />
<java-symbol type="drawable" name="unknown_image" />
<java-symbol type="drawable" name="unlock_default" />
<java-symbol type="drawable" name="unlock_halo" />
<java-symbol type="drawable" name="unlock_ring" />
<java-symbol type="drawable" name="unlock_wave" />
- <java-symbol type="drawable" name="ic_lockscreen_camera" />
- <java-symbol type="drawable" name="ic_lockscreen_silent" />
- <java-symbol type="drawable" name="ic_lockscreen_unlock" />
<java-symbol type="drawable" name="ic_action_assist_generic" />
- <java-symbol type="drawable" name="ic_lockscreen_alarm" />
<java-symbol type="drawable" name="notification_bg" />
<java-symbol type="drawable" name="notification_bg_low" />
<java-symbol type="drawable" name="notification_template_icon_bg" />
<java-symbol type="drawable" name="notification_template_icon_low_bg" />
- <java-symbol type="drawable" name="ic_lockscreen_unlock_phantom" />
<java-symbol type="drawable" name="ic_media_route_on_holo_dark" />
<java-symbol type="drawable" name="ic_media_route_disabled_holo_dark" />
@@ -1112,6 +1130,7 @@
<java-symbol type="layout" name="textview_hint" />
<java-symbol type="layout" name="time_picker" />
<java-symbol type="layout" name="time_picker_dialog" />
+ <java-symbol type="layout" name="toast_bar" />
<java-symbol type="layout" name="transient_notification" />
<java-symbol type="layout" name="volume_adjust" />
<java-symbol type="layout" name="volume_adjust_item" />
@@ -1133,12 +1152,10 @@
<java-symbol type="layout" name="notification_template_part_time" />
<java-symbol type="layout" name="notification_template_part_chronometer" />
<java-symbol type="layout" name="notification_template_inbox" />
- <java-symbol type="layout" name="keyguard_multi_user_avatar" />
- <java-symbol type="layout" name="keyguard_multi_user_selector_widget" />
<java-symbol type="layout" name="sms_short_code_confirmation_dialog" />
- <java-symbol type="layout" name="keyguard_add_widget" />
<java-symbol type="layout" name="action_bar_up_container" />
<java-symbol type="layout" name="app_not_authorized" />
+ <java-symbol type="layout" name="pin_challenge" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />
@@ -1154,7 +1171,6 @@
<java-symbol type="xml" name="password_kbd_qwerty_shifted" />
<java-symbol type="xml" name="password_kbd_symbols" />
<java-symbol type="xml" name="password_kbd_symbols_shift" />
- <java-symbol type="xml" name="kg_password_kbd_numeric" />
<java-symbol type="xml" name="power_profile" />
<java-symbol type="xml" name="time_zones_by_country" />
<java-symbol type="xml" name="sms_short_codes" />
@@ -1169,6 +1185,7 @@
<java-symbol type="style" name="Animation.DropDownUp" />
<java-symbol type="style" name="Animation.DropDownDown" />
<java-symbol type="style" name="Animation.PopupWindow" />
+ <java-symbol type="style" name="Animation.ToastBar" />
<java-symbol type="style" name="Animation.TypingFilter" />
<java-symbol type="style" name="Animation.TypingFilterRestore" />
<java-symbol type="style" name="Animation.Dream" />
@@ -1208,8 +1225,6 @@
<!-- From android.policy -->
<java-symbol type="anim" name="app_starting_exit" />
- <java-symbol type="anim" name="lock_screen_behind_enter" />
- <java-symbol type="anim" name="lock_screen_wallpaper_behind_enter" />
<java-symbol type="anim" name="dock_top_enter" />
<java-symbol type="anim" name="dock_top_exit" />
<java-symbol type="anim" name="dock_bottom_enter" />
@@ -1218,22 +1233,12 @@
<java-symbol type="anim" name="dock_left_exit" />
<java-symbol type="anim" name="dock_right_enter" />
<java-symbol type="anim" name="dock_right_exit" />
- <java-symbol type="anim" name="keyguard_security_animate_in" />
- <java-symbol type="anim" name="keyguard_security_animate_out" />
- <java-symbol type="anim" name="keyguard_security_fade_in" />
- <java-symbol type="anim" name="keyguard_security_fade_out" />
- <java-symbol type="anim" name="keyguard_action_assist_exit" />
- <java-symbol type="anim" name="keyguard_action_assist_enter" />
<java-symbol type="array" name="config_keyboardTapVibePattern" />
<java-symbol type="array" name="config_longPressVibePattern" />
<java-symbol type="array" name="config_safeModeDisabledVibePattern" />
<java-symbol type="array" name="config_safeModeEnabledVibePattern" />
<java-symbol type="array" name="config_virtualKeyVibePattern" />
- <java-symbol type="array" name="lockscreen_targets_when_silent" />
- <java-symbol type="array" name="lockscreen_targets_when_soundon" />
- <java-symbol type="array" name="lockscreen_targets_with_camera" />
- <java-symbol type="array" name="lockscreen_num_pad_klondike" />
<java-symbol type="attr" name="actionModePopupWindowStyle" />
<java-symbol type="attr" name="dialogCustomTitleDecorLayout" />
<java-symbol type="attr" name="dialogTitleDecorLayout" />
@@ -1248,33 +1253,11 @@
<java-symbol type="bool" name="config_lidControlsSleep" />
<java-symbol type="bool" name="config_reverseDefaultRotation" />
<java-symbol type="bool" name="config_showNavigationBar" />
- <java-symbol type="bool" name="kg_enable_camera_default_widget" />
- <java-symbol type="bool" name="kg_share_status_area" />
- <java-symbol type="bool" name="kg_sim_puk_account_full_screen" />
- <java-symbol type="bool" name="kg_top_align_page_shrink_on_bouncer_visible" />
<java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
- <java-symbol type="bool" name="kg_center_small_widgets_vertically" />
- <java-symbol type="bool" name="kg_show_ime_at_screen_on" />
- <java-symbol type="color" name="kg_multi_user_text_active" />
- <java-symbol type="color" name="kg_multi_user_text_inactive" />
- <java-symbol type="color" name="kg_widget_pager_gradient" />
- <java-symbol type="color" name="keyguard_avatar_frame_color" />
- <java-symbol type="color" name="keyguard_avatar_frame_pressed_color" />
- <java-symbol type="color" name="keyguard_avatar_frame_shadow_color" />
- <java-symbol type="color" name="keyguard_avatar_nick_color" />
<java-symbol type="dimen" name="navigation_bar_height" />
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />
<java-symbol type="dimen" name="status_bar_height" />
- <java-symbol type="dimen" name="kg_widget_pager_horizontal_padding" />
- <java-symbol type="dimen" name="kg_widget_pager_top_padding" />
- <java-symbol type="dimen" name="kg_widget_pager_bottom_padding" />
- <java-symbol type="dimen" name="keyguard_avatar_size" />
- <java-symbol type="dimen" name="keyguard_avatar_frame_stroke_width" />
- <java-symbol type="dimen" name="keyguard_avatar_frame_shadow_radius" />
- <java-symbol type="dimen" name="kg_edge_swipe_region_size" />
- <java-symbol type="dimen" name="kg_squashed_layout_threshold" />
- <java-symbol type="dimen" name="kg_small_widget_height" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
<java-symbol type="drawable" name="ic_jog_dial_unlock" />
@@ -1293,9 +1276,7 @@
<java-symbol type="drawable" name="magnified_region_frame" />
<java-symbol type="drawable" name="menu_background" />
<java-symbol type="drawable" name="stat_sys_secure" />
- <java-symbol type="drawable" name="kg_widget_bg_padded" />
<java-symbol type="id" name="action_mode_bar_stub" />
- <java-symbol type="id" name="alarm_status" />
<java-symbol type="id" name="button0" />
<java-symbol type="id" name="button4" />
<java-symbol type="id" name="button5" />
@@ -1303,15 +1284,12 @@
<java-symbol type="id" name="button7" />
<java-symbol type="id" name="date" />
<java-symbol type="id" name="eight" />
- <java-symbol type="id" name="face_unlock_area_view" />
- <java-symbol type="id" name="face_unlock_cancel_button" />
<java-symbol type="id" name="five" />
<java-symbol type="id" name="four" />
<java-symbol type="id" name="icon_menu_presenter" />
<java-symbol type="id" name="keyboard" />
<java-symbol type="id" name="list_menu_presenter" />
<java-symbol type="id" name="lock_screen" />
- <java-symbol type="id" name="login" />
<java-symbol type="id" name="nine" />
<java-symbol type="id" name="no_applications_message" />
<java-symbol type="id" name="ok" />
@@ -1319,57 +1297,14 @@
<java-symbol type="id" name="option1" />
<java-symbol type="id" name="option2" />
<java-symbol type="id" name="option3" />
- <java-symbol type="id" name="password" />
- <java-symbol type="id" name="passwordEntry" />
- <java-symbol type="id" name="pinEntry" />
<java-symbol type="id" name="right_icon" />
<java-symbol type="id" name="seven" />
<java-symbol type="id" name="six" />
<java-symbol type="id" name="status" />
- <java-symbol type="id" name="switch_ime_button" />
<java-symbol type="id" name="three" />
<java-symbol type="id" name="title_container" />
<java-symbol type="id" name="two" />
<java-symbol type="id" name="zero" />
- <java-symbol type="id" name="keyguard_message_area" />
- <java-symbol type="id" name="keyguard_click_area" />
- <java-symbol type="id" name="keyguard_selector_view" />
- <java-symbol type="id" name="keyguard_pattern_view" />
- <java-symbol type="id" name="keyguard_password_view" />
- <java-symbol type="id" name="keyguard_pin_view" />
- <java-symbol type="id" name="keyguard_face_unlock_view" />
- <java-symbol type="id" name="keyguard_sim_pin_view" />
- <java-symbol type="id" name="keyguard_sim_puk_view" />
- <java-symbol type="id" name="keyguard_account_view" />
- <java-symbol type="id" name="keyguard_selector_fade_container" />
- <java-symbol type="id" name="keyguard_widget_pager_delete_target" />
- <java-symbol type="id" name="keyguard_bouncer_frame" />
- <java-symbol type="id" name="app_widget_container" />
- <java-symbol type="id" name="view_flipper" />
- <java-symbol type="id" name="carrier_text" />
- <java-symbol type="id" name="emergency_call_button" />
- <java-symbol type="id" name="keyguard_host_view" />
- <java-symbol type="id" name="delete_button" />
- <java-symbol type="id" name="lockPatternView" />
- <java-symbol type="id" name="forgot_password_button" />
- <java-symbol type="id" name="glow_pad_view" />
- <java-symbol type="id" name="delete_button" />
- <java-symbol type="id" name="keyguard_user_avatar" />
- <java-symbol type="id" name="keyguard_user_name" />
- <java-symbol type="id" name="keyguard_transport_control" />
- <java-symbol type="id" name="keyguard_status_view" />
- <java-symbol type="id" name="keyguard_status_view_face_palm" />
- <java-symbol type="id" name="keyguard_users_grid" />
- <java-symbol type="id" name="clock_text" />
- <java-symbol type="id" name="clock_view" />
- <java-symbol type="id" name="keyguard_multi_user_selector" />
- <java-symbol type="id" name="sliding_layout" />
- <java-symbol type="id" name="keyguard_add_widget" />
- <java-symbol type="id" name="keyguard_add_widget_view" />
- <java-symbol type="id" name="multi_pane_challenge" />
- <java-symbol type="id" name="keyguard_user_selector" />
- <java-symbol type="id" name="key_enter" />
- <java-symbol type="id" name="keyguard_selector_view_frame" />
<java-symbol type="integer" name="config_carDockRotation" />
<java-symbol type="integer" name="config_defaultUiModeType" />
<java-symbol type="integer" name="config_deskDockRotation" />
@@ -1378,18 +1313,8 @@
<java-symbol type="integer" name="config_lidNavigationAccessibility" />
<java-symbol type="integer" name="config_lidOpenRotation" />
<java-symbol type="integer" name="config_longPressOnHomeBehavior" />
- <java-symbol type="integer" name="kg_security_flip_duration" />
- <java-symbol type="integer" name="kg_carousel_angle" />
<java-symbol type="layout" name="global_actions_item" />
<java-symbol type="layout" name="global_actions_silent_mode" />
- <java-symbol type="layout" name="keyguard_selector_view" />
- <java-symbol type="layout" name="keyguard_pattern_view" />
- <java-symbol type="layout" name="keyguard_password_view" />
- <java-symbol type="layout" name="keyguard_pin_view" />
- <java-symbol type="layout" name="keyguard_face_unlock_view" />
- <java-symbol type="layout" name="keyguard_sim_pin_view" />
- <java-symbol type="layout" name="keyguard_sim_puk_view" />
- <java-symbol type="layout" name="keyguard_account_view" />
<java-symbol type="layout" name="recent_apps_dialog" />
<java-symbol type="layout" name="screen_action_bar" />
<java-symbol type="layout" name="screen_custom_title" />
@@ -1398,9 +1323,6 @@
<java-symbol type="layout" name="screen_simple_overlay_action_mode" />
<java-symbol type="layout" name="screen_title" />
<java-symbol type="layout" name="screen_title_icons" />
- <java-symbol type="layout" name="keyguard_host_view" />
- <java-symbol type="layout" name="keyguard_transport_control_view" />
- <java-symbol type="layout" name="keyguard_status_view" />
<java-symbol type="string" name="abbrev_wday_month_day_no_year" />
<java-symbol type="string" name="system_ui_date_pattern" />
<java-symbol type="string" name="android_upgrading_title" />
@@ -1416,41 +1338,7 @@
<java-symbol type="string" name="global_action_silent_mode_on_status" />
<java-symbol type="string" name="global_action_toggle_silent_mode" />
<java-symbol type="string" name="invalidPuk" />
- <java-symbol type="string" name="keyguard_password_enter_pin_code" />
- <java-symbol type="string" name="keyguard_password_enter_puk_code" />
- <java-symbol type="string" name="keyguard_password_wrong_pin_code" />
<java-symbol type="string" name="lockscreen_carrier_default" />
- <java-symbol type="string" name="lockscreen_charged" />
- <java-symbol type="string" name="lockscreen_failed_attempts_almost_at_wipe" />
- <java-symbol type="string" name="lockscreen_failed_attempts_almost_glogin" />
- <java-symbol type="string" name="lockscreen_failed_attempts_now_wiping" />
- <java-symbol type="string" name="lockscreen_forgot_pattern_button_text" />
- <java-symbol type="string" name="lockscreen_glogin_checking_password" />
- <java-symbol type="string" name="lockscreen_glogin_forgot_pattern" />
- <java-symbol type="string" name="lockscreen_glogin_invalid_input" />
- <java-symbol type="string" name="lockscreen_glogin_too_many_attempts" />
- <java-symbol type="string" name="lockscreen_instructions_when_pattern_disabled" />
- <java-symbol type="string" name="lockscreen_low_battery" />
- <java-symbol type="string" name="lockscreen_missing_sim_instructions" />
- <java-symbol type="string" name="lockscreen_missing_sim_instructions_long" />
- <java-symbol type="string" name="lockscreen_missing_sim_message_short" />
- <java-symbol type="string" name="lockscreen_network_locked_message" />
- <java-symbol type="string" name="lockscreen_password_wrong" />
- <java-symbol type="string" name="lockscreen_pattern_instructions" />
- <java-symbol type="string" name="lockscreen_pattern_wrong" />
- <java-symbol type="string" name="lockscreen_permanent_disabled_sim_message_short" />
- <java-symbol type="string" name="lockscreen_permanent_disabled_sim_instructions" />
- <java-symbol type="string" name="lockscreen_plugged_in" />
- <java-symbol type="string" name="lockscreen_sim_locked_message" />
- <java-symbol type="string" name="lockscreen_sim_puk_locked_message" />
- <java-symbol type="string" name="lockscreen_sim_unlock_progress_dialog_message" />
- <java-symbol type="string" name="lockscreen_sound_off_label" />
- <java-symbol type="string" name="lockscreen_sound_on_label" />
- <java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" />
- <java-symbol type="string" name="lockscreen_too_many_failed_attempts_dialog_message" />
- <java-symbol type="string" name="lockscreen_too_many_failed_password_attempts_dialog_message" />
- <java-symbol type="string" name="lockscreen_too_many_failed_pin_attempts_dialog_message" />
- <java-symbol type="string" name="lockscreen_unlock_label" />
<java-symbol type="string" name="status_bar_device_locked" />
<java-symbol type="style" name="Animation.LockScreen" />
<java-symbol type="style" name="Theme.Dialog.RecentApplications" />
@@ -1458,40 +1346,6 @@
<java-symbol type="style" name="Widget.Button.NumPadKey" />
<java-symbol type="style" name="TextAppearance.NumPadKey" />
<java-symbol type="style" name="TextAppearance.NumPadKey.Klondike" />
- <java-symbol type="string" name="kg_emergency_call_label" />
- <java-symbol type="string" name="kg_forgot_pattern_button_text" />
- <java-symbol type="string" name="kg_wrong_pattern" />
- <java-symbol type="string" name="kg_wrong_password" />
- <java-symbol type="string" name="kg_wrong_pin" />
- <java-symbol type="string" name="kg_too_many_failed_attempts_countdown" />
- <java-symbol type="string" name="kg_pattern_instructions" />
- <java-symbol type="string" name="kg_sim_pin_instructions" />
- <java-symbol type="string" name="kg_pin_instructions" />
- <java-symbol type="string" name="kg_password_instructions" />
- <java-symbol type="string" name="kg_puk_enter_puk_hint" />
- <java-symbol type="string" name="kg_puk_enter_pin_hint" />
- <java-symbol type="string" name="kg_sim_unlock_progress_dialog_message" />
- <java-symbol type="string" name="kg_password_wrong_pin_code" />
- <java-symbol type="string" name="kg_invalid_sim_pin_hint" />
- <java-symbol type="string" name="kg_invalid_sim_puk_hint" />
- <java-symbol type="string" name="kg_invalid_puk" />
- <java-symbol type="string" name="kg_login_too_many_attempts" />
- <java-symbol type="string" name="kg_login_instructions" />
- <java-symbol type="string" name="kg_login_username_hint" />
- <java-symbol type="string" name="kg_login_password_hint" />
- <java-symbol type="string" name="kg_login_submit_button" />
- <java-symbol type="string" name="kg_login_invalid_input" />
- <java-symbol type="string" name="kg_login_account_recovery_hint" />
- <java-symbol type="string" name="kg_login_checking_password" />
- <java-symbol type="string" name="kg_too_many_failed_pin_attempts_dialog_message" />
- <java-symbol type="string" name="kg_too_many_failed_pattern_attempts_dialog_message" />
- <java-symbol type="string" name="kg_too_many_failed_password_attempts_dialog_message" />
- <java-symbol type="string" name="kg_failed_attempts_almost_at_wipe" />
- <java-symbol type="string" name="kg_failed_attempts_now_wiping" />
- <java-symbol type="string" name="kg_failed_attempts_almost_at_login" />
- <java-symbol type="string" name="kg_enter_confirm_pin_hint" />
- <java-symbol type="string" name="kg_invalid_confirm_pin_hint" />
- <java-symbol type="string" name="kg_text_message_separator" />
<!-- From services -->
<java-symbol type="anim" name="screen_rotate_0_enter" />
@@ -1718,6 +1572,9 @@
<java-symbol type="anim" name="push_down_out" />
<java-symbol type="anim" name="push_up_in" />
<java-symbol type="anim" name="push_up_out" />
+ <java-symbol type="anim" name="lock_screen_wallpaper_behind_enter" />
+ <java-symbol type="anim" name="lock_screen_behind_enter" />
+
<java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
<java-symbol type="dimen" name="status_bar_icon_size" />
<java-symbol type="dimen" name="system_bar_icon_size" />
diff --git a/core/tests/benchmarks/src/android/os/StrictModeBenchmark.java b/core/tests/benchmarks/src/android/os/StrictModeBenchmark.java
new file mode 100644
index 0000000..41af3820
--- /dev/null
+++ b/core/tests/benchmarks/src/android/os/StrictModeBenchmark.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.StrictMode.ThreadPolicy;
+
+import com.google.caliper.SimpleBenchmark;
+
+public class StrictModeBenchmark extends SimpleBenchmark {
+
+ private ThreadPolicy mOff = new ThreadPolicy.Builder().build();
+ private ThreadPolicy mOn = new ThreadPolicy.Builder().detectAll().build();
+
+ public void timeToggleThreadPolicy(int reps) {
+ for (int i = 0; i < reps; i++) {
+ StrictMode.setThreadPolicy(mOn);
+ StrictMode.setThreadPolicy(mOff);
+ }
+ }
+}
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
index 7b36167..87c2ef5 100644
--- a/data/keyboards/common.mk
+++ b/data/keyboards/common.mk
@@ -15,42 +15,8 @@
# This is the list of framework provided keylayouts and key character maps to include.
# Used by Android.mk and keyboards.mk.
-keylayouts := \
- Generic.kl \
- AVRCP.kl \
- qwerty.kl \
- Vendor_045e_Product_028e.kl \
- Vendor_046d_Product_c216.kl \
- Vendor_046d_Product_c294.kl \
- Vendor_046d_Product_c299.kl \
- Vendor_046d_Product_c532.kl \
- Vendor_054c_Product_0268.kl \
- Vendor_05ac_Product_0239.kl \
- Vendor_22b8_Product_093d.kl \
- Vendor_0079_Product_0011.kl \
- Vendor_046d_Product_c219.kl \
- Vendor_046d_Product_c21f.kl \
- Vendor_0583_Product_2060.kl \
- Vendor_0a5c_Product_8502.kl \
- Vendor_1038_Product_1412.kl \
- Vendor_12bd_Product_d015.kl \
- Vendor_1689_Product_fd00.kl \
- Vendor_1689_Product_fd01.kl \
- Vendor_1689_Product_fe00.kl \
- Vendor_1bad_Product_f016.kl \
- Vendor_1bad_Product_f023.kl \
- Vendor_1bad_Product_f027.kl \
- Vendor_1bad_Product_f036.kl \
- Vendor_1d79_Product_0009.kl \
- Vendor_2378_Product_100a.kl
+keylayouts := $(notdir $(wildcard $(LOCAL_PATH)/*.kl))
-keycharmaps := \
- Generic.kcm \
- Virtual.kcm \
- qwerty.kcm \
- qwerty2.kcm
+keycharmaps := $(notdir $(wildcard $(LOCAL_PATH)/*.kcm))
-keyconfigs := \
- qwerty.idc \
- qwerty2.idc
-
+keyconfigs := $(notdir $(wildcard $(LOCAL_PATH)/*.idc))
diff --git a/graphics/java/android/graphics/Atlas.java b/graphics/java/android/graphics/Atlas.java
new file mode 100644
index 0000000..39a5a53
--- /dev/null
+++ b/graphics/java/android/graphics/Atlas.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+/**
+ * @hide
+ */
+public class Atlas {
+ /**
+ * This flag indicates whether the packing algorithm will attempt
+ * to rotate entries to make them fit better in the atlas.
+ */
+ public static final int FLAG_ALLOW_ROTATIONS = 0x1;
+ /**
+ * This flag indicates whether the packing algorithm should leave
+ * an empty 1 pixel wide border around each bitmap. This border can
+ * be useful if the content of the atlas will be used in OpenGL using
+ * bilinear filtering.
+ */
+ public static final int FLAG_ADD_PADDING = 0x2;
+ /**
+ * Default flags: allow rotations and add padding.
+ */
+ public static final int FLAG_DEFAULTS = FLAG_ADD_PADDING;
+
+ /**
+ * Each type defines a different packing algorithm that can
+ * be used by an {@link Atlas}. The best algorithm to use
+ * will depend on the source dataset and the dimensions of
+ * the atlas.
+ */
+ public enum Type {
+ SliceMinArea,
+ SliceMaxArea,
+ SliceShortAxis,
+ SliceLongAxis
+ }
+
+ /**
+ * Represents a bitmap packed in the atlas. Each entry has a location in
+ * pixels in the atlas and a rotation flag. If the entry was rotated, the
+ * bitmap must be rotated by 90 degrees (in either direction as long as
+ * the origin remains the same) before being rendered into the atlas.
+ */
+ public static class Entry {
+ /**
+ * Location, in pixels, of the bitmap on the X axis in the atlas.
+ */
+ public int x;
+ /**
+ * Location, in pixels, of the bitmap on the Y axis in the atlas.
+ */
+ public int y;
+
+ /**
+ * If true, the bitmap must be rotated 90 degrees in the atlas.
+ */
+ public boolean rotated;
+ }
+
+ private final Policy mPolicy;
+
+ /**
+ * Creates a new atlas with the specified algorithm and dimensions
+ * in pixels. Calling this constructor is equivalent to calling
+ * {@link #Atlas(Atlas.Type, int, int, int)} with {@link #FLAG_DEFAULTS}.
+ *
+ * @param type The algorithm to use to pack rectangles in the atlas
+ * @param width The width of the atlas in pixels
+ * @param height The height of the atlas in pixels
+ *
+ * @see #Atlas(Atlas.Type, int, int, int)
+ */
+ public Atlas(Type type, int width, int height) {
+ this(type, width, height, FLAG_DEFAULTS);
+ }
+
+ /**
+ * Creates a new atlas with the specified algorithm and dimensions
+ * in pixels. A set of flags can also be specified to control the
+ * behavior of the atlas.
+ *
+ * @param type The algorithm to use to pack rectangles in the atlas
+ * @param width The width of the atlas in pixels
+ * @param height The height of the atlas in pixels
+ * @param flags Optional flags to control the behavior of the atlas:
+ * {@link #FLAG_ADD_PADDING}, {@link #FLAG_ALLOW_ROTATIONS}
+ *
+ * @see #Atlas(Atlas.Type, int, int)
+ */
+ public Atlas(Type type, int width, int height, int flags) {
+ mPolicy = findPolicy(type, width, height, flags);
+ }
+
+ /**
+ * Packs a rectangle of the specified dimensions in this atlas.
+ *
+ * @param width The width of the rectangle to pack in the atlas
+ * @param height The height of the rectangle to pack in the atlas
+ *
+ * @return An {@link Entry} instance if the rectangle was packed in
+ * the atlas, or null if the rectangle could not fit
+ *
+ * @see #pack(int, int, Atlas.Entry)
+ */
+ public Entry pack(int width, int height) {
+ return pack(width, height, null);
+ }
+
+ /**
+ * Packs a rectangle of the specified dimensions in this atlas.
+ *
+ * @param width The width of the rectangle to pack in the atlas
+ * @param height The height of the rectangle to pack in the atlas
+ * @param entry Out parameter that will be filled in with the location
+ * and attributes of the packed rectangle, can be null
+ *
+ * @return An {@link Entry} instance if the rectangle was packed in
+ * the atlas, or null if the rectangle could not fit
+ *
+ * @see #pack(int, int)
+ */
+ public Entry pack(int width, int height, Entry entry) {
+ if (entry == null) entry = new Entry();
+ return mPolicy.pack(width, height, entry);
+ }
+
+ private static Policy findPolicy(Type type, int width, int height, int flags) {
+ switch (type) {
+ case SliceMinArea:
+ return new SlicePolicy(width, height, flags,
+ new SlicePolicy.MinAreaSplitDecision());
+ case SliceMaxArea:
+ return new SlicePolicy(width, height, flags,
+ new SlicePolicy.MaxAreaSplitDecision());
+ case SliceShortAxis:
+ return new SlicePolicy(width, height, flags,
+ new SlicePolicy.ShorterFreeAxisSplitDecision());
+ case SliceLongAxis:
+ return new SlicePolicy(width, height, flags,
+ new SlicePolicy.LongerFreeAxisSplitDecision());
+ }
+ return null;
+ }
+
+ /**
+ * A policy defines how the atlas performs the packing operation.
+ */
+ private static abstract class Policy {
+ abstract Entry pack(int width, int height, Entry entry);
+ }
+
+ /**
+ * The Slice algorightm divides the remaining empty space either
+ * horizontally or vertically after a bitmap is placed in the atlas.
+ *
+ * NOTE: the algorithm is explained below using a tree but is
+ * implemented using a linked list instead for performance reasons.
+ *
+ * The algorithm starts with a single empty cell covering the entire
+ * atlas:
+ *
+ * -----------------------
+ * | |
+ * | |
+ * | |
+ * | Empty space |
+ * | (C0) |
+ * | |
+ * | |
+ * | |
+ * -----------------------
+ *
+ * The tree of cells looks like this:
+ *
+ * N0(free)
+ *
+ * The algorithm then places a bitmap B1, if possible:
+ *
+ * -----------------------
+ * | | |
+ * | B1 | |
+ * | | |
+ * |-------- |
+ * | |
+ * | |
+ * | |
+ * | |
+ * -----------------------
+ *
+ * After placing a bitmap in an empty cell, the algorithm splits
+ * the remaining space in two new empty cells. The split can occur
+ * vertically or horizontally (this is controlled by the "split
+ * decision" parameter of the algorithm.)
+ *
+ * Here is for the instance the result of a vertical split:
+ *
+ * -----------------------
+ * | | |
+ * | B1 | |
+ * | | |
+ * |--------| C2 |
+ * | | |
+ * | | |
+ * | C1 | |
+ * | | |
+ * -----------------------
+ *
+ * The cells tree now looks like this:
+ *
+ * C0(occupied)
+ * / \
+ * / \
+ * / \
+ * / \
+ * C1(free) C2(free)
+ *
+ * For each bitmap to place in the atlas, the Slice algorithm
+ * will visit the free cells until it finds one where a bitmap can
+ * fit. It will then split the now occupied cell and proceed onto
+ * the next bitmap.
+ */
+ private static class SlicePolicy extends Policy {
+ private final Cell mRoot = new Cell();
+
+ private final SplitDecision mSplitDecision;
+
+ private final boolean mAllowRotation;
+ private final int mPadding;
+
+ /**
+ * A cell represents a sub-rectangle of the atlas. A cell is
+ * a node in a linked list representing the available free
+ * space in the atlas.
+ */
+ private static class Cell {
+ int x;
+ int y;
+
+ int width;
+ int height;
+
+ Cell next;
+
+ @Override
+ public String toString() {
+ return String.format("cell[x=%d y=%d width=%d height=%d", x, y, width, height);
+ }
+ }
+
+ SlicePolicy(int width, int height, int flags, SplitDecision splitDecision) {
+ mAllowRotation = (flags & FLAG_ALLOW_ROTATIONS) != 0;
+ mPadding = (flags & FLAG_ADD_PADDING) != 0 ? 1 : 0;
+
+ // The entire atlas is empty at first, minus padding
+ Cell first = new Cell();
+ first.x = first.y = mPadding;
+ first.width = width - 2 * mPadding;
+ first.height = height - 2 * mPadding;
+
+ mRoot.next = first;
+ mSplitDecision = splitDecision;
+ }
+
+ @Override
+ Entry pack(int width, int height, Entry entry) {
+ Cell cell = mRoot.next;
+ Cell prev = mRoot;
+
+ while (cell != null) {
+ if (insert(cell, prev, width, height, entry)) {
+ return entry;
+ }
+
+ prev = cell;
+ cell = cell.next;
+ }
+
+ return null;
+ }
+
+ /**
+ * Defines how the remaining empty space should be split up:
+ * vertically or horizontally.
+ */
+ private static interface SplitDecision {
+ /**
+ * Returns true if the remaining space defined by
+ * <code>freeWidth</code> and <code>freeHeight</code>
+ * should be split horizontally.
+ *
+ * @param freeWidth The rectWidth of the free space after packing a rectangle
+ * @param freeHeight The rectHeight of the free space after packing a rectangle
+ * @param rectWidth The rectWidth of the rectangle that was packed in a cell
+ * @param rectHeight The rectHeight of the rectangle that was packed in a cell
+ */
+ boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight);
+ }
+
+ // Splits the free area horizontally to minimize the horizontal section area
+ private static class MinAreaSplitDecision implements SplitDecision {
+ @Override
+ public boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight) {
+ return rectWidth * freeHeight > freeWidth * rectHeight;
+ }
+ }
+
+ // Splits the free area horizontally to maximize the horizontal section area
+ private static class MaxAreaSplitDecision implements SplitDecision {
+ @Override
+ public boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight) {
+ return rectWidth * freeHeight <= freeWidth * rectHeight;
+ }
+ }
+
+ // Splits the free area horizontally if the horizontal axis is shorter
+ private static class ShorterFreeAxisSplitDecision implements SplitDecision {
+ @Override
+ public boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight) {
+ return freeWidth <= freeHeight;
+ }
+ }
+
+ // Splits the free area horizontally if the vertical axis is shorter
+ private static class LongerFreeAxisSplitDecision implements SplitDecision {
+ @Override
+ public boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight) {
+ return freeWidth > freeHeight;
+ }
+ }
+
+ /**
+ * Attempts to pack a rectangle of specified dimensions in the available
+ * empty space.
+ *
+ * @param cell The cell representing free space in which to pack the rectangle
+ * @param prev The previous cell in the free space linked list
+ * @param width The width of the rectangle to pack
+ * @param height The height of the rectangle to pack
+ * @param entry Stores the location of the packged rectangle, if it fits
+ *
+ * @return True if the rectangle was packed in the atlas, false otherwise
+ */
+ @SuppressWarnings("SuspiciousNameCombination")
+ private boolean insert(Cell cell, Cell prev, int width, int height, Entry entry) {
+ boolean rotated = false;
+
+ // If the rectangle doesn't fit we'll try to rotate it
+ // if possible before giving up
+ if (cell.width < width || cell.height < height) {
+ if (mAllowRotation) {
+ if (cell.width < height || cell.height < width) {
+ return false;
+ }
+
+ // Rotate the rectangle
+ int temp = width;
+ width = height;
+ height = temp;
+ rotated = true;
+ } else {
+ return false;
+ }
+ }
+
+ // Remaining free space after packing the rectangle
+ int deltaWidth = cell.width - width;
+ int deltaHeight = cell.height - height;
+
+ // Split the remaining free space into two new cells
+ Cell first = new Cell();
+ Cell second = new Cell();
+
+ first.x = cell.x + width + mPadding;
+ first.y = cell.y;
+ first.width = deltaWidth - mPadding;
+
+ second.x = cell.x;
+ second.y = cell.y + height + mPadding;
+ second.height = deltaHeight - mPadding;
+
+ if (mSplitDecision.splitHorizontal(deltaWidth, deltaHeight,
+ width, height)) {
+ first.height = height;
+ second.width = cell.width;
+ } else {
+ first.height = cell.height;
+ second.width = width;
+
+ // The order of the cells matters for efficient packing
+ // We want to give priority to the cell chosen by the
+ // split decision heuristic
+ Cell temp = first;
+ first = second;
+ second = temp;
+ }
+
+ // Remove degenerate cases to keep the free list as small as possible
+ if (first.width > 0 && first.height > 0) {
+ prev.next = first;
+ prev = first;
+ }
+
+ if (second.width > 0 && second.height > 0) {
+ prev.next = second;
+ second.next = cell.next;
+ } else {
+ prev.next = cell.next;
+ }
+
+ // The cell is now completely removed from the free list
+ cell.next = null;
+
+ // Return the location and rotation of the packed rectangle
+ entry.x = cell.x;
+ entry.y = cell.y;
+ entry.rotated = rotated;
+
+ return true;
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 89abdef..50231da 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -46,10 +46,11 @@
/**
* Backing buffer for the Bitmap.
* Made public for quick access from drawing methods -- do NOT modify
- * from outside this class.
+ * from outside this class
*
* @hide
*/
+ @SuppressWarnings("UnusedDeclaration") // native code only
public byte[] mBuffer;
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources
@@ -88,26 +89,20 @@
}
/**
- * @noinspection UnusedDeclaration
+ * Private constructor that must received an already allocated native
+ * bitmap int (pointer).
*/
- /* Private constructor that must received an already allocated native
- bitmap int (pointer).
-
- This can be called from JNI code.
- */
+ @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
int density) {
this(nativeBitmap, buffer, isMutable, ninePatchChunk, null, density);
}
/**
- * @noinspection UnusedDeclaration
+ * Private constructor that must received an already allocated native bitmap
+ * int (pointer).
*/
- /* Private constructor that must received an already allocated native
- bitmap int (pointer).
-
- This can be called from JNI code.
- */
+ @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
int[] layoutBounds, int density) {
if (nativeBitmap == 0) {
@@ -128,6 +123,14 @@
}
/**
+ * Native bitmap has been reconfigured, so discard cached width/height
+ */
+ @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
+ void reinit() {
+ mWidth = mHeight = -1;
+ }
+
+ /**
* <p>Returns the density for this bitmap.</p>
*
* <p>The default density is the same density as the current display,
@@ -457,7 +460,7 @@
/**
* Creates a new bitmap, scaled from an existing bitmap, when possible. If the
* specified width and height are the same as the current width and height of
- * the source btimap, the source bitmap is returned and now new bitmap is
+ * the source bitmap, the source bitmap is returned and no new bitmap is
* created.
*
* @param src The source bitmap.
@@ -701,12 +704,10 @@
if (config == Config.ARGB_8888 && !hasAlpha) {
nativeErase(bm.mNativeBitmap, 0xff000000);
nativeSetHasAlpha(bm.mNativeBitmap, hasAlpha);
- } else {
- // No need to initialize it to zeroes; it is backed by a VM byte array
- // which is by definition preinitialized to all zeroes.
- //
- //nativeErase(bm.mNativeBitmap, 0);
}
+ // No need to initialize the bitmap to zeroes with other configs;
+ // it is backed by a VM byte array which is by definition preinitialized
+ // to all zeroes.
return bm;
}
@@ -994,6 +995,10 @@
* getPixels() or setPixels(), then the pixels are uniformly treated as
* 32bit values, packed according to the Color class.
*
+ * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, this method
+ * should not be used to calculate the memory usage of the bitmap. Instead,
+ * see {@link #getAllocationByteCount()}.
+ *
* @return number of bytes between rows of the native bitmap pixels.
*/
public final int getRowBytes() {
@@ -1001,7 +1006,11 @@
}
/**
- * Returns the number of bytes used to store this bitmap's pixels.
+ * Returns the minimum number of bytes that can be used to store this bitmap's pixels.
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, the result of this method can
+ * no longer be used to determine memory usage of a bitmap. See {@link
+ * #getAllocationByteCount()}.
*/
public final int getByteCount() {
// int result permits bitmaps up to 46,340 x 46,340
@@ -1009,6 +1018,20 @@
}
/**
+ * Returns the size of the allocated memory used to store this bitmap's pixels.
+ *
+ * <p>This can be larger than the result of {@link #getByteCount()} if a bitmap is reused to
+ * decode other bitmaps of smaller size. See {@link BitmapFactory.Options#inBitmap inBitmap in
+ * BitmapFactory.Options}. If a bitmap is not reused in this way, this value will be the same as
+ * that returned by {@link #getByteCount()}.
+ *
+ * <p>This value will not change over the lifetime of a Bitmap.
+ */
+ public final int getAllocationByteCount() {
+ return mBuffer.length;
+ }
+
+ /**
* If the bitmap's internal config is in one of the public formats, return
* that config, otherwise return null.
*/
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index f155cd2..deccac1 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -50,20 +50,30 @@
* reuse this bitmap when loading content. If the decode operation cannot
* use this bitmap, the decode method will return <code>null</code> and
* will throw an IllegalArgumentException. The current implementation
- * necessitates that the reused bitmap be mutable and of the same size as the
- * source content. The source content must be in jpeg or png format (whether as
- * a resource or as a stream). The {@link android.graphics.Bitmap.Config
- * configuration} of the reused bitmap will override the setting of
- * {@link #inPreferredConfig}, if set. The reused bitmap will continue to
- * remain mutable even when decoding a resource which would normally result
- * in an immutable bitmap.
+ * necessitates that the reused bitmap be mutable, and the resulting
+ * reused bitmap will continue to remain mutable even when decoding a
+ * resource which would normally result in an immutable bitmap.
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, any mutable
+ * bitmap can be reused to decode any other bitmaps as long as the resulting
+ * {@link Bitmap#getByteCount() byte count} of the decoded bitmap is less
+ * than or equal to the {@link Bitmap#getAllocationByteCount() allocated byte count}
+ * of the reused bitmap. This can be because the intrinsic size is smaller,
+ * or the size after density / sampled size scaling is smaller.
+ *
+ * <p>Prior to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE} additional
+ * constraints apply: The image being decoded (whether as a resource or
+ * as a stream) must be in jpeg or png format. Only equal sized bitmaps
+ * are supported, with {@link #inSampleSize} set to 1. Additionally, the
+ * {@link android.graphics.Bitmap.Config configuration} of the reused
+ * bitmap will override the setting of {@link #inPreferredConfig}, if set.
*
* <p>You should still always use the returned Bitmap of the decode
* method and not assume that reusing the bitmap worked, due to the
* constraints outlined above and failure situations that can occur.
* Checking whether the return value matches the value of the inBitmap
- * set in the Options structure is a way to see if the bitmap was reused,
- * but in all cases you should use the returned Bitmap to make sure
+ * set in the Options structure will indicate if the bitmap was reused,
+ * but in all cases you should use the Bitmap returned by the decoding function to ensure
* that you are using the bitmap that was used as the decode destination.</p>
*/
public Bitmap inBitmap;
@@ -431,6 +441,7 @@
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
+ setDensityFromOptions(bm, opts);
return bm;
}
@@ -448,6 +459,31 @@
}
/**
+ * Set the newly decoded bitmap's density based on the Options.
+ */
+ private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
+ if (outputBitmap == null || opts == null) return;
+
+ final int density = opts.inDensity;
+ if (density != 0) {
+ outputBitmap.setDensity(density);
+ final int targetDensity = opts.inTargetDensity;
+ if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
+ return;
+ }
+
+ byte[] np = outputBitmap.getNinePatchChunk();
+ final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
+ if (opts.inScaled || isNinePatch) {
+ outputBitmap.setDensity(targetDensity);
+ }
+ } else if (opts.inBitmap != null) {
+ // bitmap was reused, ensure density is reset
+ outputBitmap.setDensity(Bitmap.getDefaultDensity());
+ }
+ }
+
+ /**
* Decode an input stream into a bitmap. If the input stream is null, or
* cannot be used to decode a bitmap, the function returns null.
* The stream's position will be where ever it was after the encoded data
@@ -488,25 +524,7 @@
if (is instanceof AssetManager.AssetInputStream) {
final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
-
- if (opts == null || (opts.inScaled && opts.inBitmap == null)) {
- float scale = 1.0f;
- int targetDensity = 0;
- if (opts != null) {
- final int density = opts.inDensity;
- targetDensity = opts.inTargetDensity;
- if (density != 0 && targetDensity != 0) {
- scale = targetDensity / (float) density;
- }
- }
-
- bm = nativeDecodeAsset(asset, outPadding, opts, true, scale);
- if (bm != null && targetDensity != 0) bm.setDensity(targetDensity);
-
- finish = false;
- } else {
- bm = nativeDecodeAsset(asset, outPadding, opts);
- }
+ bm = nativeDecodeAsset(asset, outPadding, opts);
} else {
// pass some temp storage down to the native code. 1024 is made up,
// but should be large enough to avoid too many small calls back
@@ -514,81 +532,18 @@
// to mark(...) above.
byte [] tempStorage = null;
if (opts != null) tempStorage = opts.inTempStorage;
- if (tempStorage == null) tempStorage = new byte[16 * 1024];
-
- if (opts == null || (opts.inScaled && opts.inBitmap == null)) {
- float scale = 1.0f;
- int targetDensity = 0;
- if (opts != null) {
- final int density = opts.inDensity;
- targetDensity = opts.inTargetDensity;
- if (density != 0 && targetDensity != 0) {
- scale = targetDensity / (float) density;
- }
- }
-
- bm = nativeDecodeStream(is, tempStorage, outPadding, opts, true, scale);
- if (bm != null && targetDensity != 0) bm.setDensity(targetDensity);
-
- finish = false;
- } else {
- bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
- }
+ if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
+ bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
}
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
- return finish ? finishDecode(bm, outPadding, opts) : bm;
- }
-
- private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
- if (bm == null || opts == null) {
- return bm;
- }
-
- final int density = opts.inDensity;
- if (density == 0) {
- return bm;
- }
-
- bm.setDensity(density);
- final int targetDensity = opts.inTargetDensity;
- if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
- return bm;
- }
- byte[] np = bm.getNinePatchChunk();
- int[] lb = bm.getLayoutBounds();
- final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
- if (opts.inScaled || isNinePatch) {
- float scale = targetDensity / (float) density;
- if (scale != 1.0f) {
- final Bitmap oldBitmap = bm;
- bm = Bitmap.createScaledBitmap(oldBitmap,
- Math.max(1, (int) (bm.getWidth() * scale + 0.5f)),
- Math.max(1, (int) (bm.getHeight() * scale + 0.5f)), true);
- if (bm != oldBitmap) oldBitmap.recycle();
-
- if (isNinePatch) {
- np = nativeScaleNinePatch(np, scale, outPadding);
- bm.setNinePatchChunk(np);
- }
- if (lb != null) {
- int[] newLb = new int[lb.length];
- for (int i=0; i<lb.length; i++) {
- newLb[i] = (int)((lb[i]*scale)+.5f);
- }
- bm.setLayoutBounds(newLb);
- }
- }
-
- bm.setDensity(targetDensity);
- }
-
+ setDensityFromOptions(bm, opts);
return bm;
}
-
+
/**
* Decode an input stream into a bitmap. If the input stream is null, or
* cannot be used to decode a bitmap, the function returns null.
@@ -623,7 +578,7 @@
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
- return finishDecode(bm, outPadding, opts);
+ return bm;
} else {
FileInputStream fis = new FileInputStream(fd);
try {
@@ -650,8 +605,6 @@
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
- private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
- Rect padding, Options opts, boolean applyScale, float scale);
private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
Rect padding, Options opts);
private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts);
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index c851844..14ac901 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -37,12 +37,14 @@
* Canvas and Drawables</a> developer guide.</p></div>
*/
public class Canvas {
+
// assigned in constructors or setBitmap, freed in finalizer
- int mNativeCanvas;
-
+ /** @hide */
+ public int mNativeCanvas;
+
// may be null
private Bitmap mBitmap;
-
+
// optional field set by the caller
private DrawFilter mDrawFilter;
@@ -59,7 +61,7 @@
protected int mScreenDensity = Bitmap.DENSITY_NONE;
// Used by native code
- @SuppressWarnings({"UnusedDeclaration"})
+ @SuppressWarnings("UnusedDeclaration")
private int mSurfaceFormat;
/**
@@ -79,10 +81,9 @@
private static final int MAXMIMUM_BITMAP_SIZE = 32766;
// This field is used to finalize the native Canvas properly
- @SuppressWarnings({"UnusedDeclaration"})
private final CanvasFinalizer mFinalizer;
- private static class CanvasFinalizer {
+ private static final class CanvasFinalizer {
private int mNativeCanvas;
public CanvasFinalizer(int nativeCanvas) {
@@ -92,13 +93,18 @@
@Override
protected void finalize() throws Throwable {
try {
- if (mNativeCanvas != 0) {
- finalizer(mNativeCanvas);
- }
+ dispose();
} finally {
super.finalize();
}
}
+
+ public void dispose() {
+ if (mNativeCanvas != 0) {
+ finalizer(mNativeCanvas);
+ mNativeCanvas = 0;
+ }
+ }
}
/**
@@ -132,13 +138,14 @@
mBitmap = bitmap;
mDensity = bitmap.mDensity;
}
-
- Canvas(int nativeCanvas) {
+
+ /** @hide */
+ public Canvas(int nativeCanvas) {
if (nativeCanvas == 0) {
throw new IllegalStateException();
}
mNativeCanvas = nativeCanvas;
- mFinalizer = new CanvasFinalizer(nativeCanvas);
+ mFinalizer = new CanvasFinalizer(mNativeCanvas);
mDensity = Bitmap.getDefaultDensity();
}
@@ -155,7 +162,18 @@
}
finalizer(oldCanvas);
}
-
+
+ /**
+ * Gets the native canvas pointer.
+ *
+ * @return The native pointer.
+ *
+ * @hide
+ */
+ public int getNativeCanvas() {
+ return mNativeCanvas;
+ }
+
/**
* Returns null.
*
@@ -364,8 +382,8 @@
*/
public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
return native_saveLayer(mNativeCanvas, bounds,
- paint != null ? paint.mNativePaint : 0,
- saveFlags);
+ paint != null ? paint.mNativePaint : 0,
+ saveFlags);
}
/**
@@ -374,8 +392,8 @@
public int saveLayer(float left, float top, float right, float bottom, Paint paint,
int saveFlags) {
return native_saveLayer(mNativeCanvas, left, top, right, bottom,
- paint != null ? paint.mNativePaint : 0,
- saveFlags);
+ paint != null ? paint.mNativePaint : 0,
+ saveFlags);
}
/**
@@ -1022,7 +1040,7 @@
throw new NullPointerException();
}
native_drawArc(mNativeCanvas, oval, startAngle, sweepAngle,
- useCenter, paint.mNativePaint);
+ useCenter, paint.mNativePaint);
}
/**
@@ -1062,18 +1080,29 @@
/**
* Draws the specified bitmap as an N-patch (most often, a 9-patches.)
*
- * Note: Only supported by hardware accelerated canvas at the moment.
- *
- * @param bitmap The bitmap to draw as an N-patch
- * @param chunks The patches information (matches the native struct Res_png_9patch)
+ * @param patch The ninepatch object to render
* @param dst The destination rectangle.
* @param paint The paint to draw the bitmap with. may be null
*
* @hide
*/
- public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
- }
-
+ public void drawPatch(NinePatch patch, Rect dst, Paint paint) {
+ patch.drawSoftware(this, dst, paint);
+ }
+
+ /**
+ * Draws the specified bitmap as an N-patch (most often, a 9-patches.)
+ *
+ * @param patch The ninepatch object to render
+ * @param dst The destination rectangle.
+ * @param paint The paint to draw the bitmap with. may be null
+ *
+ * @hide
+ */
+ public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
+ patch.drawSoftware(this, dst, paint);
+ }
+
/**
* Draw the specified bitmap, with its top/left corner at (x,y), using
* the specified paint, transformed by the current matrix.
@@ -1158,7 +1187,7 @@
}
throwIfRecycled(bitmap);
native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst,
- paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
+ paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
}
/**
@@ -1279,8 +1308,8 @@
checkRange(colors.length, colorOffset, count);
}
nativeDrawBitmapMesh(mNativeCanvas, bitmap.ni(), meshWidth, meshHeight,
- verts, vertOffset, colors, colorOffset,
- paint != null ? paint.mNativePaint : 0);
+ verts, vertOffset, colors, colorOffset,
+ paint != null ? paint.mNativePaint : 0);
}
public enum VertexMode {
@@ -1342,8 +1371,8 @@
checkRange(indices.length, indexOffset, indexCount);
}
nativeDrawVertices(mNativeCanvas, mode.nativeInt, vertexCount, verts,
- vertOffset, texs, texOffset, colors, colorOffset,
- indices, indexOffset, indexCount, paint.mNativePaint);
+ vertOffset, texs, texOffset, colors, colorOffset,
+ indices, indexOffset, indexCount, paint.mNativePaint);
}
/**
@@ -1414,10 +1443,10 @@
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
native_drawText(mNativeCanvas, text.toString(), start, end, x, y,
- paint.mBidiFlags, paint.mNativePaint);
+ paint.mBidiFlags, paint.mNativePaint);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawText(this, start, end, x, y,
- paint);
+ paint);
} else {
char[] buf = TemporaryBuffer.obtain(end - start);
TextUtils.getChars(text, start, end, buf, 0);
@@ -1538,7 +1567,7 @@
throw new IndexOutOfBoundsException();
}
native_drawPosText(mNativeCanvas, text, index, count, pos,
- paint.mNativePaint);
+ paint.mNativePaint);
}
/**
@@ -1579,8 +1608,8 @@
throw new ArrayIndexOutOfBoundsException();
}
native_drawTextOnPath(mNativeCanvas, text, index, count,
- path.ni(), hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
+ path.ni(), hOffset, vOffset,
+ paint.mBidiFlags, paint.mNativePaint);
}
/**
@@ -1647,6 +1676,15 @@
}
/**
+ * Releases the resources associated with this canvas.
+ *
+ * @hide
+ */
+ public void release() {
+ mFinalizer.dispose();
+ }
+
+ /**
* Free up as much memory as possible from private caches (e.g. fonts, images)
*
* @hide
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index f6b747a..d64006d 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -73,6 +73,66 @@
public static final int YV12 = 0x32315659;
/**
+ * <p>Android Y8 format.</p>
+ *
+ * <p>Y8 is a YUV planar format comprised of a WxH Y plane only, with each pixel
+ * being represented by 8 bits. It is equivalent to just the Y plane from {@link #YV12}
+ * format.</p>
+ *
+ * <p>This format assumes
+ * <ul>
+ * <li>an even width</li>
+ * <li>an even height</li>
+ * <li>a horizontal stride multiple of 16 pixels</li>
+ * </ul>
+ * </p>
+ *
+ * <pre> y_size = stride * height </pre>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.hardware.photography.CameraDevice}
+ * through a {@link android.media.ImageReader} object if this format is
+ * supported by {@link android.hardware.photography.CameraDevice}.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.photography.CameraDevice
+ *
+ * @hide
+ */
+ public static final int Y8 = 0x20203859;
+
+ /**
+ * <p>Android Y16 format.</p>
+ *
+ * Y16 is a YUV planar format comprised of a WxH Y plane, with each pixel
+ * being represented by 16 bits. It is just like {@link #Y8}, but has 16
+ * bits per pixel (little endian).</p>
+ *
+ * <p>This format assumes
+ * <ul>
+ * <li>an even width</li>
+ * <li>an even height</li>
+ * <li>a horizontal stride multiple of 16 pixels</li>
+ * </ul>
+ * </p>
+ *
+ * <pre> y_size = stride * height </pre>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.hardware.photography.CameraDevice}
+ * through a {@link android.media.ImageReader} object if this format is
+ * supported by {@link android.hardware.photography.CameraDevice}.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.photography.CameraDevice
+ *
+ * @hide
+ */
+ public static final int Y16 = 0x20363159;
+
+ /**
* YCbCr format, used for video. Whether this format is supported by the
* camera hardware can be determined by
* {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
@@ -100,6 +160,41 @@
public static final int JPEG = 0x100;
/**
+ * <p>Multi-plane Android YUV format</p>
+ *
+ * <p>This format is a generic YCbCr format, capable of describing any 4:2:0
+ * chroma-subsampled planar or semiplanar buffer, with 8 bits per color
+ * sample.</p>
+ *
+ * <p>Images in this format are always represented by three separate buffers
+ * of data, one for each color plane. Additional information always
+ * accompanies the buffers, describing the row stride and the pixel stride
+ * for each plane.</p>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.hardware.photography.CameraDevice}
+ * through a {@link android.media.ImageReader} object.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.photography.CameraDevice
+ */
+ public static final int YUV_420_888 = 0x23;
+
+ /**
+ * <p>General raw camera sensor image format, usually representing a
+ * single-channel Bayer-mosaic image. Each pixel color sample is stored with
+ * 16 bits of precision.</p>
+ *
+ * <p>The layout of the color mosaic, the maximum and minimum encoding
+ * values of the raw pixel data, the color space of the image, and all other
+ * needed information to interpret a raw sensor image must be queried from
+ * the {@link android.hardware.photography.CameraDevice} which produced the
+ * image.</p>
+ */
+ public static final int RAW_SENSOR = 0x20;
+
+ /**
* Raw bayer format used for images, which is 10 bit precision samples
* stored in 16 bit words. The filter pattern is RGGB. Whether this format
* is supported by the camera hardware can be determined by
@@ -127,8 +222,16 @@
return 16;
case YV12:
return 12;
+ case Y8:
+ return 8;
+ case Y16:
+ return 16;
case NV21:
return 12;
+ case YUV_420_888:
+ return 12;
+ case RAW_SENSOR:
+ return 16;
case BAYER_RGGB:
return 16;
}
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index a837294..4e06448 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -267,13 +267,23 @@
native_set(native_instance, src.native_instance);
}
}
-
+
/** Returns true iff obj is a Matrix and its values equal our values.
*/
+ @Override
public boolean equals(Object obj) {
- return obj != null &&
- obj instanceof Matrix &&
- native_equals(native_instance, ((Matrix)obj).native_instance);
+ //if (obj == this) return true; -- NaN value would mean matrix != itself
+ if (!(obj instanceof Matrix)) return false;
+ return native_equals(native_instance, ((Matrix)obj).native_instance);
+ }
+
+ @Override
+ public int hashCode() {
+ // This should generate the hash code by performing some arithmetic operation on all
+ // the matrix elements -- our equals() does an element-by-element comparison, and we
+ // need to ensure that the hash code for two equal objects is the same. We're not
+ // really using this at the moment, so we take the easy way out.
+ return 44;
}
/** Set the matrix to identity */
@@ -512,7 +522,7 @@
*/
END (3);
- // the native values must match those in SkMatrix.h
+ // the native values must match those in SkMatrix.h
ScaleToFit(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -535,7 +545,7 @@
}
return native_setRectToRect(native_instance, src, dst, stf.nativeInt);
}
-
+
// private helper to perform range checks on arrays of "points"
private static void checkPointArrays(float[] src, int srcIndex,
float[] dst, int dstIndex,
@@ -598,7 +608,7 @@
native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
pointCount, true);
}
-
+
/**
* Apply this matrix to the array of 2D vectors specified by src, and write
* the transformed vectors into the array of vectors specified by dst. The
@@ -620,7 +630,7 @@
native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
vectorCount, false);
}
-
+
/**
* Apply this matrix to the array of 2D points specified by src, and write
* the transformed points into the array of points specified by dst. The
@@ -713,7 +723,7 @@
public float mapRadius(float radius) {
return native_mapRadius(native_instance, radius);
}
-
+
/** Copy 9 values from the matrix into the array.
*/
public void getValues(float[] values) {
@@ -736,13 +746,14 @@
native_setValues(native_instance, values);
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
sb.append("Matrix{");
toShortString(sb);
sb.append('}');
return sb.toString();
-
+
}
public String toShortString() {
@@ -780,13 +791,18 @@
pw.print(values[5]); pw.print("][");
pw.print(values[6]); pw.print(", "); pw.print(values[7]); pw.print(", ");
pw.print(values[8]); pw.print(']');
-
+
}
+ @Override
protected void finalize() throws Throwable {
- finalizer(native_instance);
+ try {
+ finalizer(native_instance);
+ } finally {
+ super.finalize();
+ }
}
-
+
/*package*/ final int ni() {
return native_instance;
}
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 6de4d84..528d9de 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -18,11 +18,8 @@
/**
- * The NinePatch class permits drawing a bitmap in nine sections.
- * The four corners are unscaled; the four edges are scaled in one axis,
- * and the middle is scaled in both axes. Normally, the middle is
- * transparent so that the patch can provide a selection about a rectangle.
- * Essentially, it allows the creation of custom graphics that will scale the
+ * The NinePatch class permits drawing a bitmap in nine or more sections.
+ * Essentially, it allows the creation of custom graphics that will scale the
* way that you define, when content added within the image exceeds the normal
* bounds of the graphic. For a thorough explanation of a NinePatch image,
* read the discussion in the
@@ -36,24 +33,40 @@
*/
public class NinePatch {
private final Bitmap mBitmap;
- private final byte[] mChunk;
+
+ /**
+ * Used by native code. This pointer is an instance of Res_png_9patch*.
+ *
+ * @hide
+ */
+ public final int mNativeChunk;
+
private Paint mPaint;
- private String mSrcName; // Useful for debugging
- private final RectF mRect = new RectF();
-
+ private String mSrcName;
+
+ /**
+ * Create a drawable projection from a bitmap to nine patches.
+ *
+ * @param bitmap The bitmap describing the patches.
+ * @param chunk The 9-patch data chunk describing how the underlying bitmap
+ * is split apart and drawn.
+ */
+ public NinePatch(Bitmap bitmap, byte[] chunk) {
+ this(bitmap, chunk, null);
+ }
+
/**
* Create a drawable projection from a bitmap to nine patches.
*
- * @param bitmap The bitmap describing the patches.
- * @param chunk The 9-patch data chunk describing how the underlying
- * bitmap is split apart and drawn.
- * @param srcName The name of the source for the bitmap. Might be null.
+ * @param bitmap The bitmap describing the patches.
+ * @param chunk The 9-patch data chunk describing how the underlying
+ * bitmap is split apart and drawn.
+ * @param srcName The name of the source for the bitmap. Might be null.
*/
public NinePatch(Bitmap bitmap, byte[] chunk, String srcName) {
mBitmap = bitmap;
- mChunk = chunk;
mSrcName = srcName;
- validateNinePatchChunk(mBitmap.ni(), chunk);
+ mNativeChunk = validateNinePatchChunk(mBitmap.ni(), chunk);
}
/**
@@ -61,69 +74,103 @@
*/
public NinePatch(NinePatch patch) {
mBitmap = patch.mBitmap;
- mChunk = patch.mChunk;
mSrcName = patch.mSrcName;
if (patch.mPaint != null) {
mPaint = new Paint(patch.mPaint);
}
- validateNinePatchChunk(mBitmap.ni(), mChunk);
+ // No need to validate the 9patch chunk again, it was done by
+ // the instance we're copying from
+ mNativeChunk = patch.mNativeChunk;
}
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nativeFinalize(mNativeChunk);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Returns the name of this NinePatch object if one was specified
+ * when calling the constructor.
+ */
+ public String getName() {
+ return mSrcName;
+ }
+
+ /**
+ * Returns the paint used to draw this NinePatch. The paint can be null.
+ *
+ * @see #setPaint(Paint)
+ * @see #draw(Canvas, Rect)
+ * @see #draw(Canvas, RectF)
+ */
+ public Paint getPaint() {
+ return mPaint;
+ }
+
+ /**
+ * Sets the paint to use when drawing the NinePatch.
+ *
+ * @param p The paint that will be used to draw this NinePatch.
+ *
+ * @see #getPaint()
+ * @see #draw(Canvas, Rect)
+ * @see #draw(Canvas, RectF)
+ */
public void setPaint(Paint p) {
mPaint = p;
}
-
- /**
- * Draw a bitmap of nine patches.
- *
- * @param canvas A container for the current matrix and clip used to draw the bitmap.
- * @param location Where to draw the bitmap.
+
+ /**
+ * Returns the bitmap used to draw this NinePatch.
*/
- public void draw(Canvas canvas, RectF location) {
- if (!canvas.isHardwareAccelerated()) {
- nativeDraw(canvas.mNativeCanvas, location,
- mBitmap.ni(), mChunk,
- mPaint != null ? mPaint.mNativePaint : 0,
- canvas.mDensity, mBitmap.mDensity);
- } else {
- canvas.drawPatch(mBitmap, mChunk, location, mPaint);
- }
+ public Bitmap getBitmap() {
+ return mBitmap;
}
/**
- * Draw a bitmap of nine patches.
+ * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}.
*
- * @param canvas A container for the current matrix and clip used to draw the bitmap.
- * @param location Where to draw the bitmap.
+ * @param canvas A container for the current matrix and clip used to draw the NinePatch.
+ * @param location Where to draw the NinePatch.
*/
- public void draw(Canvas canvas, Rect location) {
- if (!canvas.isHardwareAccelerated()) {
- nativeDraw(canvas.mNativeCanvas, location,
- mBitmap.ni(), mChunk,
- mPaint != null ? mPaint.mNativePaint : 0,
- canvas.mDensity, mBitmap.mDensity);
- } else {
- mRect.set(location);
- canvas.drawPatch(mBitmap, mChunk, mRect, mPaint);
- }
+ public void draw(Canvas canvas, RectF location) {
+ canvas.drawPatch(this, location, mPaint);
}
/**
- * Draw a bitmap of nine patches.
+ * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}.
*
- * @param canvas A container for the current matrix and clip used to draw the bitmap.
- * @param location Where to draw the bitmap.
- * @param paint The Paint to draw through.
+ * @param canvas A container for the current matrix and clip used to draw the NinePatch.
+ * @param location Where to draw the NinePatch.
+ */
+ public void draw(Canvas canvas, Rect location) {
+ canvas.drawPatch(this, location, mPaint);
+ }
+
+ /**
+ * Draws the NinePatch. This method will ignore the paint returned
+ * by {@link #getPaint()} and use the specified paint instead.
+ *
+ * @param canvas A container for the current matrix and clip used to draw the NinePatch.
+ * @param location Where to draw the NinePatch.
+ * @param paint The Paint to draw through.
*/
public void draw(Canvas canvas, Rect location, Paint paint) {
- if (!canvas.isHardwareAccelerated()) {
- nativeDraw(canvas.mNativeCanvas, location,
- mBitmap.ni(), mChunk, paint != null ? paint.mNativePaint : 0,
- canvas.mDensity, mBitmap.mDensity);
- } else {
- mRect.set(location);
- canvas.drawPatch(mBitmap, mChunk, mRect, paint);
- }
+ canvas.drawPatch(this, location, paint);
+ }
+
+ void drawSoftware(Canvas canvas, RectF location, Paint paint) {
+ nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk,
+ paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
+ }
+
+ void drawSoftware(Canvas canvas, Rect location, Paint paint) {
+ nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk,
+ paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
}
/**
@@ -133,33 +180,67 @@
public int getDensity() {
return mBitmap.mDensity;
}
-
+
+ /**
+ * Returns the intrinsic width, in pixels, of this NinePatch. This is equivalent
+ * to querying the width of the underlying bitmap returned by {@link #getBitmap()}.
+ */
public int getWidth() {
return mBitmap.getWidth();
}
+ /**
+ * Returns the intrinsic height, in pixels, of this NinePatch. This is equivalent
+ * to querying the height of the underlying bitmap returned by {@link #getBitmap()}.
+ */
public int getHeight() {
return mBitmap.getHeight();
}
+ /**
+ * Indicates whether this NinePatch contains transparent or translucent pixels.
+ * This is equivalent to calling <code>getBitmap().hasAlpha()</code> on this
+ * NinePatch.
+ */
public final boolean hasAlpha() {
return mBitmap.hasAlpha();
}
- public final Region getTransparentRegion(Rect location) {
- int r = nativeGetTransparentRegion(mBitmap.ni(), mChunk, location);
+ /**
+ * Returns a {@link Region} representing the parts of the NinePatch that are
+ * completely transparent.
+ *
+ * @param bounds The location and size of the NinePatch.
+ *
+ * @return null if the NinePatch has no transparent region to
+ * report, else a {@link Region} holding the parts of the specified bounds
+ * that are transparent.
+ */
+ public final Region getTransparentRegion(Rect bounds) {
+ int r = nativeGetTransparentRegion(mBitmap.ni(), mNativeChunk, bounds);
return r != 0 ? new Region(r) : null;
}
-
+
+ /**
+ * Verifies that the specified byte array is a valid 9-patch data chunk.
+ *
+ * @param chunk A byte array representing a 9-patch data chunk.
+ *
+ * @return True if the specified byte array represents a 9-patch data chunk,
+ * false otherwise.
+ */
public native static boolean isNinePatchChunk(byte[] chunk);
- private static native void validateNinePatchChunk(int bitmap, byte[] chunk);
+ /**
+ * Validates the 9-patch chunk and throws an exception if the chunk is invalid.
+ * If validation is successful, this method returns a native Res_png_9patch*
+ * object used by the renderers.
+ */
+ private static native int validateNinePatchChunk(int bitmap, byte[] chunk);
+ private static native void nativeFinalize(int chunk);
private static native void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
- byte[] c, int paint_instance_or_null,
- int destDensity, int srcDensity);
+ int c, int paint_instance_or_null, int destDensity, int srcDensity);
private static native void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance,
- byte[] c, int paint_instance_or_null,
- int destDensity, int srcDensity);
- private static native int nativeGetTransparentRegion(
- int bitmap, byte[] chunk, Rect location);
+ int c, int paint_instance_or_null, int destDensity, int srcDensity);
+ private static native int nativeGetTransparentRegion(int bitmap, int chunk, Rect location);
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index f0e9723..9accbbc 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -153,6 +153,11 @@
}
@Override
+ public int getAlpha() {
+ return mState.mDrawable.getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
mState.mDrawable.setColorFilter(cf);
}
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 7c7cd01..bde978d 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -167,7 +167,7 @@
* @return The Drawable at the specified frame index
*/
public Drawable getFrame(int index) {
- return mAnimationState.getChildren()[index];
+ return mAnimationState.getChild(index);
}
/**
@@ -322,7 +322,7 @@
mDurations = orig.mDurations;
mOneShot = orig.mOneShot;
} else {
- mDurations = new int[getChildren().length];
+ mDurations = new int[getCapacity()];
mOneShot = true;
}
}
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index a97ed2c..8689261 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -458,6 +458,11 @@
}
@Override
+ public int getAlpha() {
+ return mBitmapState.mPaint.getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
mBitmapState.mPaint.setColorFilter(cf);
invalidateSelf();
@@ -579,6 +584,11 @@
}
@Override
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
+
+ @Override
public Drawable newDrawable() {
return new BitmapDrawable(this, null);
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index eb9f046..2a9a14b 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -158,6 +158,11 @@
}
@Override
+ public int getAlpha() {
+ return mClipState.mDrawable.getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
mClipState.mDrawable.setColorFilter(cf);
}
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index d11e554..61dd675 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -116,6 +116,7 @@
*
* @return A value between 0 and 255.
*/
+ @Override
public int getAlpha() {
return mState.mUseColor >>> 24;
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index d5183d5..c8fce9e 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -411,6 +411,17 @@
public abstract void setAlpha(int alpha);
/**
+ * Gets the current alpha value for the drawable. 0 means fully transparent,
+ * 255 means fully opaque. This method is implemented by
+ * Drawable subclasses and the value returned is specific to how that class treats alpha.
+ * The default return value is 255 if the class does not override this method to return a value
+ * specific to its use of alpha.
+ */
+ public int getAlpha() {
+ return 0xFF;
+ }
+
+ /**
* Specify an optional colorFilter for the drawable. Pass null to remove
* any filters.
*/
@@ -858,10 +869,6 @@
drawable = new StateListDrawable();
} else if (name.equals("level-list")) {
drawable = new LevelListDrawable();
- /* Probably not doing this.
- } else if (name.equals("mipmap")) {
- drawable = new MipmapDrawable();
- */
} else if (name.equals("layer-list")) {
drawable = new LayerDrawable();
} else if (name.equals("transition")) {
@@ -985,6 +992,13 @@
* this drawable (and thus require completely reloading it).
*/
public abstract int getChangingConfigurations();
+
+ /**
+ * @hide
+ */
+ public Bitmap getBitmap() {
+ return null;
+ }
}
/**
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 0f84e86..f9cd4e2 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -23,6 +23,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemClock;
+import android.util.SparseArray;
/**
* A helper class that contains several {@link Drawable}s and selects which one to use.
@@ -76,7 +77,7 @@
| mDrawableContainerState.mChangingConfigurations
| mDrawableContainerState.mChildrenChangingConfigurations;
}
-
+
@Override
public boolean getPadding(Rect padding) {
final Rect r = mDrawableContainerState.getConstantPadding();
@@ -114,6 +115,11 @@
}
@Override
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @Override
public void setDither(boolean dither) {
if (mDrawableContainerState.mDither != dither) {
mDrawableContainerState.mDither = dither;
@@ -132,7 +138,7 @@
}
}
}
-
+
/**
* Change the global fade duration when a new drawable is entering
* the scene.
@@ -141,7 +147,7 @@
public void setEnterFadeDuration(int ms) {
mDrawableContainerState.mEnterFadeDuration = ms;
}
-
+
/**
* Change the global fade duration when a new drawable is leaving
* the scene.
@@ -150,7 +156,7 @@
public void setExitFadeDuration(int ms) {
mDrawableContainerState.mExitFadeDuration = ms;
}
-
+
@Override
protected void onBoundsChange(Rect bounds) {
if (mLastDrawable != null) {
@@ -160,12 +166,12 @@
mCurrDrawable.setBounds(bounds);
}
}
-
+
@Override
public boolean isStateful() {
return mDrawableContainerState.isStateful();
}
-
+
@Override
public void jumpToCurrentState() {
boolean changed = false;
@@ -228,7 +234,7 @@
}
return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1;
}
-
+
@Override
public int getMinimumWidth() {
if (mDrawableContainerState.isConstantSize()) {
@@ -245,18 +251,21 @@
return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0;
}
+ @Override
public void invalidateDrawable(Drawable who) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().invalidateDrawable(this);
}
}
+ @Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().scheduleDrawable(this, what, when);
}
}
+ @Override
public void unscheduleDrawable(Drawable who, Runnable what) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().unscheduleDrawable(this, what);
@@ -308,7 +317,7 @@
}
if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
- Drawable d = mDrawableContainerState.mDrawables[idx];
+ final Drawable d = mDrawableContainerState.getChild(idx);
mCurrDrawable = d;
mCurIndex = idx;
if (d != null) {
@@ -350,7 +359,7 @@
return true;
}
-
+
void animate(boolean schedule) {
final long now = SystemClock.uptimeMillis();
boolean animating = false;
@@ -410,11 +419,7 @@
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
- final int N = mDrawableContainerState.getChildCount();
- final Drawable[] drawables = mDrawableContainerState.getChildren();
- for (int i = 0; i < N; i++) {
- if (drawables[i] != null) drawables[i].mutate();
- }
+ mDrawableContainerState.mutate();
mMutated = true;
}
return this;
@@ -428,90 +433,105 @@
*/
public abstract static class DrawableContainerState extends ConstantState {
final DrawableContainer mOwner;
+ final Resources mRes;
- int mChangingConfigurations;
- int mChildrenChangingConfigurations;
-
- Drawable[] mDrawables;
- int mNumChildren;
+ SparseArray<ConstantStateFuture> mDrawableFutures;
- boolean mVariablePadding = false;
- Rect mConstantPadding = null;
+ int mChangingConfigurations;
+ int mChildrenChangingConfigurations;
- boolean mConstantSize = false;
- boolean mComputedConstantSize = false;
- int mConstantWidth;
- int mConstantHeight;
- int mConstantMinimumWidth;
- int mConstantMinimumHeight;
+ Drawable[] mDrawables;
+ int mNumChildren;
- int mOpacity;
+ boolean mVariablePadding;
+ boolean mPaddingChecked;
+ Rect mConstantPadding;
- boolean mHaveStateful = false;
- boolean mStateful;
+ boolean mConstantSize;
+ boolean mComputedConstantSize;
+ int mConstantWidth;
+ int mConstantHeight;
+ int mConstantMinimumWidth;
+ int mConstantMinimumHeight;
- boolean mCheckedConstantState;
- boolean mCanConstantState;
+ boolean mCheckedOpacity;
+ int mOpacity;
- boolean mPaddingChecked = false;
-
- boolean mDither = DEFAULT_DITHER;
+ boolean mCheckedStateful;
+ boolean mStateful;
- int mEnterFadeDuration;
- int mExitFadeDuration;
+ boolean mCheckedConstantState;
+ boolean mCanConstantState;
+
+ boolean mDither = DEFAULT_DITHER;
+
+ boolean mMutated;
+ int mLayoutDirection;
+
+ int mEnterFadeDuration;
+ int mExitFadeDuration;
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
+ mRes = res;
if (orig != null) {
mChangingConfigurations = orig.mChangingConfigurations;
mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
-
- final Drawable[] origDr = orig.mDrawables;
- mDrawables = new Drawable[origDr.length];
- mNumChildren = orig.mNumChildren;
+ mCheckedConstantState = true;
+ mCanConstantState = true;
- final int N = mNumChildren;
- for (int i=0; i<N; i++) {
- if (res != null) {
- mDrawables[i] = origDr[i].getConstantState().newDrawable(res);
- } else {
- mDrawables[i] = origDr[i].getConstantState().newDrawable();
- }
- mDrawables[i].setCallback(owner);
- mDrawables[i].setLayoutDirection(origDr[i].getLayoutDirection());
- }
-
- mCheckedConstantState = mCanConstantState = true;
mVariablePadding = orig.mVariablePadding;
- if (orig.mConstantPadding != null) {
- mConstantPadding = new Rect(orig.mConstantPadding);
- }
mConstantSize = orig.mConstantSize;
- mComputedConstantSize = orig.mComputedConstantSize;
- mConstantWidth = orig.mConstantWidth;
- mConstantHeight = orig.mConstantHeight;
- mConstantMinimumWidth = orig.mConstantMinimumWidth;
- mConstantMinimumHeight = orig.mConstantMinimumHeight;
-
- mOpacity = orig.mOpacity;
- mHaveStateful = orig.mHaveStateful;
- mStateful = orig.mStateful;
-
mDither = orig.mDither;
-
+ mMutated = orig.mMutated;
+ mLayoutDirection = orig.mLayoutDirection;
mEnterFadeDuration = orig.mEnterFadeDuration;
mExitFadeDuration = orig.mExitFadeDuration;
+ // Cloning the following values may require creating futures.
+ mConstantPadding = orig.getConstantPadding();
+ mPaddingChecked = true;
+
+ mConstantWidth = orig.getConstantWidth();
+ mConstantHeight = orig.getConstantHeight();
+ mConstantMinimumWidth = orig.getConstantMinimumWidth();
+ mConstantMinimumHeight = orig.getConstantMinimumHeight();
+ mComputedConstantSize = true;
+
+ mOpacity = orig.getOpacity();
+ mCheckedOpacity = true;
+
+ mStateful = orig.isStateful();
+ mCheckedStateful = true;
+
+ // Postpone cloning children and futures until we're absolutely
+ // sure that we're done computing values for the original state.
+ final Drawable[] origDr = orig.mDrawables;
+ mDrawables = new Drawable[origDr.length];
+ mNumChildren = orig.mNumChildren;
+
+ final SparseArray<ConstantStateFuture> origDf = orig.mDrawableFutures;
+ if (origDf != null) {
+ mDrawableFutures = origDf.clone();
+ } else {
+ mDrawableFutures = new SparseArray<ConstantStateFuture>(mNumChildren);
+ }
+
+ final int N = mNumChildren;
+ for (int i = 0; i < N; i++) {
+ if (origDr[i] != null) {
+ mDrawableFutures.put(i, new ConstantStateFuture(origDr[i]));
+ }
+ }
} else {
mDrawables = new Drawable[10];
mNumChildren = 0;
- mCheckedConstantState = mCanConstantState = false;
}
}
-
+
@Override
public int getChangingConfigurations() {
return mChangingConfigurations | mChildrenChangingConfigurations;
@@ -530,7 +550,8 @@
mDrawables[pos] = dr;
mNumChildren++;
mChildrenChangingConfigurations |= dr.getChangingConfigurations();
- mHaveStateful = false;
+ mCheckedStateful = false;
+ mCheckedOpacity = false;
mConstantPadding = null;
mPaddingChecked = false;
@@ -539,18 +560,89 @@
return pos;
}
+ final int getCapacity() {
+ return mDrawables.length;
+ }
+
+ private final void createAllFutures() {
+ if (mDrawableFutures != null) {
+ final int futureCount = mDrawableFutures.size();
+ for (int keyIndex = 0; keyIndex < futureCount; keyIndex++) {
+ final int index = mDrawableFutures.keyAt(keyIndex);
+ mDrawables[index] = mDrawableFutures.valueAt(keyIndex).get(this);
+ }
+
+ mDrawableFutures = null;
+ }
+ }
+
public final int getChildCount() {
return mNumChildren;
}
+ /*
+ * @deprecated Use {@link #getChild} instead.
+ */
public final Drawable[] getChildren() {
+ // Create all futures for backwards compatibility.
+ createAllFutures();
+
return mDrawables;
}
- /** A boolean value indicating whether to use the maximum padding value of
- * all frames in the set (false), or to use the padding value of the frame
- * being shown (true). Default value is false.
- */
+ public final Drawable getChild(int index) {
+ final Drawable result = mDrawables[index];
+ if (result != null) {
+ return result;
+ }
+
+ // Prepare future drawable if necessary.
+ if (mDrawableFutures != null) {
+ final int keyIndex = mDrawableFutures.indexOfKey(index);
+ if (keyIndex >= 0) {
+ final Drawable prepared = mDrawableFutures.valueAt(keyIndex).get(this);
+ mDrawables[index] = prepared;
+ mDrawableFutures.removeAt(keyIndex);
+ return prepared;
+ }
+ }
+
+ return null;
+ }
+
+ final void setLayoutDirection(int layoutDirection) {
+ // No need to call createAllFutures, since future drawables will
+ // change layout direction when they are prepared.
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ if (drawables[i] != null) {
+ drawables[i].setLayoutDirection(layoutDirection);
+ }
+ }
+
+ mLayoutDirection = layoutDirection;
+ }
+
+ final void mutate() {
+ // No need to call createAllFutures, since future drawables will
+ // mutate when they are prepared.
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ if (drawables[i] != null) {
+ drawables[i].mutate();
+ }
+ }
+
+ mMutated = true;
+ }
+
+ /**
+ * A boolean value indicating whether to use the maximum padding value
+ * of all frames in the set (false), or to use the padding value of the
+ * frame being shown (true). Default value is false.
+ */
public final void setVariablePadding(boolean variable) {
mVariablePadding = variable;
}
@@ -559,13 +651,16 @@
if (mVariablePadding) {
return null;
}
- if (mConstantPadding != null || mPaddingChecked) {
+
+ if ((mConstantPadding != null) || mPaddingChecked) {
return mConstantPadding;
}
+ createAllFutures();
+
Rect r = null;
final Rect t = new Rect();
- final int N = getChildCount();
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
if (drawables[i].getPadding(t)) {
@@ -576,6 +671,7 @@
if (t.bottom > r.bottom) r.bottom = t.bottom;
}
}
+
mPaddingChecked = true;
return (mConstantPadding = r);
}
@@ -623,12 +719,14 @@
protected void computeConstantSize() {
mComputedConstantSize = true;
- final int N = getChildCount();
+ createAllFutures();
+
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
mConstantWidth = mConstantHeight = -1;
mConstantMinimumWidth = mConstantMinimumHeight = 0;
for (int i = 0; i < N; i++) {
- Drawable dr = drawables[i];
+ final Drawable dr = drawables[i];
int s = dr.getIntrinsicWidth();
if (s > mConstantWidth) mConstantWidth = s;
s = dr.getIntrinsicHeight();
@@ -657,33 +755,45 @@
}
public final int getOpacity() {
- final int N = getChildCount();
+ if (mCheckedOpacity) {
+ return mOpacity;
+ }
+
+ createAllFutures();
+
+ mCheckedOpacity = true;
+
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
- int op = N > 0 ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
+ int op = (N > 0) ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
for (int i = 1; i < N; i++) {
op = Drawable.resolveOpacity(op, drawables[i].getOpacity());
}
+
mOpacity = op;
return op;
}
public final boolean isStateful() {
- if (mHaveStateful) {
+ if (mCheckedStateful) {
return mStateful;
}
-
- boolean stateful = false;
- final int N = getChildCount();
+
+ createAllFutures();
+
+ mCheckedStateful = true;
+
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
- if (mDrawables[i].isStateful()) {
- stateful = true;
- break;
+ if (drawables[i].isStateful()) {
+ mStateful = true;
+ return true;
}
}
-
- mStateful = stateful;
- mHaveStateful = true;
- return stateful;
+
+ mStateful = false;
+ return false;
}
public void growArray(int oldSize, int newSize) {
@@ -693,24 +803,60 @@
}
public synchronized boolean canConstantState() {
- if (!mCheckedConstantState) {
- mCanConstantState = true;
- final int N = mNumChildren;
- for (int i=0; i<N; i++) {
- if (mDrawables[i].getConstantState() == null) {
- mCanConstantState = false;
- break;
- }
- }
- mCheckedConstantState = true;
+ if (mCheckedConstantState) {
+ return mCanConstantState;
}
- return mCanConstantState;
+ createAllFutures();
+
+ mCheckedConstantState = true;
+
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ if (drawables[i].getConstantState() == null) {
+ mCanConstantState = false;
+ return false;
+ }
+ }
+
+ mCanConstantState = true;
+ return true;
+ }
+
+ /**
+ * Class capable of cloning a Drawable from another Drawable's
+ * ConstantState.
+ */
+ private static class ConstantStateFuture {
+ private final ConstantState mConstantState;
+
+ private ConstantStateFuture(Drawable source) {
+ mConstantState = source.getConstantState();
+ }
+
+ /**
+ * Obtains and prepares the Drawable represented by this future.
+ *
+ * @param state the container into which this future will be placed
+ * @return a prepared Drawable
+ */
+ public Drawable get(DrawableContainerState state) {
+ final Drawable result = (state.mRes == null) ?
+ mConstantState.newDrawable() : mConstantState.newDrawable(state.mRes);
+ result.setLayoutDirection(state.mLayoutDirection);
+ result.setCallback(state.mOwner);
+
+ if (state.mMutated) {
+ result.mutate();
+ }
+
+ return result;
+ }
}
}
- protected void setConstantState(DrawableContainerState state)
- {
+ protected void setConstantState(DrawableContainerState state) {
mDrawableContainerState = state;
}
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index b966bb4..d226c8c 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -639,6 +639,11 @@
}
@Override
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @Override
public void setDither(boolean dither) {
if (dither != mDither) {
mDither = dither;
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 2576f42e..6cc9d8f 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -192,7 +192,12 @@
public void setAlpha(int alpha) {
mInsetState.mDrawable.setAlpha(alpha);
}
-
+
+ @Override
+ public int getAlpha() {
+ return mInsetState.mDrawable.getAlpha();
+ }
+
@Override
public void setColorFilter(ColorFilter cf) {
mInsetState.mDrawable.setColorFilter(cf);
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 6b59dba..206897b 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -402,7 +402,18 @@
array[i].mDrawable.setAlpha(alpha);
}
}
-
+
+ @Override
+ public int getAlpha() {
+ final ChildDrawable[] array = mLayerState.mChildren;
+ if (mLayerState.mNum > 0) {
+ // All layers should have the same alpha set on them - just return the first one
+ return array[0].mDrawable.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+
@Override
public void setColorFilter(ColorFilter cf) {
final ChildDrawable[] array = mLayerState.mChildren;
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index 21be983..872fdce 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -164,8 +164,8 @@
mLows = orig.mLows;
mHighs = orig.mHighs;
} else {
- mLows = new int[getChildren().length];
- mHighs = new int[getChildren().length];
+ mLows = new int[getCapacity()];
+ mHighs = new int[getCapacity()];
}
}
diff --git a/graphics/java/android/graphics/drawable/MipmapDrawable.java b/graphics/java/android/graphics/drawable/MipmapDrawable.java
deleted file mode 100644
index cd39719..0000000
--- a/graphics/java/android/graphics/drawable/MipmapDrawable.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.drawable;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-
-import java.io.IOException;
-
-/**
- * @hide -- we are probably moving to do MipMaps in another way (more integrated
- * with the resource system).
- *
- * A resource that manages a number of alternate Drawables, and which actually draws the one which
- * size matches the most closely the drawing bounds. Providing several pre-scaled version of the
- * drawable helps minimizing the aliasing artifacts that can be introduced by the scaling.
- *
- * <p>
- * Use {@link #addDrawable(Drawable)} to define the different Drawables that will represent the
- * mipmap levels of this MipmapDrawable. The mipmap Drawable that will actually be used when this
- * MipmapDrawable is drawn is the one which has the smallest intrinsic height greater or equal than
- * the bounds' height. This selection ensures that the best available mipmap level is scaled down to
- * draw this MipmapDrawable.
- * </p>
- *
- * If the bounds' height is larger than the largest mipmap, the largest mipmap will be scaled up.
- * Note that Drawables without intrinsic height (i.e. with a negative value, such as Color) will
- * only be used if no other mipmap Drawable are provided. The Drawables' intrinsic heights should
- * not be changed after the Drawable has been added to this MipmapDrawable.
- *
- * <p>
- * The different mipmaps' parameters (opacity, padding, color filter, gravity...) should typically
- * be similar to ensure a continuous visual appearance when the MipmapDrawable is scaled. The aspect
- * ratio of the different mipmaps should especially be equal.
- * </p>
- *
- * A typical example use of a MipmapDrawable would be for an image which is intended to be scaled at
- * various sizes, and for which one wants to provide pre-scaled versions to precisely control its
- * appearance.
- *
- * <p>
- * The intrinsic size of a MipmapDrawable are inferred from those of the largest mipmap (in terms of
- * {@link Drawable#getIntrinsicHeight()}). On the opposite, its minimum
- * size is defined by the smallest provided mipmap.
- * </p>
-
- * It can be defined in an XML file with the <code><mipmap></code> element.
- * Each mipmap Drawable is defined in a nested <code><item></code>. For example:
- * <pre>
- * <mipmap xmlns:android="http://schemas.android.com/apk/res/android">
- * <item android:drawable="@drawable/my_image_8" />
- * <item android:drawable="@drawable/my_image_32" />
- * <item android:drawable="@drawable/my_image_128" />
- * </mipmap>
- *</pre>
- * <p>
- * With this XML saved into the res/drawable/ folder of the project, it can be referenced as
- * the drawable for an {@link android.widget.ImageView}. Assuming that the heights of the provided
- * drawables are respectively 8, 32 and 128 pixels, the first one will be scaled down when the
- * bounds' height is lower or equal than 8 pixels. The second drawable will then be used up to a
- * height of 32 pixels and the largest drawable will be used for greater heights.
- * </p>
- * @attr ref android.R.styleable#MipmapDrawableItem_drawable
- */
-public class MipmapDrawable extends DrawableContainer {
- private final MipmapContainerState mMipmapContainerState;
- private boolean mMutated;
-
- public MipmapDrawable() {
- this(null, null);
- }
-
- /**
- * Adds a Drawable to the list of available mipmap Drawables. The Drawable actually used when
- * this MipmapDrawable is drawn is determined from its bounds.
- *
- * This method has no effect if drawable is null.
- *
- * @param drawable The Drawable that will be added to list of available mipmap Drawables.
- */
-
- public void addDrawable(Drawable drawable) {
- if (drawable != null) {
- mMipmapContainerState.addDrawable(drawable);
- onDrawableAdded();
- }
- }
-
- private void onDrawableAdded() {
- // selectDrawable assumes that the container content does not change.
- // When a Drawable is added, the same index can correspond to a new Drawable, and since
- // selectDrawable has a fast exit case when oldIndex==newIndex, the new drawable could end
- // up not being used in place of the previous one if they happen to share the same index.
- // This make sure the new computed index can actually replace the previous one.
- selectDrawable(-1);
- onBoundsChange(getBounds());
- }
-
- // overrides from Drawable
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- final int index = mMipmapContainerState.indexForBounds(bounds);
-
- // Will call invalidateSelf() if needed
- selectDrawable(index);
-
- super.onBoundsChange(bounds);
- }
-
- @Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
- throws XmlPullParserException, IOException {
-
- super.inflate(r, parser, attrs);
-
- int type;
-
- final int innerDepth = parser.getDepth() + 1;
- int depth;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth
- || type != XmlPullParser.END_TAG)) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (depth > innerDepth || !parser.getName().equals("item")) {
- continue;
- }
-
- TypedArray a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.MipmapDrawableItem);
-
- int drawableRes = a.getResourceId(
- com.android.internal.R.styleable.MipmapDrawableItem_drawable, 0);
-
- a.recycle();
-
- Drawable dr;
- if (drawableRes != 0) {
- dr = r.getDrawable(drawableRes);
- } else {
- while ((type = parser.next()) == XmlPullParser.TEXT) {
- }
- if (type != XmlPullParser.START_TAG) {
- throw new XmlPullParserException(
- parser.getPositionDescription()
- + ": <item> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- dr = Drawable.createFromXmlInner(r, parser, attrs);
- }
-
- mMipmapContainerState.addDrawable(dr);
- }
-
- onDrawableAdded();
- }
-
- @Override
- public Drawable mutate() {
- if (!mMutated && super.mutate() == this) {
- mMipmapContainerState.mMipmapHeights = mMipmapContainerState.mMipmapHeights.clone();
- mMutated = true;
- }
- return this;
- }
-
- private final static class MipmapContainerState extends DrawableContainerState {
- private int[] mMipmapHeights;
-
- MipmapContainerState(MipmapContainerState orig, MipmapDrawable owner, Resources res) {
- super(orig, owner, res);
-
- if (orig != null) {
- mMipmapHeights = orig.mMipmapHeights;
- } else {
- mMipmapHeights = new int[getChildren().length];
- }
-
- // Change the default value
- setConstantSize(true);
- }
-
- /**
- * Returns the index of the child mipmap drawable that will best fit the provided bounds.
- * This index is determined by comparing bounds' height and children intrinsic heights.
- * The returned mipmap index is the smallest mipmap which height is greater or equal than
- * the bounds' height. If the bounds' height is larger than the largest mipmap, the largest
- * mipmap index is returned.
- *
- * @param bounds The bounds of the MipMapDrawable.
- * @return The index of the child Drawable that will best fit these bounds, or -1 if there
- * are no children mipmaps.
- */
- public int indexForBounds(Rect bounds) {
- final int boundsHeight = bounds.height();
- final int N = getChildCount();
- for (int i = 0; i < N; i++) {
- if (boundsHeight <= mMipmapHeights[i]) {
- return i;
- }
- }
-
- // No mipmap larger than bounds found. Use largest one which will be scaled up.
- if (N > 0) {
- return N - 1;
- }
- // No Drawable mipmap at all
- return -1;
- }
-
- /**
- * Adds a Drawable to the list of available mipmap Drawables. This list can be retrieved
- * using {@link DrawableContainer.DrawableContainerState#getChildren()} and this method
- * ensures that it is always sorted by increasing {@link Drawable#getIntrinsicHeight()}.
- *
- * @param drawable The Drawable that will be added to children list
- */
- public void addDrawable(Drawable drawable) {
- // Insert drawable in last position, correctly resetting cached values and
- // especially mComputedConstantSize
- int pos = addChild(drawable);
-
- // Bubble sort the last drawable to restore the sort by intrinsic height
- final int drawableHeight = drawable.getIntrinsicHeight();
-
- while (pos > 0) {
- final Drawable previousDrawable = mDrawables[pos-1];
- final int previousIntrinsicHeight = previousDrawable.getIntrinsicHeight();
-
- if (drawableHeight < previousIntrinsicHeight) {
- mDrawables[pos] = previousDrawable;
- mMipmapHeights[pos] = previousIntrinsicHeight;
-
- mDrawables[pos-1] = drawable;
- mMipmapHeights[pos-1] = drawableHeight;
- pos--;
- } else {
- break;
- }
- }
- }
-
- /**
- * Intrinsic sizes are those of the largest available mipmap.
- * Minimum sizes are those of the smallest available mipmap.
- */
- @Override
- protected void computeConstantSize() {
- final int N = getChildCount();
- if (N > 0) {
- final Drawable smallestDrawable = mDrawables[0];
- mConstantMinimumWidth = smallestDrawable.getMinimumWidth();
- mConstantMinimumHeight = smallestDrawable.getMinimumHeight();
-
- final Drawable largestDrawable = mDrawables[N-1];
- mConstantWidth = largestDrawable.getIntrinsicWidth();
- mConstantHeight = largestDrawable.getIntrinsicHeight();
- } else {
- mConstantWidth = mConstantHeight = -1;
- mConstantMinimumWidth = mConstantMinimumHeight = 0;
- }
- mComputedConstantSize = true;
- }
-
- @Override
- public Drawable newDrawable() {
- return new MipmapDrawable(this, null);
- }
-
- @Override
- public Drawable newDrawable(Resources res) {
- return new MipmapDrawable(this, res);
- }
-
- @Override
- public void growArray(int oldSize, int newSize) {
- super.growArray(oldSize, newSize);
- int[] newInts = new int[newSize];
- System.arraycopy(mMipmapHeights, 0, newInts, 0, oldSize);
- mMipmapHeights = newInts;
- }
- }
-
- private MipmapDrawable(MipmapContainerState state, Resources res) {
- MipmapContainerState as = new MipmapContainerState(state, this, res);
- mMipmapContainerState = as;
- setConstantState(as);
- onDrawableAdded();
- }
-}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index a9dc22b..86e4bad 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -52,7 +52,7 @@
*/
public class NinePatchDrawable extends Drawable {
// dithering helps a lot, and is pretty cheap, so default is true
- private static final boolean DEFAULT_DITHER = true;
+ private static final boolean DEFAULT_DITHER = false;
private NinePatchState mNinePatchState;
private NinePatch mNinePatch;
private Rect mPadding;
@@ -197,10 +197,8 @@
mBitmapHeight = mNinePatch.getHeight();
mOpticalInsets = mNinePatchState.mOpticalInsets;
} else {
- mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(),
- sdensity, tdensity);
- mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(),
- sdensity, tdensity);
+ mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(), sdensity, tdensity);
+ mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(), sdensity, tdensity);
if (mNinePatchState.mPadding != null && mPadding != null) {
Rect dest = mPadding;
Rect src = mNinePatchState.mPadding;
@@ -251,6 +249,15 @@
}
@Override
+ public int getAlpha() {
+ if (mPaint == null) {
+ // Fast common case -- normal alpha.
+ return 0xFF;
+ }
+ return getPaint().getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
if (mPaint == null && cf == null) {
// Fast common case -- leave at no color filter.
@@ -262,6 +269,7 @@
@Override
public void setDither(boolean dither) {
+ //noinspection PointlessBooleanExpression
if (mPaint == null && dither == DEFAULT_DITHER) {
// Fast common case -- leave at default dither.
return;
@@ -290,8 +298,7 @@
}
final boolean dither = a.getBoolean(
- com.android.internal.R.styleable.NinePatchDrawable_dither,
- DEFAULT_DITHER);
+ com.android.internal.R.styleable.NinePatchDrawable_dither, DEFAULT_DITHER);
final BitmapFactory.Options options = new BitmapFactory.Options();
if (dither) {
options.inDither = false;
@@ -321,8 +328,7 @@
": <nine-patch> requires a valid 9-patch source image");
}
- setNinePatchState(new NinePatchState(
- new NinePatch(bitmap, bitmap.getNinePatchChunk(), "XML 9-patch"),
+ setNinePatchState(new NinePatchState(new NinePatch(bitmap, bitmap.getNinePatchChunk()),
padding, opticalInsets, dither), r);
mNinePatchState.mTargetDensity = mTargetDensity;
@@ -394,7 +400,7 @@
return this;
}
- private final static class NinePatchState extends ConstantState {
+ final static class NinePatchState extends ConstantState {
final NinePatch mNinePatch;
final Rect mPadding;
final Insets mOpticalInsets;
@@ -420,7 +426,8 @@
// Copy constructor
NinePatchState(NinePatchState state) {
- mNinePatch = new NinePatch(state.mNinePatch);
+ // Note we don't copy the nine patch because it is immutable.
+ mNinePatch = state.mNinePatch;
// Note we don't copy the padding because it is immutable.
mPadding = state.mPadding;
mOpticalInsets = state.mOpticalInsets;
@@ -430,6 +437,11 @@
}
@Override
+ public Bitmap getBitmap() {
+ return mNinePatch.getBitmap();
+ }
+
+ @Override
public Drawable newDrawable() {
return new NinePatchDrawable(this, null);
}
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 83d9581..aec3a4b 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -108,6 +108,11 @@
mState.mDrawable.setAlpha(alpha);
}
+ @Override
+ public int getAlpha() {
+ return mState.mDrawable.getAlpha();
+ }
+
public void setColorFilter(ColorFilter cf) {
mState.mDrawable.setColorFilter(cf);
}
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 0664214..ec6b2c16 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -177,6 +177,11 @@
}
@Override
+ public int getAlpha() {
+ return mScaleState.mDrawable.getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
mScaleState.mDrawable.setColorFilter(cf);
}
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 1dbcddb..93f2dc60 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -252,7 +252,12 @@
mShapeState.mAlpha = alpha;
invalidateSelf();
}
-
+
+ @Override
+ public int getAlpha() {
+ return mShapeState.mAlpha;
+ }
+
@Override
public void setColorFilter(ColorFilter cf) {
mShapeState.mPaint.setColorFilter(cf);
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index f8f3ac9..e3c7bc5 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -228,7 +228,7 @@
* @see #getStateSet(int)
*/
public Drawable getStateDrawable(int index) {
- return mStateListState.getChildren()[index];
+ return mStateListState.getChild(index);
}
/**
@@ -264,11 +264,11 @@
/** @hide */
@Override
public void setLayoutDirection(int layoutDirection) {
- final int numStates = getStateCount();
- for (int i = 0; i < numStates; i++) {
- getStateDrawable(i).setLayoutDirection(layoutDirection);
- }
super.setLayoutDirection(layoutDirection);
+
+ // Let the container handle setting its own layout direction. Otherwise,
+ // we're accessing potentially unused states.
+ mStateListState.setLayoutDirection(layoutDirection);
}
static final class StateListState extends DrawableContainerState {
@@ -278,9 +278,9 @@
super(orig, owner, res);
if (orig != null) {
- mStateSets = orig.mStateSets;
+ mStateSets = Arrays.copyOf(orig.mStateSets, orig.mStateSets.length);
} else {
- mStateSets = new int[getChildren().length][];
+ mStateSets = new int[getCapacity()][];
}
}
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index b460a4d..362b586 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -28,6 +28,7 @@
import android.util.Log;
import android.util.TypedValue;
import android.graphics.Canvas;
+import android.os.Trace;
/**
* <p> This class provides the primary method through which data is passed to
@@ -352,6 +353,7 @@
*
*/
public void syncAll(int srcLocation) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "syncAll");
switch (srcLocation) {
case USAGE_GRAPHICS_TEXTURE:
case USAGE_SCRIPT:
@@ -372,6 +374,7 @@
}
mRS.validate();
mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -382,12 +385,14 @@
*
*/
public void ioSend() {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "ioSend");
if ((mUsage & USAGE_IO_OUTPUT) == 0) {
throw new RSIllegalArgumentException(
"Can only send buffer if IO_OUTPUT usage specified.");
}
mRS.validate();
mRS.nAllocationIoSend(getID(mRS));
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -404,12 +409,14 @@
*
*/
public void ioReceive() {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "ioReceive");
if ((mUsage & USAGE_IO_INPUT) == 0) {
throw new RSIllegalArgumentException(
"Can only receive if IO_INPUT usage specified.");
}
mRS.validate();
mRS.nAllocationIoReceive(getID(mRS));
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -418,6 +425,7 @@
* @param d Source array.
*/
public void copyFrom(BaseObj[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
validateIsObject();
if (d.length != mCurrentCount) {
@@ -429,6 +437,7 @@
i[ct] = d[ct].getID(mRS);
}
copy1DRangeFromUnchecked(0, mCurrentCount, i);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
private void validateBitmapFormat(Bitmap b) {
@@ -494,6 +503,7 @@
* @param d the source data array
*/
public void copyFromUnchecked(int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -502,7 +512,9 @@
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy into this Allocation from an array. This method does not guarantee
* that the Allocation is compatible with the input buffer; it copies memory
@@ -511,6 +523,7 @@
* @param d the source data array
*/
public void copyFromUnchecked(short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -519,7 +532,9 @@
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy into this Allocation from an array. This method does not guarantee
* that the Allocation is compatible with the input buffer; it copies memory
@@ -528,6 +543,7 @@
* @param d the source data array
*/
public void copyFromUnchecked(byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -536,7 +552,9 @@
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy into this Allocation from an array. This method does not guarantee
* that the Allocation is compatible with the input buffer; it copies memory
@@ -545,6 +563,7 @@
* @param d the source data array
*/
public void copyFromUnchecked(float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -553,8 +572,10 @@
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy into this Allocation from an array. This variant is type checked
* and will generate exceptions if the Allocation's {@link
@@ -563,6 +584,7 @@
* @param d the source data array
*/
public void copyFrom(int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -571,6 +593,7 @@
} else {
copy1DRangeFrom(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -581,6 +604,7 @@
* @param d the source data array
*/
public void copyFrom(short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -589,6 +613,7 @@
} else {
copy1DRangeFrom(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -599,6 +624,7 @@
* @param d the source data array
*/
public void copyFrom(byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -607,6 +633,7 @@
} else {
copy1DRangeFrom(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -617,6 +644,7 @@
* @param d the source data array
*/
public void copyFrom(float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -625,6 +653,7 @@
} else {
copy1DRangeFrom(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -641,6 +670,7 @@
* @param b the source bitmap
*/
public void copyFrom(Bitmap b) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (b.getConfig() == null) {
Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
@@ -652,6 +682,7 @@
validateBitmapSize(b);
validateBitmapFormat(b);
mRS.nAllocationCopyFromBitmap(getID(mRS), b);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -661,14 +692,15 @@
* @param a the source allocation
*/
public void copyFrom(Allocation a) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (!mType.equals(a.getType())) {
throw new RSIllegalArgumentException("Types of allocations must match.");
}
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, a, 0, 0);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
-
/**
* This is only intended to be used by auto-generated code reflected from
* the RenderScript script files and should not be used by developers.
@@ -759,10 +791,13 @@
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
int dataSize = mType.mElement.getBytesSize() * count;
data1DChecks(off, count, d.length * 4, dataSize);
mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy an array into part of this Allocation. This method does not
* guarantee that the Allocation is compatible with the input buffer.
@@ -772,10 +807,13 @@
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
int dataSize = mType.mElement.getBytesSize() * count;
data1DChecks(off, count, d.length * 2, dataSize);
mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy an array into part of this Allocation. This method does not
* guarantee that the Allocation is compatible with the input buffer.
@@ -785,10 +823,13 @@
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
int dataSize = mType.mElement.getBytesSize() * count;
data1DChecks(off, count, d.length, dataSize);
mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy an array into part of this Allocation. This method does not
* guarantee that the Allocation is compatible with the input buffer.
@@ -798,9 +839,11 @@
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
int dataSize = mType.mElement.getBytesSize() * count;
data1DChecks(off, count, d.length * 4, dataSize);
mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -813,8 +856,10 @@
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsInt32();
copy1DRangeFromUnchecked(off, count, d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -827,8 +872,10 @@
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsInt16();
copy1DRangeFromUnchecked(off, count, d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -841,8 +888,10 @@
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsInt8();
copy1DRangeFromUnchecked(off, count, d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -855,10 +904,11 @@
* @param d the source data array.
*/
public void copy1DRangeFrom(int off, int count, float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsFloat32();
copy1DRangeFromUnchecked(off, count, d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
-
/**
* Copy part of an Allocation into this Allocation.
*
@@ -869,6 +919,7 @@
* be copied.
*/
public void copy1DRangeFrom(int off, int count, Allocation data, int dataOff) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
mRS.nAllocationData2D(getIDSafe(), off, 0,
mSelectedLOD, mSelectedFace.mID,
count, 1, data.getID(mRS), dataOff, 0,
@@ -893,34 +944,41 @@
}
void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, byte[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, short[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length * 2);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, int[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length * 4);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, float[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length * 4);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
-
/**
* Copy from an array into a rectangular region in this Allocation. The
* array is assumed to be tightly packed.
@@ -932,8 +990,10 @@
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, byte[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsInt8();
copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -947,8 +1007,10 @@
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsInt16();
copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -962,8 +1024,10 @@
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, int[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsInt32();
copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -977,8 +1041,10 @@
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, float[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsFloat32();
copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -995,12 +1061,14 @@
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h,
Allocation data, int dataXoff, int dataYoff) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff,
mSelectedLOD, mSelectedFace.mID,
w, h, data.getID(mRS), dataXoff, dataYoff,
data.mSelectedLOD, data.mSelectedFace.mID);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1013,6 +1081,7 @@
* @param data the Bitmap to be copied
*/
public void copy2DRangeFrom(int xoff, int yoff, Bitmap data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
mRS.validate();
if (data.getConfig() == null) {
Bitmap newBitmap = Bitmap.createBitmap(data.getWidth(), data.getHeight(), Bitmap.Config.ARGB_8888);
@@ -1024,6 +1093,7 @@
validateBitmapFormat(data);
validate2DRange(xoff, yoff, data.getWidth(), data.getHeight());
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
private void validate3DRange(int xoff, int yoff, int zoff, int w, int h, int d) {
@@ -1166,10 +1236,12 @@
* @param b The bitmap to be set from the Allocation.
*/
public void copyTo(Bitmap b) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
mRS.validate();
validateBitmapFormat(b);
validateBitmapSize(b);
mRS.nAllocationCopyToBitmap(getID(mRS), b);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1180,9 +1252,11 @@
* @param d The array to be set from the Allocation.
*/
public void copyTo(byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsInt8();
mRS.validate();
mRS.nAllocationRead(getID(mRS), d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1193,9 +1267,11 @@
* @param d The array to be set from the Allocation.
*/
public void copyTo(short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsInt16();
mRS.validate();
mRS.nAllocationRead(getID(mRS), d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1206,9 +1282,11 @@
* @param d The array to be set from the Allocation.
*/
public void copyTo(int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsInt32();
mRS.validate();
mRS.nAllocationRead(getID(mRS), d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1219,9 +1297,11 @@
* @param d The array to be set from the Allocation.
*/
public void copyTo(float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsFloat32();
mRS.validate();
mRS.nAllocationRead(getID(mRS), d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1271,6 +1351,7 @@
* utilized
*/
static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips, int usage) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "createTyped");
rs.validate();
if (type.getID(rs) == 0) {
throw new RSInvalidStateException("Bad Type");
@@ -1279,6 +1360,7 @@
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
return new Allocation(id, rs, type, usage);
}
@@ -1323,6 +1405,7 @@
*/
static public Allocation createSized(RenderScript rs, Element e,
int count, int usage) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "createSized");
rs.validate();
Type.Builder b = new Type.Builder(rs, e);
b.setX(count);
@@ -1332,6 +1415,7 @@
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
return new Allocation(id, rs, t, usage);
}
@@ -1391,6 +1475,7 @@
static public Allocation createFromBitmap(RenderScript rs, Bitmap b,
MipmapControl mips,
int usage) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "createFromBitmap");
rs.validate();
// WAR undocumented color formats
@@ -1426,6 +1511,7 @@
if (id == 0) {
throw new RSRuntimeException("Load failed.");
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
return new Allocation(id, rs, t, usage);
}
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 1264adc..4de4766 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -29,8 +29,8 @@
import android.os.Process;
import android.util.Log;
import android.view.Surface;
-
-
+import android.os.SystemProperties;
+import android.os.Trace;
/**
* This class provides access to a RenderScript context, which controls RenderScript
@@ -44,6 +44,8 @@
* </div>
**/
public class RenderScript {
+ static final long TRACE_TAG = Trace.TRACE_TAG_RS;
+
static final String LOG_TAG = "RenderScript_jni";
static final boolean DEBUG = false;
@SuppressWarnings({"UnusedDeclaration", "deprecation"})
@@ -55,20 +57,22 @@
* We use a class initializer to allow the native code to cache some
* field offsets.
*/
- @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+ @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // TODO: now used locally; remove?
static boolean sInitialized;
native static void _nInit();
static {
sInitialized = false;
- try {
- System.loadLibrary("rs_jni");
- _nInit();
- sInitialized = true;
- } catch (UnsatisfiedLinkError e) {
- Log.e(LOG_TAG, "Error loading RS jni library: " + e);
- throw new RSRuntimeException("Error loading RS jni library: " + e);
+ if (!SystemProperties.getBoolean("config.disable_renderscript", false)) {
+ try {
+ System.loadLibrary("rs_jni");
+ _nInit();
+ sInitialized = true;
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(LOG_TAG, "Error loading RS jni library: " + e);
+ throw new RSRuntimeException("Error loading RS jni library: " + e);
+ }
}
}
@@ -92,6 +96,11 @@
* @param cacheDir A directory the current process can write to
*/
public static void setupDiskCache(File cacheDir) {
+ if (!sInitialized) {
+ Log.e(LOG_TAG, "RenderScript.setupDiskCache() called when disabled");
+ return;
+ }
+
// Defer creation of cache path to nScriptCCreate().
mCacheDir = cacheDir;
}
@@ -1137,6 +1146,11 @@
* @return RenderScript
*/
public static RenderScript create(Context ctx, int sdkVersion, ContextType ct) {
+ if (!sInitialized) {
+ Log.e(LOG_TAG, "RenderScript.create() called when disabled; someone is likely to crash");
+ return null;
+ }
+
RenderScript rs = new RenderScript(ctx);
rs.mDev = rs.nDeviceCreate();
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
index c9c54b2..5d3c1d3 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -31,7 +31,10 @@
}
/**
- * Supported elements types are {@link Element#U8_4}
+ * Supported elements types are {@link Element#U8}, {@link
+ * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
+ * {@link Element#F32}, {@link Element#F32_2}, {@link
+ * Element#F32_3}, and {@link Element#F32_4}
*
* The default coefficients are.
*
@@ -48,7 +51,14 @@
*/
public static ScriptIntrinsicConvolve3x3 create(RenderScript rs, Element e) {
float f[] = { 0, 0, 0, 0, 1, 0, 0, 0, 0};
- if (!e.isCompatible(Element.U8_4(rs))) {
+ if (!e.isCompatible(Element.U8(rs)) &&
+ !e.isCompatible(Element.U8_2(rs)) &&
+ !e.isCompatible(Element.U8_3(rs)) &&
+ !e.isCompatible(Element.U8_4(rs)) &&
+ !e.isCompatible(Element.F32(rs)) &&
+ !e.isCompatible(Element.F32_2(rs)) &&
+ !e.isCompatible(Element.F32_3(rs)) &&
+ !e.isCompatible(Element.F32_4(rs))) {
throw new RSIllegalArgumentException("Unsuported element type.");
}
int id = rs.nScriptIntrinsicCreate(1, e.getID(rs));
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java b/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
index c6e1e39..ad09f95 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
@@ -31,7 +31,10 @@
}
/**
- * Supported elements types are {@link Element#U8_4}
+ * Supported elements types are {@link Element#U8}, {@link
+ * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
+ * {@link Element#F32}, {@link Element#F32_2}, {@link
+ * Element#F32_3}, and {@link Element#F32_4}
*
* The default coefficients are.
* <code>
@@ -48,6 +51,17 @@
* @return ScriptIntrinsicConvolve5x5
*/
public static ScriptIntrinsicConvolve5x5 create(RenderScript rs, Element e) {
+ if (!e.isCompatible(Element.U8(rs)) &&
+ !e.isCompatible(Element.U8_2(rs)) &&
+ !e.isCompatible(Element.U8_3(rs)) &&
+ !e.isCompatible(Element.U8_4(rs)) &&
+ !e.isCompatible(Element.F32(rs)) &&
+ !e.isCompatible(Element.F32_2(rs)) &&
+ !e.isCompatible(Element.F32_3(rs)) &&
+ !e.isCompatible(Element.F32_4(rs))) {
+ throw new RSIllegalArgumentException("Unsuported element type.");
+ }
+
int id = rs.nScriptIntrinsicCreate(4, e.getID(rs));
return new ScriptIntrinsicConvolve5x5(id, rs);
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicHistogram.java b/graphics/java/android/renderscript/ScriptIntrinsicHistogram.java
new file mode 100644
index 0000000..f143326
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsicHistogram.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+/**
+ * Intrinsic Histogram filter.
+ *
+ *
+ **/
+public final class ScriptIntrinsicHistogram extends ScriptIntrinsic {
+ private Allocation mOut;
+
+ private ScriptIntrinsicHistogram(int id, RenderScript rs) {
+ super(id, rs);
+ }
+
+ /**
+ * Create an intrinsic for calculating the histogram of an uchar
+ * or uchar4 image.
+ *
+ * Supported elements types are
+ * {@link Element#U8_4}, {@link Element#U8_3},
+ * {@link Element#U8_2}, {@link Element#U8}
+ *
+ * @param rs The RenderScript context
+ * @param e Element type for inputs
+ *
+ * @return ScriptIntrinsicHistogram
+ */
+ public static ScriptIntrinsicHistogram create(RenderScript rs, Element e) {
+ if ((!e.isCompatible(Element.U8_4(rs))) &&
+ (!e.isCompatible(Element.U8_3(rs))) &&
+ (!e.isCompatible(Element.U8_2(rs))) &&
+ (!e.isCompatible(Element.U8(rs)))) {
+ throw new RSIllegalArgumentException("Unsuported element type.");
+ }
+ int id = rs.nScriptIntrinsicCreate(9, e.getID(rs));
+ ScriptIntrinsicHistogram sib = new ScriptIntrinsicHistogram(id, rs);
+ return sib;
+ }
+
+ /**
+ * Process an input buffer and place the histogram into the
+ * output allocation. The output allocation may be a narrower
+ * vector size than the input. In this case the vector size of
+ * the output is used to determine how many of the input
+ * channels are used in the computation. This is useful if you
+ * have an RGBA input buffer but only want the histogram for
+ * RGB.
+ *
+ * 1D and 2D input allocations are supported.
+ *
+ * @param ain The input image
+ */
+ public void forEach(Allocation ain) {
+ if (ain.getType().getElement().getVectorSize() <
+ mOut.getType().getElement().getVectorSize()) {
+
+ throw new RSIllegalArgumentException(
+ "Input vector size must be >= output vector size.");
+ }
+ if (ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
+ ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+ throw new RSIllegalArgumentException("Output type must be U32 or I32.");
+ }
+
+ forEach(0, ain, null, null);
+ }
+
+ /**
+ * Set the coefficients used for the RGBA to Luminocity
+ * calculation. The default is {0.299f, 0.587f, 0.114f, 0.f}.
+ *
+ * Coefficients must be >= 0 and sum to 1.0 or less.
+ *
+ * @param r Red coefficient
+ * @param g Green coefficient
+ * @param b Blue coefficient
+ * @param a Alpha coefficient
+ */
+ public void setDotCoefficients(float r, float g, float b, float a) {
+ if ((r < 0.f) || (g < 0.f) || (b < 0.f) || (a < 0.f)) {
+ throw new RSIllegalArgumentException("Coefficient may not be negative.");
+ }
+ if ((r + g + b + a) > 1.f) {
+ throw new RSIllegalArgumentException("Sum of coefficients must be 1.0 or less.");
+ }
+
+ FieldPacker fp = new FieldPacker(16);
+ fp.addF32(r);
+ fp.addF32(g);
+ fp.addF32(b);
+ fp.addF32(a);
+ setVar(0, fp);
+ }
+
+ /**
+ * Set the output of the histogram. 32 bit integer types are
+ * supported.
+ *
+ * @param aout The output allocation
+ */
+ public void setOutput(Allocation aout) {
+ mOut = aout;
+ if (mOut.getType().getElement() != Element.U32(mRS) &&
+ mOut.getType().getElement() != Element.U32_2(mRS) &&
+ mOut.getType().getElement() != Element.U32_3(mRS) &&
+ mOut.getType().getElement() != Element.U32_4(mRS) &&
+ mOut.getType().getElement() != Element.I32(mRS) &&
+ mOut.getType().getElement() != Element.I32_2(mRS) &&
+ mOut.getType().getElement() != Element.I32_3(mRS) &&
+ mOut.getType().getElement() != Element.I32_4(mRS)) {
+
+ throw new RSIllegalArgumentException("Output type must be U32 or I32.");
+ }
+ if ((mOut.getType().getX() != 256) ||
+ (mOut.getType().getY() != 0) ||
+ mOut.getType().hasMipmaps() ||
+ (mOut.getType().getYuv() != 0)) {
+
+ throw new RSIllegalArgumentException("Output must be 1D, 256 elements.");
+ }
+ setVar(1, aout);
+ }
+
+ /**
+ * Process an input buffer and place the histogram into the
+ * output allocation. The dot product of the input channel and
+ * the coefficients from 'setDotCoefficients' are used to
+ * calculate the output values.
+ *
+ * 1D and 2D input allocations are supported.
+ *
+ * @param ain The input image
+ */
+ public void forEach_dot(Allocation ain) {
+ if (mOut.getType().getElement().getVectorSize() != 1) {
+ throw new RSIllegalArgumentException("Output vector size must be one.");
+ }
+ if (ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
+ ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+ throw new RSIllegalArgumentException("Output type must be U32 or I32.");
+ }
+
+ forEach(1, ain, null, null);
+ }
+
+
+
+ /**
+ * Get a KernelID for this intrinsic kernel.
+ *
+ * @return Script.KernelID The KernelID object.
+ */
+ public Script.KernelID getKernelID_seperate() {
+ return createKernelID(0, 3, null, null);
+ }
+
+ /**
+ * Get a FieldID for the input field of this intrinsic.
+ *
+ * @return Script.FieldID The FieldID object.
+ */
+ public Script.FieldID getFieldID_Input() {
+ return createFieldID(1, null);
+ }
+}
+
diff --git a/graphics/tests/graphicstests/src/android/graphics/drawable/MipmapDrawableTest.java b/graphics/tests/graphicstests/src/android/graphics/drawable/MipmapDrawableTest.java
deleted file mode 100644
index 5fcc3bc..0000000
--- a/graphics/tests/graphicstests/src/android/graphics/drawable/MipmapDrawableTest.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.drawable;
-
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.MipmapDrawable;
-import android.graphics.drawable.DrawableContainer.DrawableContainerState;
-import android.test.InstrumentationTestCase;
-
-public class MipmapDrawableTest extends InstrumentationTestCase {
- private MockMipmapDrawable mMipmapDrawable;
-
- private DrawableContainerState mDrawableContainerState;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mMipmapDrawable = new MockMipmapDrawable();
- mDrawableContainerState = (DrawableContainerState) mMipmapDrawable.getConstantState();
- }
-
- public void testMipmapDrawable() {
- new MipmapDrawable();
- // Check the values set in the constructor
- assertNotNull(new MipmapDrawable().getConstantState());
- assertTrue(new MockMipmapDrawable().hasCalledOnBoundsChanged());
- }
-
- public void testAddDrawable() {
- assertEquals(0, mDrawableContainerState.getChildCount());
-
- // nothing happens if drawable is null
- mMipmapDrawable.reset();
- mMipmapDrawable.addDrawable(null);
- assertEquals(0, mDrawableContainerState.getChildCount());
- assertFalse(mMipmapDrawable.hasCalledOnBoundsChanged());
-
- mMipmapDrawable.reset();
- mMipmapDrawable.addDrawable(new MockDrawable());
- assertEquals(1, mDrawableContainerState.getChildCount());
- assertTrue(mMipmapDrawable.hasCalledOnBoundsChanged());
-
- mMipmapDrawable.reset();
- mMipmapDrawable.addDrawable(new MockDrawable());
- assertEquals(2, mDrawableContainerState.getChildCount());
- assertTrue(mMipmapDrawable.hasCalledOnBoundsChanged());
- }
-
- public void testSortedByHeight() {
- Drawable small = new MockDrawable(8);
- Drawable medium = new MockDrawable(32);
- Drawable large = new MockDrawable(128);
-
- mMipmapDrawable.addDrawable(medium);
- assertSame(medium, mDrawableContainerState.getChildren()[0]);
-
- mMipmapDrawable.addDrawable(small);
- assertSame(small, mDrawableContainerState.getChildren()[0]);
- assertSame(medium, mDrawableContainerState.getChildren()[1]);
-
- mMipmapDrawable.addDrawable(large);
- assertSame(small, mDrawableContainerState.getChildren()[0]);
- assertSame(medium, mDrawableContainerState.getChildren()[1]);
- assertSame(large, mDrawableContainerState.getChildren()[2]);
-
- mMipmapDrawable.addDrawable(small);
- assertSame(small, mDrawableContainerState.getChildren()[0]);
- assertSame(small, mDrawableContainerState.getChildren()[1]);
- assertSame(medium, mDrawableContainerState.getChildren()[2]);
- assertSame(large, mDrawableContainerState.getChildren()[3]);
-
- mMipmapDrawable.addDrawable(medium);
- assertSame(small, mDrawableContainerState.getChildren()[0]);
- assertSame(small, mDrawableContainerState.getChildren()[1]);
- assertSame(medium, mDrawableContainerState.getChildren()[2]);
- assertSame(medium, mDrawableContainerState.getChildren()[3]);
- assertSame(large, mDrawableContainerState.getChildren()[4]);
-
- mMipmapDrawable.addDrawable(large);
- assertSame(small, mDrawableContainerState.getChildren()[0]);
- assertSame(small, mDrawableContainerState.getChildren()[1]);
- assertSame(medium, mDrawableContainerState.getChildren()[2]);
- assertSame(medium, mDrawableContainerState.getChildren()[3]);
- assertSame(large, mDrawableContainerState.getChildren()[4]);
- assertSame(large, mDrawableContainerState.getChildren()[5]);
- }
-
- public void testSetBoundsOneItem() {
- // the method is not called if same bounds are set
- mMipmapDrawable.reset();
- mMipmapDrawable.setBounds(mMipmapDrawable.getBounds());
- assertFalse(mMipmapDrawable.hasCalledOnBoundsChanged());
-
- // the method is called if different bounds are set, even without drawables
- mMipmapDrawable.reset();
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, mMipmapDrawable.getBounds().height() + 1));
- assertTrue(mMipmapDrawable.hasCalledOnBoundsChanged());
-
- // adding an item should check bounds to see if new drawable is more appropriate
- mMipmapDrawable.reset();
- Drawable item = new MockDrawable(42);
- mMipmapDrawable.addDrawable(item);
- assertTrue(mMipmapDrawable.hasCalledOnBoundsChanged());
-
- // the method is called if different bounds are set
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, mMipmapDrawable.getBounds().height() + 1));
- assertTrue(mMipmapDrawable.hasCalledOnBoundsChanged());
-
- // check that correct drawable is selected for any size.
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, item.getIntrinsicHeight() - 1));
- assertSame(item, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, item.getIntrinsicHeight()));
- assertSame(item, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, item.getIntrinsicHeight() + 1));
- assertSame(item, mMipmapDrawable.getCurrent());
- }
-
- public void testSetBounds() {
- Drawable small = new MockDrawable(8);
- Drawable medium = new MockDrawable(32);
- Drawable large = new MockDrawable(128);
-
- mMipmapDrawable.addDrawable(large);
- mMipmapDrawable.addDrawable(small);
- mMipmapDrawable.addDrawable(medium);
-
- // check that correct drawable is selected.
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, small.getIntrinsicHeight() - 1));
- assertSame(small, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, small.getIntrinsicHeight()));
- assertSame(small, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, small.getIntrinsicHeight() + 1));
- assertSame(medium, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, medium.getIntrinsicHeight() - 1));
- assertSame(medium, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, medium.getIntrinsicHeight()));
- assertSame(medium, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, medium.getIntrinsicHeight() + 1));
- assertSame(large, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, large.getIntrinsicHeight() - 1));
- assertSame(large, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, large.getIntrinsicHeight()));
- assertSame(large, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, large.getIntrinsicHeight() + 1));
- assertSame(large, mMipmapDrawable.getCurrent());
- }
-
- public void testSizes() {
- // Check default value with no mipmap defined
- assertEquals(-1, mMipmapDrawable.getIntrinsicHeight());
- assertEquals(-1, mMipmapDrawable.getIntrinsicWidth());
- assertEquals(0, mMipmapDrawable.getMinimumHeight());
- assertEquals(0, mMipmapDrawable.getMinimumWidth());
-
- Drawable small = new MockDrawable(8, 4);
- Drawable medium = new MockDrawable(32, 16);
- Drawable large = new MockDrawable(128, 64);
-
- mMipmapDrawable.addDrawable(medium);
- assertEquals(medium.getIntrinsicHeight(), mMipmapDrawable.getIntrinsicHeight());
- assertEquals(medium.getMinimumHeight(), mMipmapDrawable.getMinimumHeight());
-
- mMipmapDrawable.addDrawable(large);
- assertEquals(large.getIntrinsicHeight(), mMipmapDrawable.getIntrinsicHeight());
- assertEquals(medium.getMinimumHeight(), mMipmapDrawable.getMinimumHeight());
-
- mMipmapDrawable.addDrawable(small);
- assertEquals(large.getIntrinsicHeight(), mMipmapDrawable.getIntrinsicHeight());
- assertEquals(small.getMinimumHeight(), mMipmapDrawable.getMinimumHeight());
- }
-
- public void testReplacementWhenAdded() {
- Drawable small = new MockDrawable(8);
- Drawable medium = new MockDrawable(32);
- Drawable large = new MockDrawable(128);
-
- // Small bounds, so that the smallest mipmap should always be selected
- mMipmapDrawable.setBounds(new Rect(0, 0, 0, 0));
-
- // Providing smaller versions, that should immediately be used as current
- mMipmapDrawable.addDrawable(large);
- assertSame(large, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.addDrawable(medium);
- assertSame(medium, mMipmapDrawable.getCurrent());
-
- mMipmapDrawable.addDrawable(small);
- assertSame(small, mMipmapDrawable.getCurrent());
- }
-
- private class MockMipmapDrawable extends MipmapDrawable {
- private boolean mHasCalledOnBoundsChanged;
-
- public boolean hasCalledOnBoundsChanged() {
- return mHasCalledOnBoundsChanged;
- }
-
- public void reset() {
- mHasCalledOnBoundsChanged = false;
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- super.onBoundsChange(bounds);
- mHasCalledOnBoundsChanged = true;
- }
- }
-
- private class MockDrawable extends Drawable {
- int mIntrinsicHeight;
- int mMinimumHeight;
-
- public MockDrawable() {
- this(0);
- }
-
- public MockDrawable(int intrinsicHeight) {
- this(intrinsicHeight, intrinsicHeight);
- }
-
- public MockDrawable(int intrinsicHeight, int minimumHeight) {
- mIntrinsicHeight = intrinsicHeight;
- mMinimumHeight = minimumHeight;
- }
-
- @Override
- public void draw(Canvas canvas) {
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
-
- @Override
- public void setAlpha(int alpha) {
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mIntrinsicHeight;
- }
-
- @Override
- public int getMinimumHeight() {
- return mMinimumHeight;
- }
- }
-}
diff --git a/include/android_runtime/android_view_InputQueue.h b/include/android_runtime/android_view_InputQueue.h
index ba2d02d..ed37b0a 100644
--- a/include/android_runtime/android_view_InputQueue.h
+++ b/include/android_runtime/android_view_InputQueue.h
@@ -17,7 +17,7 @@
#ifndef _ANDROID_VIEW_INPUTQUEUE_H
#define _ANDROID_VIEW_INPUTQUEUE_H
-#include <androidfw/Input.h>
+#include <input/Input.h>
#include <utils/Looper.h>
#include <utils/TypeHelpers.h>
#include <utils/Vector.h>
diff --git a/include/androidfw/AssetDir.h b/include/androidfw/AssetDir.h
index abf8a35..bd89d7d 100644
--- a/include/androidfw/AssetDir.h
+++ b/include/androidfw/AssetDir.h
@@ -20,10 +20,10 @@
#ifndef __LIBS_ASSETDIR_H
#define __LIBS_ASSETDIR_H
+#include <androidfw/misc.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/SortedVector.h>
-#include <utils/misc.h>
#include <sys/types.h>
namespace android {
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index d153c31..d95b45e 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -22,13 +22,13 @@
#include <androidfw/Asset.h>
#include <androidfw/AssetDir.h>
+#include <androidfw/ZipFileRO.h>
#include <utils/KeyedVector.h>
#include <utils/SortedVector.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Vector.h>
-#include <utils/ZipFileRO.h>
/*
* Native-app access is via the opaque typedef struct AAssetManager in the C namespace.
diff --git a/include/androidfw/Input.h b/include/androidfw/Input.h
deleted file mode 100644
index 37ab279..0000000
--- a/include/androidfw/Input.h
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROIDFW_INPUT_H
-#define _ANDROIDFW_INPUT_H
-
-/**
- * Native input event structures.
- */
-
-#include <android/input.h>
-#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-
-#ifdef HAVE_ANDROID_OS
-class SkMatrix;
-#endif
-
-/*
- * Additional private constants not defined in ndk/ui/input.h.
- */
-enum {
- /* Signifies that the key is being predispatched */
- AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000,
-
- /* Private control to determine when an app is tracking a key sequence. */
- AKEY_EVENT_FLAG_START_TRACKING = 0x40000000,
-
- /* Key event is inconsistent with previously sent key events. */
- AKEY_EVENT_FLAG_TAINTED = 0x80000000,
-};
-
-enum {
- /* Motion event is inconsistent with previously sent motion events. */
- AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
-};
-
-enum {
- /* Used when a motion event is not associated with any display.
- * Typically used for non-pointer events. */
- ADISPLAY_ID_NONE = -1,
-
- /* The default display id. */
- ADISPLAY_ID_DEFAULT = 0,
-};
-
-enum {
- /*
- * Indicates that an input device has switches.
- * This input source flag is hidden from the API because switches are only used by the system
- * and applications have no way to interact with them.
- */
- AINPUT_SOURCE_SWITCH = 0x80000000,
-};
-
-/*
- * SystemUiVisibility constants from View.
- */
-enum {
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
-};
-
-/*
- * Maximum number of pointers supported per motion event.
- * Smallest number of pointers is 1.
- * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
- * will occasionally emit 11. There is not much harm making this constant bigger.)
- */
-#define MAX_POINTERS 16
-
-/*
- * Maximum pointer id value supported in a motion event.
- * Smallest pointer id is 0.
- * (This is limited by our use of BitSet32 to track pointer assignments.)
- */
-#define MAX_POINTER_ID 31
-
-/*
- * Declare a concrete type for the NDK's input event forward declaration.
- */
-struct AInputEvent {
- virtual ~AInputEvent() { }
-};
-
-/*
- * Declare a concrete type for the NDK's input device forward declaration.
- */
-struct AInputDevice {
- virtual ~AInputDevice() { }
-};
-
-
-namespace android {
-
-#ifdef HAVE_ANDROID_OS
-class Parcel;
-#endif
-
-/*
- * Flags that flow alongside events in the input dispatch system to help with certain
- * policy decisions such as waking from device sleep.
- *
- * These flags are also defined in frameworks/base/core/java/android/view/WindowManagerPolicy.java.
- */
-enum {
- /* These flags originate in RawEvents and are generally set in the key map.
- * NOTE: If you edit these flags, also edit labels in KeycodeLabels.h. */
-
- POLICY_FLAG_WAKE = 0x00000001,
- POLICY_FLAG_WAKE_DROPPED = 0x00000002,
- POLICY_FLAG_SHIFT = 0x00000004,
- POLICY_FLAG_CAPS_LOCK = 0x00000008,
- POLICY_FLAG_ALT = 0x00000010,
- POLICY_FLAG_ALT_GR = 0x00000020,
- POLICY_FLAG_MENU = 0x00000040,
- POLICY_FLAG_LAUNCHER = 0x00000080,
- POLICY_FLAG_VIRTUAL = 0x00000100,
- POLICY_FLAG_FUNCTION = 0x00000200,
-
- POLICY_FLAG_RAW_MASK = 0x0000ffff,
-
- /* These flags are set by the input dispatcher. */
-
- // Indicates that the input event was injected.
- POLICY_FLAG_INJECTED = 0x01000000,
-
- // Indicates that the input event is from a trusted source such as a directly attached
- // input device or an application with system-wide event injection permission.
- POLICY_FLAG_TRUSTED = 0x02000000,
-
- // Indicates that the input event has passed through an input filter.
- POLICY_FLAG_FILTERED = 0x04000000,
-
- // Disables automatic key repeating behavior.
- POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000,
-
- /* These flags are set by the input reader policy as it intercepts each event. */
-
- // Indicates that the screen was off when the event was received and the event
- // should wake the device.
- POLICY_FLAG_WOKE_HERE = 0x10000000,
-
- // Indicates that the screen was dim when the event was received and the event
- // should brighten the device.
- POLICY_FLAG_BRIGHT_HERE = 0x20000000,
-
- // Indicates that the event should be dispatched to applications.
- // The input event should still be sent to the InputDispatcher so that it can see all
- // input events received include those that it will not deliver.
- POLICY_FLAG_PASS_TO_USER = 0x40000000,
-};
-
-/*
- * Pointer coordinate data.
- */
-struct PointerCoords {
- enum { MAX_AXES = 14 }; // 14 so that sizeof(PointerCoords) == 64
-
- // Bitfield of axes that are present in this structure.
- uint64_t bits;
-
- // Values of axes that are stored in this structure packed in order by axis id
- // for each axis that is present in the structure according to 'bits'.
- float values[MAX_AXES];
-
- inline void clear() {
- bits = 0;
- }
-
- float getAxisValue(int32_t axis) const;
- status_t setAxisValue(int32_t axis, float value);
-
- void scale(float scale);
-
- inline float getX() const {
- return getAxisValue(AMOTION_EVENT_AXIS_X);
- }
-
- inline float getY() const {
- return getAxisValue(AMOTION_EVENT_AXIS_Y);
- }
-
-#ifdef HAVE_ANDROID_OS
- status_t readFromParcel(Parcel* parcel);
- status_t writeToParcel(Parcel* parcel) const;
-#endif
-
- bool operator==(const PointerCoords& other) const;
- inline bool operator!=(const PointerCoords& other) const {
- return !(*this == other);
- }
-
- void copyFrom(const PointerCoords& other);
-
-private:
- void tooManyAxes(int axis);
-};
-
-/*
- * Pointer property data.
- */
-struct PointerProperties {
- // The id of the pointer.
- int32_t id;
-
- // The pointer tool type.
- int32_t toolType;
-
- inline void clear() {
- id = -1;
- toolType = 0;
- }
-
- bool operator==(const PointerProperties& other) const;
- inline bool operator!=(const PointerProperties& other) const {
- return !(*this == other);
- }
-
- void copyFrom(const PointerProperties& other);
-};
-
-/*
- * Input events.
- */
-class InputEvent : public AInputEvent {
-public:
- virtual ~InputEvent() { }
-
- virtual int32_t getType() const = 0;
-
- inline int32_t getDeviceId() const { return mDeviceId; }
-
- inline int32_t getSource() const { return mSource; }
-
- inline void setSource(int32_t source) { mSource = source; }
-
-protected:
- void initialize(int32_t deviceId, int32_t source);
- void initialize(const InputEvent& from);
-
- int32_t mDeviceId;
- int32_t mSource;
-};
-
-/*
- * Key events.
- */
-class KeyEvent : public InputEvent {
-public:
- virtual ~KeyEvent() { }
-
- virtual int32_t getType() const { return AINPUT_EVENT_TYPE_KEY; }
-
- inline int32_t getAction() const { return mAction; }
-
- inline int32_t getFlags() const { return mFlags; }
-
- inline void setFlags(int32_t flags) { mFlags = flags; }
-
- inline int32_t getKeyCode() const { return mKeyCode; }
-
- inline int32_t getScanCode() const { return mScanCode; }
-
- inline int32_t getMetaState() const { return mMetaState; }
-
- inline int32_t getRepeatCount() const { return mRepeatCount; }
-
- inline nsecs_t getDownTime() const { return mDownTime; }
-
- inline nsecs_t getEventTime() const { return mEventTime; }
-
- // Return true if this event may have a default action implementation.
- static bool hasDefaultAction(int32_t keyCode);
- bool hasDefaultAction() const;
-
- // Return true if this event represents a system key.
- static bool isSystemKey(int32_t keyCode);
- bool isSystemKey() const;
-
- void initialize(
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime);
- void initialize(const KeyEvent& from);
-
-protected:
- int32_t mAction;
- int32_t mFlags;
- int32_t mKeyCode;
- int32_t mScanCode;
- int32_t mMetaState;
- int32_t mRepeatCount;
- nsecs_t mDownTime;
- nsecs_t mEventTime;
-};
-
-/*
- * Motion events.
- */
-class MotionEvent : public InputEvent {
-public:
- virtual ~MotionEvent() { }
-
- virtual int32_t getType() const { return AINPUT_EVENT_TYPE_MOTION; }
-
- inline int32_t getAction() const { return mAction; }
-
- inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
-
- inline int32_t getActionIndex() const {
- return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
- >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
- }
-
- inline void setAction(int32_t action) { mAction = action; }
-
- inline int32_t getFlags() const { return mFlags; }
-
- inline void setFlags(int32_t flags) { mFlags = flags; }
-
- inline int32_t getEdgeFlags() const { return mEdgeFlags; }
-
- inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; }
-
- inline int32_t getMetaState() const { return mMetaState; }
-
- inline void setMetaState(int32_t metaState) { mMetaState = metaState; }
-
- inline int32_t getButtonState() const { return mButtonState; }
-
- inline float getXOffset() const { return mXOffset; }
-
- inline float getYOffset() const { return mYOffset; }
-
- inline float getXPrecision() const { return mXPrecision; }
-
- inline float getYPrecision() const { return mYPrecision; }
-
- inline nsecs_t getDownTime() const { return mDownTime; }
-
- inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; }
-
- inline size_t getPointerCount() const { return mPointerProperties.size(); }
-
- inline const PointerProperties* getPointerProperties(size_t pointerIndex) const {
- return &mPointerProperties[pointerIndex];
- }
-
- inline int32_t getPointerId(size_t pointerIndex) const {
- return mPointerProperties[pointerIndex].id;
- }
-
- inline int32_t getToolType(size_t pointerIndex) const {
- return mPointerProperties[pointerIndex].toolType;
- }
-
- inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
-
- const PointerCoords* getRawPointerCoords(size_t pointerIndex) const;
-
- float getRawAxisValue(int32_t axis, size_t pointerIndex) const;
-
- inline float getRawX(size_t pointerIndex) const {
- return getRawAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
- }
-
- inline float getRawY(size_t pointerIndex) const {
- return getRawAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex);
- }
-
- float getAxisValue(int32_t axis, size_t pointerIndex) const;
-
- inline float getX(size_t pointerIndex) const {
- return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
- }
-
- inline float getY(size_t pointerIndex) const {
- return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex);
- }
-
- inline float getPressure(size_t pointerIndex) const {
- return getAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerIndex);
- }
-
- inline float getSize(size_t pointerIndex) const {
- return getAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerIndex);
- }
-
- inline float getTouchMajor(size_t pointerIndex) const {
- return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex);
- }
-
- inline float getTouchMinor(size_t pointerIndex) const {
- return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex);
- }
-
- inline float getToolMajor(size_t pointerIndex) const {
- return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex);
- }
-
- inline float getToolMinor(size_t pointerIndex) const {
- return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex);
- }
-
- inline float getOrientation(size_t pointerIndex) const {
- return getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex);
- }
-
- inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; }
-
- inline nsecs_t getHistoricalEventTime(size_t historicalIndex) const {
- return mSampleEventTimes[historicalIndex];
- }
-
- const PointerCoords* getHistoricalRawPointerCoords(
- size_t pointerIndex, size_t historicalIndex) const;
-
- float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
- size_t historicalIndex) const;
-
- inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalRawAxisValue(
- AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex);
- }
-
- inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalRawAxisValue(
- AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex);
- }
-
- float getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const;
-
- inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalAxisValue(
- AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex);
- }
-
- inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalAxisValue(
- AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex);
- }
-
- inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalAxisValue(
- AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historicalIndex);
- }
-
- inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalAxisValue(
- AMOTION_EVENT_AXIS_SIZE, pointerIndex, historicalIndex);
- }
-
- inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalAxisValue(
- AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex);
- }
-
- inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalAxisValue(
- AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex);
- }
-
- inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalAxisValue(
- AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex);
- }
-
- inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalAxisValue(
- AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historicalIndex);
- }
-
- inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const {
- return getHistoricalAxisValue(
- AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex);
- }
-
- ssize_t findPointerIndex(int32_t pointerId) const;
-
- void initialize(
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t edgeFlags,
- int32_t metaState,
- int32_t buttonState,
- float xOffset,
- float yOffset,
- float xPrecision,
- float yPrecision,
- nsecs_t downTime,
- nsecs_t eventTime,
- size_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords);
-
- void copyFrom(const MotionEvent* other, bool keepHistory);
-
- void addSample(
- nsecs_t eventTime,
- const PointerCoords* pointerCoords);
-
- void offsetLocation(float xOffset, float yOffset);
-
- void scale(float scaleFactor);
-
-#ifdef HAVE_ANDROID_OS
- void transform(const SkMatrix* matrix);
-
- status_t readFromParcel(Parcel* parcel);
- status_t writeToParcel(Parcel* parcel) const;
-#endif
-
- static bool isTouchEvent(int32_t source, int32_t action);
- inline bool isTouchEvent() const {
- return isTouchEvent(mSource, mAction);
- }
-
- // Low-level accessors.
- inline const PointerProperties* getPointerProperties() const {
- return mPointerProperties.array();
- }
- inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
- inline const PointerCoords* getSamplePointerCoords() const {
- return mSamplePointerCoords.array();
- }
-
-protected:
- int32_t mAction;
- int32_t mFlags;
- int32_t mEdgeFlags;
- int32_t mMetaState;
- int32_t mButtonState;
- float mXOffset;
- float mYOffset;
- float mXPrecision;
- float mYPrecision;
- nsecs_t mDownTime;
- Vector<PointerProperties> mPointerProperties;
- Vector<nsecs_t> mSampleEventTimes;
- Vector<PointerCoords> mSamplePointerCoords;
-};
-
-/*
- * Input event factory.
- */
-class InputEventFactoryInterface {
-protected:
- virtual ~InputEventFactoryInterface() { }
-
-public:
- InputEventFactoryInterface() { }
-
- virtual KeyEvent* createKeyEvent() = 0;
- virtual MotionEvent* createMotionEvent() = 0;
-};
-
-/*
- * A simple input event factory implementation that uses a single preallocated instance
- * of each type of input event that are reused for each request.
- */
-class PreallocatedInputEventFactory : public InputEventFactoryInterface {
-public:
- PreallocatedInputEventFactory() { }
- virtual ~PreallocatedInputEventFactory() { }
-
- virtual KeyEvent* createKeyEvent() { return & mKeyEvent; }
- virtual MotionEvent* createMotionEvent() { return & mMotionEvent; }
-
-private:
- KeyEvent mKeyEvent;
- MotionEvent mMotionEvent;
-};
-
-/*
- * An input event factory implementation that maintains a pool of input events.
- */
-class PooledInputEventFactory : public InputEventFactoryInterface {
-public:
- PooledInputEventFactory(size_t maxPoolSize = 20);
- virtual ~PooledInputEventFactory();
-
- virtual KeyEvent* createKeyEvent();
- virtual MotionEvent* createMotionEvent();
-
- void recycle(InputEvent* event);
-
-private:
- const size_t mMaxPoolSize;
-
- Vector<KeyEvent*> mKeyEventPool;
- Vector<MotionEvent*> mMotionEventPool;
-};
-
-} // namespace android
-
-#endif // _ANDROIDFW_INPUT_H
diff --git a/include/androidfw/InputDevice.h b/include/androidfw/InputDevice.h
deleted file mode 100644
index 45dc2db..0000000
--- a/include/androidfw/InputDevice.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROIDFW_INPUT_DEVICE_H
-#define _ANDROIDFW_INPUT_DEVICE_H
-
-#include <androidfw/Input.h>
-#include <androidfw/KeyCharacterMap.h>
-
-namespace android {
-
-/*
- * Identifies a device.
- */
-struct InputDeviceIdentifier {
- inline InputDeviceIdentifier() :
- bus(0), vendor(0), product(0), version(0) {
- }
-
- // Information provided by the kernel.
- String8 name;
- String8 location;
- String8 uniqueId;
- uint16_t bus;
- uint16_t vendor;
- uint16_t product;
- uint16_t version;
-
- // A composite input device descriptor string that uniquely identifies the device
- // even across reboots or reconnections. The value of this field is used by
- // upper layers of the input system to associate settings with individual devices.
- // It is hashed from whatever kernel provided information is available.
- // Ideally, the way this value is computed should not change between Android releases
- // because that would invalidate persistent settings that rely on it.
- String8 descriptor;
-};
-
-/*
- * Describes the characteristics and capabilities of an input device.
- */
-class InputDeviceInfo {
-public:
- InputDeviceInfo();
- InputDeviceInfo(const InputDeviceInfo& other);
- ~InputDeviceInfo();
-
- struct MotionRange {
- int32_t axis;
- uint32_t source;
- float min;
- float max;
- float flat;
- float fuzz;
- float resolution;
- };
-
- void initialize(int32_t id, int32_t generation, const InputDeviceIdentifier& identifier,
- const String8& alias, bool isExternal);
-
- inline int32_t getId() const { return mId; }
- inline int32_t getGeneration() const { return mGeneration; }
- inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; }
- inline const String8& getAlias() const { return mAlias; }
- inline const String8& getDisplayName() const {
- return mAlias.isEmpty() ? mIdentifier.name : mAlias;
- }
- inline bool isExternal() const { return mIsExternal; }
- inline uint32_t getSources() const { return mSources; }
-
- const MotionRange* getMotionRange(int32_t axis, uint32_t source) const;
-
- void addSource(uint32_t source);
- void addMotionRange(int32_t axis, uint32_t source,
- float min, float max, float flat, float fuzz, float resolution);
- void addMotionRange(const MotionRange& range);
-
- inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
- inline int32_t getKeyboardType() const { return mKeyboardType; }
-
- inline void setKeyCharacterMap(const sp<KeyCharacterMap>& value) {
- mKeyCharacterMap = value;
- }
-
- inline sp<KeyCharacterMap> getKeyCharacterMap() const {
- return mKeyCharacterMap;
- }
-
- inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
- inline bool hasVibrator() const { return mHasVibrator; }
-
- inline const Vector<MotionRange>& getMotionRanges() const {
- return mMotionRanges;
- }
-
-private:
- int32_t mId;
- int32_t mGeneration;
- InputDeviceIdentifier mIdentifier;
- String8 mAlias;
- bool mIsExternal;
- uint32_t mSources;
- int32_t mKeyboardType;
- sp<KeyCharacterMap> mKeyCharacterMap;
- bool mHasVibrator;
-
- Vector<MotionRange> mMotionRanges;
-};
-
-/* Types of input device configuration files. */
-enum InputDeviceConfigurationFileType {
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
-};
-
-/*
- * Gets the path of an input device configuration file, if one is available.
- * Considers both system provided and user installed configuration files.
- *
- * The device identifier is used to construct several default configuration file
- * names to try based on the device name, vendor, product, and version.
- *
- * Returns an empty string if not found.
- */
-extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
- const InputDeviceIdentifier& deviceIdentifier,
- InputDeviceConfigurationFileType type);
-
-/*
- * Gets the path of an input device configuration file, if one is available.
- * Considers both system provided and user installed configuration files.
- *
- * The name is case-sensitive and is used to construct the filename to resolve.
- * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores.
- *
- * Returns an empty string if not found.
- */
-extern String8 getInputDeviceConfigurationFilePathByName(
- const String8& name, InputDeviceConfigurationFileType type);
-
-} // namespace android
-
-#endif // _ANDROIDFW_INPUT_DEVICE_H
diff --git a/include/androidfw/InputTransport.h b/include/androidfw/InputTransport.h
deleted file mode 100644
index 8712995..0000000
--- a/include/androidfw/InputTransport.h
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROIDFW_INPUT_TRANSPORT_H
-#define _ANDROIDFW_INPUT_TRANSPORT_H
-
-/**
- * Native input transport.
- *
- * The InputChannel provides a mechanism for exchanging InputMessage structures across processes.
- *
- * The InputPublisher and InputConsumer each handle one end-point of an input channel.
- * The InputPublisher is used by the input dispatcher to send events to the application.
- * The InputConsumer is used by the application to receive events from the input dispatcher.
- */
-
-#include <androidfw/Input.h>
-#include <utils/Errors.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-#include <utils/BitSet.h>
-
-namespace android {
-
-/*
- * Intermediate representation used to send input events and related signals.
- */
-struct InputMessage {
- enum {
- TYPE_KEY = 1,
- TYPE_MOTION = 2,
- TYPE_FINISHED = 3,
- };
-
- struct Header {
- uint32_t type;
- uint32_t padding; // 8 byte alignment for the body that follows
- } header;
-
- union Body {
- struct Key {
- uint32_t seq;
- nsecs_t eventTime;
- int32_t deviceId;
- int32_t source;
- int32_t action;
- int32_t flags;
- int32_t keyCode;
- int32_t scanCode;
- int32_t metaState;
- int32_t repeatCount;
- nsecs_t downTime;
-
- inline size_t size() const {
- return sizeof(Key);
- }
- } key;
-
- struct Motion {
- uint32_t seq;
- nsecs_t eventTime;
- int32_t deviceId;
- int32_t source;
- int32_t action;
- int32_t flags;
- int32_t metaState;
- int32_t buttonState;
- int32_t edgeFlags;
- nsecs_t downTime;
- float xOffset;
- float yOffset;
- float xPrecision;
- float yPrecision;
- size_t pointerCount;
- struct Pointer {
- PointerProperties properties;
- PointerCoords coords;
- } pointers[MAX_POINTERS];
-
- int32_t getActionId() const {
- uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
- >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
- return pointers[index].properties.id;
- }
-
- inline size_t size() const {
- return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS
- + sizeof(Pointer) * pointerCount;
- }
- } motion;
-
- struct Finished {
- uint32_t seq;
- bool handled;
-
- inline size_t size() const {
- return sizeof(Finished);
- }
- } finished;
- } body;
-
- bool isValid(size_t actualSize) const;
- size_t size() const;
-};
-
-/*
- * An input channel consists of a local unix domain socket used to send and receive
- * input messages across processes. Each channel has a descriptive name for debugging purposes.
- *
- * Each endpoint has its own InputChannel object that specifies its file descriptor.
- *
- * The input channel is closed when all references to it are released.
- */
-class InputChannel : public RefBase {
-protected:
- virtual ~InputChannel();
-
-public:
- InputChannel(const String8& name, int fd);
-
- /* Creates a pair of input channels.
- *
- * Returns OK on success.
- */
- static status_t openInputChannelPair(const String8& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
-
- inline String8 getName() const { return mName; }
- inline int getFd() const { return mFd; }
-
- /* Sends a message to the other endpoint.
- *
- * If the channel is full then the message is guaranteed not to have been sent at all.
- * Try again after the consumer has sent a finished signal indicating that it has
- * consumed some of the pending messages from the channel.
- *
- * Returns OK on success.
- * Returns WOULD_BLOCK if the channel is full.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
- * Other errors probably indicate that the channel is broken.
- */
- status_t sendMessage(const InputMessage* msg);
-
- /* Receives a message sent by the other endpoint.
- *
- * If there is no message present, try again after poll() indicates that the fd
- * is readable.
- *
- * Returns OK on success.
- * Returns WOULD_BLOCK if there is no message present.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
- * Other errors probably indicate that the channel is broken.
- */
- status_t receiveMessage(InputMessage* msg);
-
- /* Returns a new object that has a duplicate of this channel's fd. */
- sp<InputChannel> dup() const;
-
-private:
- String8 mName;
- int mFd;
-};
-
-/*
- * Publishes input events to an input channel.
- */
-class InputPublisher {
-public:
- /* Creates a publisher associated with an input channel. */
- explicit InputPublisher(const sp<InputChannel>& channel);
-
- /* Destroys the publisher and releases its input channel. */
- ~InputPublisher();
-
- /* Gets the underlying input channel. */
- inline sp<InputChannel> getChannel() { return mChannel; }
-
- /* Publishes a key event to the input channel.
- *
- * Returns OK on success.
- * Returns WOULD_BLOCK if the channel is full.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
- * Returns BAD_VALUE if seq is 0.
- * Other errors probably indicate that the channel is broken.
- */
- status_t publishKeyEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime);
-
- /* Publishes a motion event to the input channel.
- *
- * Returns OK on success.
- * Returns WOULD_BLOCK if the channel is full.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
- * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS.
- * Other errors probably indicate that the channel is broken.
- */
- status_t publishMotionEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t edgeFlags,
- int32_t metaState,
- int32_t buttonState,
- float xOffset,
- float yOffset,
- float xPrecision,
- float yPrecision,
- nsecs_t downTime,
- nsecs_t eventTime,
- size_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords);
-
- /* Receives the finished signal from the consumer in reply to the original dispatch signal.
- * If a signal was received, returns the message sequence number,
- * and whether the consumer handled the message.
- *
- * The returned sequence number is never 0 unless the operation failed.
- *
- * Returns OK on success.
- * Returns WOULD_BLOCK if there is no signal present.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
- * Other errors probably indicate that the channel is broken.
- */
- status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
-
-private:
- sp<InputChannel> mChannel;
-};
-
-/*
- * Consumes input events from an input channel.
- */
-class InputConsumer {
-public:
- /* Creates a consumer associated with an input channel. */
- explicit InputConsumer(const sp<InputChannel>& channel);
-
- /* Destroys the consumer and releases its input channel. */
- ~InputConsumer();
-
- /* Gets the underlying input channel. */
- inline sp<InputChannel> getChannel() { return mChannel; }
-
- /* Consumes an input event from the input channel and copies its contents into
- * an InputEvent object created using the specified factory.
- *
- * Tries to combine a series of move events into larger batches whenever possible.
- *
- * If consumeBatches is false, then defers consuming pending batched events if it
- * is possible for additional samples to be added to them later. Call hasPendingBatch()
- * to determine whether a pending batch is available to be consumed.
- *
- * If consumeBatches is true, then events are still batched but they are consumed
- * immediately as soon as the input channel is exhausted.
- *
- * The frameTime parameter specifies the time when the current display frame started
- * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown.
- *
- * The returned sequence number is never 0 unless the operation failed.
- *
- * Returns OK on success.
- * Returns WOULD_BLOCK if there is no event present.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
- * Returns NO_MEMORY if the event could not be created.
- * Other errors probably indicate that the channel is broken.
- */
- status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
-
- /* Sends a finished signal to the publisher to inform it that the message
- * with the specified sequence number has finished being process and whether
- * the message was handled by the consumer.
- *
- * Returns OK on success.
- * Returns BAD_VALUE if seq is 0.
- * Other errors probably indicate that the channel is broken.
- */
- status_t sendFinishedSignal(uint32_t seq, bool handled);
-
- /* Returns true if there is a deferred event waiting.
- *
- * Should be called after calling consume() to determine whether the consumer
- * has a deferred event to be processed. Deferred events are somewhat special in
- * that they have already been removed from the input channel. If the input channel
- * becomes empty, the client may need to do extra work to ensure that it processes
- * the deferred event despite the fact that the input channel's file descriptor
- * is not readable.
- *
- * One option is simply to call consume() in a loop until it returns WOULD_BLOCK.
- * This guarantees that all deferred events will be processed.
- *
- * Alternately, the caller can call hasDeferredEvent() to determine whether there is
- * a deferred event waiting and then ensure that its event loop wakes up at least
- * one more time to consume the deferred event.
- */
- bool hasDeferredEvent() const;
-
- /* Returns true if there is a pending batch.
- *
- * Should be called after calling consume() with consumeBatches == false to determine
- * whether consume() should be called again later on with consumeBatches == true.
- */
- bool hasPendingBatch() const;
-
-private:
- // True if touch resampling is enabled.
- const bool mResampleTouch;
-
- // The input channel.
- sp<InputChannel> mChannel;
-
- // The current input message.
- InputMessage mMsg;
-
- // True if mMsg contains a valid input message that was deferred from the previous
- // call to consume and that still needs to be handled.
- bool mMsgDeferred;
-
- // Batched motion events per device and source.
- struct Batch {
- Vector<InputMessage> samples;
- };
- Vector<Batch> mBatches;
-
- // Touch state per device and source, only for sources of class pointer.
- struct History {
- nsecs_t eventTime;
- BitSet32 idBits;
- int32_t idToIndex[MAX_POINTER_ID + 1];
- PointerCoords pointers[MAX_POINTERS];
-
- void initializeFrom(const InputMessage* msg) {
- eventTime = msg->body.motion.eventTime;
- idBits.clear();
- for (size_t i = 0; i < msg->body.motion.pointerCount; i++) {
- uint32_t id = msg->body.motion.pointers[i].properties.id;
- idBits.markBit(id);
- idToIndex[id] = i;
- pointers[i].copyFrom(msg->body.motion.pointers[i].coords);
- }
- }
-
- const PointerCoords& getPointerById(uint32_t id) const {
- return pointers[idToIndex[id]];
- }
- };
- struct TouchState {
- int32_t deviceId;
- int32_t source;
- size_t historyCurrent;
- size_t historySize;
- History history[2];
- History lastResample;
-
- void initialize(int32_t deviceId, int32_t source) {
- this->deviceId = deviceId;
- this->source = source;
- historyCurrent = 0;
- historySize = 0;
- lastResample.eventTime = 0;
- lastResample.idBits.clear();
- }
-
- void addHistory(const InputMessage* msg) {
- historyCurrent ^= 1;
- if (historySize < 2) {
- historySize += 1;
- }
- history[historyCurrent].initializeFrom(msg);
- }
-
- const History* getHistory(size_t index) const {
- return &history[(historyCurrent + index) & 1];
- }
- };
- Vector<TouchState> mTouchStates;
-
- // Chain of batched sequence numbers. When multiple input messages are combined into
- // a batch, we append a record here that associates the last sequence number in the
- // batch with the previous one. When the finished signal is sent, we traverse the
- // chain to individually finish all input messages that were part of the batch.
- struct SeqChain {
- uint32_t seq; // sequence number of batched input message
- uint32_t chain; // sequence number of previous batched input message
- };
- Vector<SeqChain> mSeqChains;
-
- status_t consumeBatch(InputEventFactoryInterface* factory,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
- status_t consumeSamples(InputEventFactoryInterface* factory,
- Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent);
-
- void updateTouchState(InputMessage* msg);
- void rewriteMessage(const TouchState& state, InputMessage* msg);
- void resampleTouchState(nsecs_t frameTime, MotionEvent* event,
- const InputMessage *next);
-
- ssize_t findBatch(int32_t deviceId, int32_t source) const;
- ssize_t findTouchState(int32_t deviceId, int32_t source) const;
-
- status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
-
- static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
- static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
- static void addSample(MotionEvent* event, const InputMessage* msg);
- static bool canAddSample(const Batch& batch, const InputMessage* msg);
- static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
- static bool shouldResampleTool(int32_t toolType);
-
- static bool isTouchResamplingEnabled();
-};
-
-} // namespace android
-
-#endif // _ANDROIDFW_INPUT_TRANSPORT_H
diff --git a/include/androidfw/KeyCharacterMap.h b/include/androidfw/KeyCharacterMap.h
deleted file mode 100644
index 06799f9..0000000
--- a/include/androidfw/KeyCharacterMap.h
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _ANDROIDFW_KEY_CHARACTER_MAP_H
-#define _ANDROIDFW_KEY_CHARACTER_MAP_H
-
-#include <stdint.h>
-
-#if HAVE_ANDROID_OS
-#include <binder/IBinder.h>
-#endif
-
-#include <androidfw/Input.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/Tokenizer.h>
-#include <utils/String8.h>
-#include <utils/Unicode.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-/**
- * Describes a mapping from Android key codes to characters.
- * Also specifies other functions of the keyboard such as the keyboard type
- * and key modifier semantics.
- *
- * This object is immutable after it has been loaded.
- */
-class KeyCharacterMap : public RefBase {
-public:
- enum KeyboardType {
- KEYBOARD_TYPE_UNKNOWN = 0,
- KEYBOARD_TYPE_NUMERIC = 1,
- KEYBOARD_TYPE_PREDICTIVE = 2,
- KEYBOARD_TYPE_ALPHA = 3,
- KEYBOARD_TYPE_FULL = 4,
- KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
- KEYBOARD_TYPE_OVERLAY = 6,
- };
-
- enum Format {
- // Base keyboard layout, may contain device-specific options, such as "type" declaration.
- FORMAT_BASE = 0,
- // Overlay keyboard layout, more restrictive, may be published by applications,
- // cannot override device-specific options.
- FORMAT_OVERLAY = 1,
- // Either base or overlay layout ok.
- FORMAT_ANY = 2,
- };
-
- // Substitute key code and meta state for fallback action.
- struct FallbackAction {
- int32_t keyCode;
- int32_t metaState;
- };
-
- /* Loads a key character map from a file. */
- static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap);
-
- /* Loads a key character map from its string contents. */
- static status_t loadContents(const String8& filename,
- const char* contents, Format format, sp<KeyCharacterMap>* outMap);
-
- /* Combines a base key character map and an overlay. */
- static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
- const sp<KeyCharacterMap>& overlay);
-
- /* Returns an empty key character map. */
- static sp<KeyCharacterMap> empty();
-
- /* Gets the keyboard type. */
- int32_t getKeyboardType() const;
-
- /* Gets the primary character for this key as in the label physically printed on it.
- * Returns 0 if none (eg. for non-printing keys). */
- char16_t getDisplayLabel(int32_t keyCode) const;
-
- /* Gets the Unicode character for the number or symbol generated by the key
- * when the keyboard is used as a dialing pad.
- * Returns 0 if no number or symbol is generated.
- */
- char16_t getNumber(int32_t keyCode) const;
-
- /* Gets the Unicode character generated by the key and meta key modifiers.
- * Returns 0 if no character is generated.
- */
- char16_t getCharacter(int32_t keyCode, int32_t metaState) const;
-
- /* Gets the fallback action to use by default if the application does not
- * handle the specified key.
- * Returns true if an action was available, false if none.
- */
- bool getFallbackAction(int32_t keyCode, int32_t metaState,
- FallbackAction* outFallbackAction) const;
-
- /* Gets the first matching Unicode character that can be generated by the key,
- * preferring the one with the specified meta key modifiers.
- * Returns 0 if no matching character is generated.
- */
- char16_t getMatch(int32_t keyCode, const char16_t* chars,
- size_t numChars, int32_t metaState) const;
-
- /* Gets a sequence of key events that could plausibly generate the specified
- * character sequence. Returns false if some of the characters cannot be generated.
- */
- bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
- Vector<KeyEvent>& outEvents) const;
-
- /* Maps a scan code and usage code to a key code, in case this key map overrides
- * the mapping in some way. */
- status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const;
-
-#if HAVE_ANDROID_OS
- /* Reads a key map from a parcel. */
- static sp<KeyCharacterMap> readFromParcel(Parcel* parcel);
-
- /* Writes a key map to a parcel. */
- void writeToParcel(Parcel* parcel) const;
-#endif
-
-protected:
- virtual ~KeyCharacterMap();
-
-private:
- struct Behavior {
- Behavior();
- Behavior(const Behavior& other);
-
- /* The next behavior in the list, or NULL if none. */
- Behavior* next;
-
- /* The meta key modifiers for this behavior. */
- int32_t metaState;
-
- /* The character to insert. */
- char16_t character;
-
- /* The fallback keycode if the key is not handled. */
- int32_t fallbackKeyCode;
- };
-
- struct Key {
- Key();
- Key(const Key& other);
- ~Key();
-
- /* The single character label printed on the key, or 0 if none. */
- char16_t label;
-
- /* The number or symbol character generated by the key, or 0 if none. */
- char16_t number;
-
- /* The list of key behaviors sorted from most specific to least specific
- * meta key binding. */
- Behavior* firstBehavior;
- };
-
- class Parser {
- enum State {
- STATE_TOP = 0,
- STATE_KEY = 1,
- };
-
- enum {
- PROPERTY_LABEL = 1,
- PROPERTY_NUMBER = 2,
- PROPERTY_META = 3,
- };
-
- struct Property {
- inline Property(int32_t property = 0, int32_t metaState = 0) :
- property(property), metaState(metaState) { }
-
- int32_t property;
- int32_t metaState;
- };
-
- KeyCharacterMap* mMap;
- Tokenizer* mTokenizer;
- Format mFormat;
- State mState;
- int32_t mKeyCode;
-
- public:
- Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format);
- ~Parser();
- status_t parse();
-
- private:
- status_t parseType();
- status_t parseMap();
- status_t parseMapKey();
- status_t parseKey();
- status_t parseKeyProperty();
- status_t finishKey(Key* key);
- status_t parseModifier(const String8& token, int32_t* outMetaState);
- status_t parseCharacterLiteral(char16_t* outCharacter);
- };
-
- static sp<KeyCharacterMap> sEmpty;
-
- KeyedVector<int32_t, Key*> mKeys;
- int mType;
-
- KeyedVector<int32_t, int32_t> mKeysByScanCode;
- KeyedVector<int32_t, int32_t> mKeysByUsageCode;
-
- KeyCharacterMap();
- KeyCharacterMap(const KeyCharacterMap& other);
-
- bool getKey(int32_t keyCode, const Key** outKey) const;
- bool getKeyBehavior(int32_t keyCode, int32_t metaState,
- const Key** outKey, const Behavior** outBehavior) const;
- static bool matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState);
-
- bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
-
- static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
-
- static void addKey(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
- static void addMetaKeys(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
- int32_t* currentMetaState);
- static bool addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
- int32_t keyCode, int32_t keyMetaState,
- int32_t* currentMetaState);
- static void addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
- int32_t leftKeyCode, int32_t leftKeyMetaState,
- int32_t rightKeyCode, int32_t rightKeyMetaState,
- int32_t eitherKeyMetaState,
- int32_t* currentMetaState);
- static void addLockedMetaKey(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t metaState, nsecs_t time,
- int32_t keyCode, int32_t keyMetaState,
- int32_t* currentMetaState);
-};
-
-} // namespace android
-
-#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H
diff --git a/include/androidfw/KeyLayoutMap.h b/include/androidfw/KeyLayoutMap.h
deleted file mode 100644
index e7f22a2..0000000
--- a/include/androidfw/KeyLayoutMap.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _ANDROIDFW_KEY_LAYOUT_MAP_H
-#define _ANDROIDFW_KEY_LAYOUT_MAP_H
-
-#include <stdint.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/Tokenizer.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-struct AxisInfo {
- enum Mode {
- // Axis value is reported directly.
- MODE_NORMAL = 0,
- // Axis value should be inverted before reporting.
- MODE_INVERT = 1,
- // Axis value should be split into two axes
- MODE_SPLIT = 2,
- };
-
- // Axis mode.
- Mode mode;
-
- // Axis id.
- // When split, this is the axis used for values smaller than the split position.
- int32_t axis;
-
- // When split, this is the axis used for values after higher than the split position.
- int32_t highAxis;
-
- // The split value, or 0 if not split.
- int32_t splitValue;
-
- // The flat value, or -1 if none.
- int32_t flatOverride;
-
- AxisInfo() : mode(MODE_NORMAL), axis(-1), highAxis(-1), splitValue(0), flatOverride(-1) {
- }
-};
-
-/**
- * Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes.
- *
- * This object is immutable after it has been loaded.
- */
-class KeyLayoutMap : public RefBase {
-public:
- static status_t load(const String8& filename, sp<KeyLayoutMap>* outMap);
-
- status_t mapKey(int32_t scanCode, int32_t usageCode,
- int32_t* outKeyCode, uint32_t* outFlags) const;
- status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
-
- status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
-
-protected:
- virtual ~KeyLayoutMap();
-
-private:
- struct Key {
- int32_t keyCode;
- uint32_t flags;
- };
-
- KeyedVector<int32_t, Key> mKeysByScanCode;
- KeyedVector<int32_t, Key> mKeysByUsageCode;
- KeyedVector<int32_t, AxisInfo> mAxes;
-
- KeyLayoutMap();
-
- const Key* getKey(int32_t scanCode, int32_t usageCode) const;
-
- class Parser {
- KeyLayoutMap* mMap;
- Tokenizer* mTokenizer;
-
- public:
- Parser(KeyLayoutMap* map, Tokenizer* tokenizer);
- ~Parser();
- status_t parse();
-
- private:
- status_t parseKey();
- status_t parseAxis();
- };
-};
-
-} // namespace android
-
-#endif // _ANDROIDFW_KEY_LAYOUT_MAP_H
diff --git a/include/androidfw/Keyboard.h b/include/androidfw/Keyboard.h
deleted file mode 100644
index 6537a8f..0000000
--- a/include/androidfw/Keyboard.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROIDFW_KEYBOARD_H
-#define _ANDROIDFW_KEYBOARD_H
-
-#include <androidfw/Input.h>
-#include <androidfw/InputDevice.h>
-#include <utils/Errors.h>
-#include <utils/String8.h>
-#include <utils/PropertyMap.h>
-
-namespace android {
-
-enum {
- /* Device id of the built in keyboard. */
- DEVICE_ID_BUILT_IN_KEYBOARD = 0,
-
- /* Device id of a generic virtual keyboard with a full layout that can be used
- * to synthesize key events. */
- DEVICE_ID_VIRTUAL_KEYBOARD = -1,
-};
-
-class KeyLayoutMap;
-class KeyCharacterMap;
-
-/**
- * Loads the key layout map and key character map for a keyboard device.
- */
-class KeyMap {
-public:
- String8 keyLayoutFile;
- sp<KeyLayoutMap> keyLayoutMap;
-
- String8 keyCharacterMapFile;
- sp<KeyCharacterMap> keyCharacterMap;
-
- KeyMap();
- ~KeyMap();
-
- status_t load(const InputDeviceIdentifier& deviceIdenfier,
- const PropertyMap* deviceConfiguration);
-
- inline bool haveKeyLayout() const {
- return !keyLayoutFile.isEmpty();
- }
-
- inline bool haveKeyCharacterMap() const {
- return !keyCharacterMapFile.isEmpty();
- }
-
- inline bool isComplete() const {
- return haveKeyLayout() && haveKeyCharacterMap();
- }
-
-private:
- bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
- status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
- status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name);
- String8 getPath(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name, InputDeviceConfigurationFileType type);
-};
-
-/**
- * Returns true if the keyboard is eligible for use as a built-in keyboard.
- */
-extern bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
- const PropertyMap* deviceConfiguration, const KeyMap* keyMap);
-
-/**
- * Gets a key code by its short form label, eg. "HOME".
- * Returns 0 if unknown.
- */
-extern int32_t getKeyCodeByLabel(const char* label);
-
-/**
- * Gets a key flag by its short form label, eg. "WAKE".
- * Returns 0 if unknown.
- */
-extern uint32_t getKeyFlagByLabel(const char* label);
-
-/**
- * Gets a axis by its short form label, eg. "X".
- * Returns -1 if unknown.
- */
-extern int32_t getAxisByLabel(const char* label);
-
-/**
- * Gets a axis label by its id.
- * Returns NULL if unknown.
- */
-extern const char* getAxisLabel(int32_t axisId);
-
-/**
- * Updates a meta state field when a key is pressed or released.
- */
-extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
-
-/**
- * Returns true if a key is a meta key like ALT or CAPS_LOCK.
- */
-extern bool isMetaKey(int32_t keyCode);
-
-} // namespace android
-
-#endif // _ANDROIDFW_KEYBOARD_H
diff --git a/include/androidfw/KeycodeLabels.h b/include/androidfw/KeycodeLabels.h
deleted file mode 100644
index 3e12f26..0000000
--- a/include/androidfw/KeycodeLabels.h
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _ANDROIDFW_KEYCODE_LABELS_H
-#define _ANDROIDFW_KEYCODE_LABELS_H
-
-#include <android/keycodes.h>
-
-struct KeycodeLabel {
- const char *literal;
- int value;
-};
-
-static const KeycodeLabel KEYCODES[] = {
- { "SOFT_LEFT", 1 },
- { "SOFT_RIGHT", 2 },
- { "HOME", 3 },
- { "BACK", 4 },
- { "CALL", 5 },
- { "ENDCALL", 6 },
- { "0", 7 },
- { "1", 8 },
- { "2", 9 },
- { "3", 10 },
- { "4", 11 },
- { "5", 12 },
- { "6", 13 },
- { "7", 14 },
- { "8", 15 },
- { "9", 16 },
- { "STAR", 17 },
- { "POUND", 18 },
- { "DPAD_UP", 19 },
- { "DPAD_DOWN", 20 },
- { "DPAD_LEFT", 21 },
- { "DPAD_RIGHT", 22 },
- { "DPAD_CENTER", 23 },
- { "VOLUME_UP", 24 },
- { "VOLUME_DOWN", 25 },
- { "POWER", 26 },
- { "CAMERA", 27 },
- { "CLEAR", 28 },
- { "A", 29 },
- { "B", 30 },
- { "C", 31 },
- { "D", 32 },
- { "E", 33 },
- { "F", 34 },
- { "G", 35 },
- { "H", 36 },
- { "I", 37 },
- { "J", 38 },
- { "K", 39 },
- { "L", 40 },
- { "M", 41 },
- { "N", 42 },
- { "O", 43 },
- { "P", 44 },
- { "Q", 45 },
- { "R", 46 },
- { "S", 47 },
- { "T", 48 },
- { "U", 49 },
- { "V", 50 },
- { "W", 51 },
- { "X", 52 },
- { "Y", 53 },
- { "Z", 54 },
- { "COMMA", 55 },
- { "PERIOD", 56 },
- { "ALT_LEFT", 57 },
- { "ALT_RIGHT", 58 },
- { "SHIFT_LEFT", 59 },
- { "SHIFT_RIGHT", 60 },
- { "TAB", 61 },
- { "SPACE", 62 },
- { "SYM", 63 },
- { "EXPLORER", 64 },
- { "ENVELOPE", 65 },
- { "ENTER", 66 },
- { "DEL", 67 },
- { "GRAVE", 68 },
- { "MINUS", 69 },
- { "EQUALS", 70 },
- { "LEFT_BRACKET", 71 },
- { "RIGHT_BRACKET", 72 },
- { "BACKSLASH", 73 },
- { "SEMICOLON", 74 },
- { "APOSTROPHE", 75 },
- { "SLASH", 76 },
- { "AT", 77 },
- { "NUM", 78 },
- { "HEADSETHOOK", 79 },
- { "FOCUS", 80 },
- { "PLUS", 81 },
- { "MENU", 82 },
- { "NOTIFICATION", 83 },
- { "SEARCH", 84 },
- { "MEDIA_PLAY_PAUSE", 85 },
- { "MEDIA_STOP", 86 },
- { "MEDIA_NEXT", 87 },
- { "MEDIA_PREVIOUS", 88 },
- { "MEDIA_REWIND", 89 },
- { "MEDIA_FAST_FORWARD", 90 },
- { "MUTE", 91 },
- { "PAGE_UP", 92 },
- { "PAGE_DOWN", 93 },
- { "PICTSYMBOLS", 94 },
- { "SWITCH_CHARSET", 95 },
- { "BUTTON_A", 96 },
- { "BUTTON_B", 97 },
- { "BUTTON_C", 98 },
- { "BUTTON_X", 99 },
- { "BUTTON_Y", 100 },
- { "BUTTON_Z", 101 },
- { "BUTTON_L1", 102 },
- { "BUTTON_R1", 103 },
- { "BUTTON_L2", 104 },
- { "BUTTON_R2", 105 },
- { "BUTTON_THUMBL", 106 },
- { "BUTTON_THUMBR", 107 },
- { "BUTTON_START", 108 },
- { "BUTTON_SELECT", 109 },
- { "BUTTON_MODE", 110 },
- { "ESCAPE", 111 },
- { "FORWARD_DEL", 112 },
- { "CTRL_LEFT", 113 },
- { "CTRL_RIGHT", 114 },
- { "CAPS_LOCK", 115 },
- { "SCROLL_LOCK", 116 },
- { "META_LEFT", 117 },
- { "META_RIGHT", 118 },
- { "FUNCTION", 119 },
- { "SYSRQ", 120 },
- { "BREAK", 121 },
- { "MOVE_HOME", 122 },
- { "MOVE_END", 123 },
- { "INSERT", 124 },
- { "FORWARD", 125 },
- { "MEDIA_PLAY", 126 },
- { "MEDIA_PAUSE", 127 },
- { "MEDIA_CLOSE", 128 },
- { "MEDIA_EJECT", 129 },
- { "MEDIA_RECORD", 130 },
- { "F1", 131 },
- { "F2", 132 },
- { "F3", 133 },
- { "F4", 134 },
- { "F5", 135 },
- { "F6", 136 },
- { "F7", 137 },
- { "F8", 138 },
- { "F9", 139 },
- { "F10", 140 },
- { "F11", 141 },
- { "F12", 142 },
- { "NUM_LOCK", 143 },
- { "NUMPAD_0", 144 },
- { "NUMPAD_1", 145 },
- { "NUMPAD_2", 146 },
- { "NUMPAD_3", 147 },
- { "NUMPAD_4", 148 },
- { "NUMPAD_5", 149 },
- { "NUMPAD_6", 150 },
- { "NUMPAD_7", 151 },
- { "NUMPAD_8", 152 },
- { "NUMPAD_9", 153 },
- { "NUMPAD_DIVIDE", 154 },
- { "NUMPAD_MULTIPLY", 155 },
- { "NUMPAD_SUBTRACT", 156 },
- { "NUMPAD_ADD", 157 },
- { "NUMPAD_DOT", 158 },
- { "NUMPAD_COMMA", 159 },
- { "NUMPAD_ENTER", 160 },
- { "NUMPAD_EQUALS", 161 },
- { "NUMPAD_LEFT_PAREN", 162 },
- { "NUMPAD_RIGHT_PAREN", 163 },
- { "VOLUME_MUTE", 164 },
- { "INFO", 165 },
- { "CHANNEL_UP", 166 },
- { "CHANNEL_DOWN", 167 },
- { "ZOOM_IN", 168 },
- { "ZOOM_OUT", 169 },
- { "TV", 170 },
- { "WINDOW", 171 },
- { "GUIDE", 172 },
- { "DVR", 173 },
- { "BOOKMARK", 174 },
- { "CAPTIONS", 175 },
- { "SETTINGS", 176 },
- { "TV_POWER", 177 },
- { "TV_INPUT", 178 },
- { "STB_POWER", 179 },
- { "STB_INPUT", 180 },
- { "AVR_POWER", 181 },
- { "AVR_INPUT", 182 },
- { "PROG_RED", 183 },
- { "PROG_GREEN", 184 },
- { "PROG_YELLOW", 185 },
- { "PROG_BLUE", 186 },
- { "APP_SWITCH", 187 },
- { "BUTTON_1", 188 },
- { "BUTTON_2", 189 },
- { "BUTTON_3", 190 },
- { "BUTTON_4", 191 },
- { "BUTTON_5", 192 },
- { "BUTTON_6", 193 },
- { "BUTTON_7", 194 },
- { "BUTTON_8", 195 },
- { "BUTTON_9", 196 },
- { "BUTTON_10", 197 },
- { "BUTTON_11", 198 },
- { "BUTTON_12", 199 },
- { "BUTTON_13", 200 },
- { "BUTTON_14", 201 },
- { "BUTTON_15", 202 },
- { "BUTTON_16", 203 },
- { "LANGUAGE_SWITCH", 204 },
- { "MANNER_MODE", 205 },
- { "3D_MODE", 206 },
- { "CONTACTS", 207 },
- { "CALENDAR", 208 },
- { "MUSIC", 209 },
- { "CALCULATOR", 210 },
- { "ZENKAKU_HANKAKU", 211 },
- { "EISU", 212 },
- { "MUHENKAN", 213 },
- { "HENKAN", 214 },
- { "KATAKANA_HIRAGANA", 215 },
- { "YEN", 216 },
- { "RO", 217 },
- { "KANA", 218 },
- { "ASSIST", 219 },
- { "BRIGHTNESS_DOWN", 220 },
- { "BRIGHTNESS_UP", 221 },
-
- // NOTE: If you add a new keycode here you must also add it to several other files.
- // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
-
- { NULL, 0 }
-};
-
-// NOTE: If you edit these flags, also edit policy flags in Input.h.
-static const KeycodeLabel FLAGS[] = {
- { "WAKE", 0x00000001 },
- { "WAKE_DROPPED", 0x00000002 },
- { "SHIFT", 0x00000004 },
- { "CAPS_LOCK", 0x00000008 },
- { "ALT", 0x00000010 },
- { "ALT_GR", 0x00000020 },
- { "MENU", 0x00000040 },
- { "LAUNCHER", 0x00000080 },
- { "VIRTUAL", 0x00000100 },
- { "FUNCTION", 0x00000200 },
- { NULL, 0 }
-};
-
-static const KeycodeLabel AXES[] = {
- { "X", 0 },
- { "Y", 1 },
- { "PRESSURE", 2 },
- { "SIZE", 3 },
- { "TOUCH_MAJOR", 4 },
- { "TOUCH_MINOR", 5 },
- { "TOOL_MAJOR", 6 },
- { "TOOL_MINOR", 7 },
- { "ORIENTATION", 8 },
- { "VSCROLL", 9 },
- { "HSCROLL", 10 },
- { "Z", 11 },
- { "RX", 12 },
- { "RY", 13 },
- { "RZ", 14 },
- { "HAT_X", 15 },
- { "HAT_Y", 16 },
- { "LTRIGGER", 17 },
- { "RTRIGGER", 18 },
- { "THROTTLE", 19 },
- { "RUDDER", 20 },
- { "WHEEL", 21 },
- { "GAS", 22 },
- { "BRAKE", 23 },
- { "DISTANCE", 24 },
- { "TILT", 25 },
- { "GENERIC_1", 32 },
- { "GENERIC_2", 33 },
- { "GENERIC_3", 34 },
- { "GENERIC_4", 35 },
- { "GENERIC_5", 36 },
- { "GENERIC_6", 37 },
- { "GENERIC_7", 38 },
- { "GENERIC_8", 39 },
- { "GENERIC_9", 40 },
- { "GENERIC_10", 41 },
- { "GENERIC_11", 42 },
- { "GENERIC_12", 43 },
- { "GENERIC_13", 44 },
- { "GENERIC_14", 45 },
- { "GENERIC_15", 46 },
- { "GENERIC_16", 47 },
-
- // NOTE: If you add a new axis here you must also add it to several other files.
- // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
-
- { NULL, -1 }
-};
-
-#endif // _ANDROIDFW_KEYCODE_LABELS_H
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 5b45d70..a305fc3 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1553,10 +1553,8 @@
static bool getIdmapInfo(const void* idmap, size_t size,
uint32_t* pOriginalCrc, uint32_t* pOverlayCrc);
-#ifndef HAVE_ANDROID_OS
void print(bool inclValues) const;
static String8 normalizeForOutput(const char* input);
-#endif
private:
struct Header;
diff --git a/include/androidfw/VelocityControl.h b/include/androidfw/VelocityControl.h
deleted file mode 100644
index 84e0444..0000000
--- a/include/androidfw/VelocityControl.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROIDFW_VELOCITY_CONTROL_H
-#define _ANDROIDFW_VELOCITY_CONTROL_H
-
-#include <androidfw/Input.h>
-#include <androidfw/VelocityTracker.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-/*
- * Specifies parameters that govern pointer or wheel acceleration.
- */
-struct VelocityControlParameters {
- // A scale factor that is multiplied with the raw velocity deltas
- // prior to applying any other velocity control factors. The scale
- // factor should be used to adapt the input device resolution
- // (eg. counts per inch) to the output device resolution (eg. pixels per inch).
- //
- // Must be a positive value.
- // Default is 1.0 (no scaling).
- float scale;
-
- // The scaled speed at which acceleration begins to be applied.
- // This value establishes the upper bound of a low speed regime for
- // small precise motions that are performed without any acceleration.
- //
- // Must be a non-negative value.
- // Default is 0.0 (no low threshold).
- float lowThreshold;
-
- // The scaled speed at which maximum acceleration is applied.
- // The difference between highThreshold and lowThreshold controls
- // the range of speeds over which the acceleration factor is interpolated.
- // The wider the range, the smoother the acceleration.
- //
- // Must be a non-negative value greater than or equal to lowThreshold.
- // Default is 0.0 (no high threshold).
- float highThreshold;
-
- // The acceleration factor.
- // When the speed is above the low speed threshold, the velocity will scaled
- // by an interpolated value between 1.0 and this amount.
- //
- // Must be a positive greater than or equal to 1.0.
- // Default is 1.0 (no acceleration).
- float acceleration;
-
- VelocityControlParameters() :
- scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) {
- }
-
- VelocityControlParameters(float scale, float lowThreshold,
- float highThreshold, float acceleration) :
- scale(scale), lowThreshold(lowThreshold),
- highThreshold(highThreshold), acceleration(acceleration) {
- }
-};
-
-/*
- * Implements mouse pointer and wheel speed control and acceleration.
- */
-class VelocityControl {
-public:
- VelocityControl();
-
- /* Sets the various parameters. */
- void setParameters(const VelocityControlParameters& parameters);
-
- /* Resets the current movement counters to zero.
- * This has the effect of nullifying any acceleration. */
- void reset();
-
- /* Translates a raw movement delta into an appropriately
- * scaled / accelerated delta based on the current velocity. */
- void move(nsecs_t eventTime, float* deltaX, float* deltaY);
-
-private:
- // If no movements are received within this amount of time,
- // we assume the movement has stopped and reset the movement counters.
- static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms
-
- VelocityControlParameters mParameters;
-
- nsecs_t mLastMovementTime;
- VelocityTracker::Position mRawPosition;
- VelocityTracker mVelocityTracker;
-};
-
-} // namespace android
-
-#endif // _ANDROIDFW_VELOCITY_CONTROL_H
diff --git a/include/androidfw/VelocityTracker.h b/include/androidfw/VelocityTracker.h
deleted file mode 100644
index 8c24219..0000000
--- a/include/androidfw/VelocityTracker.h
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROIDFW_VELOCITY_TRACKER_H
-#define _ANDROIDFW_VELOCITY_TRACKER_H
-
-#include <androidfw/Input.h>
-#include <utils/Timers.h>
-#include <utils/BitSet.h>
-
-namespace android {
-
-class VelocityTrackerStrategy;
-
-/*
- * Calculates the velocity of pointer movements over time.
- */
-class VelocityTracker {
-public:
- struct Position {
- float x, y;
- };
-
- struct Estimator {
- static const size_t MAX_DEGREE = 4;
-
- // Estimator time base.
- nsecs_t time;
-
- // Polynomial coefficients describing motion in X and Y.
- float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
-
- // Polynomial degree (number of coefficients), or zero if no information is
- // available.
- uint32_t degree;
-
- // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
- float confidence;
-
- inline void clear() {
- time = 0;
- degree = 0;
- confidence = 0;
- for (size_t i = 0; i <= MAX_DEGREE; i++) {
- xCoeff[i] = 0;
- yCoeff[i] = 0;
- }
- }
- };
-
- // Creates a velocity tracker using the specified strategy.
- // If strategy is NULL, uses the default strategy for the platform.
- VelocityTracker(const char* strategy = NULL);
-
- ~VelocityTracker();
-
- // Resets the velocity tracker state.
- void clear();
-
- // Resets the velocity tracker state for specific pointers.
- // Call this method when some pointers have changed and may be reusing
- // an id that was assigned to a different pointer earlier.
- void clearPointers(BitSet32 idBits);
-
- // Adds movement information for a set of pointers.
- // The idBits bitfield specifies the pointer ids of the pointers whose positions
- // are included in the movement.
- // The positions array contains position information for each pointer in order by
- // increasing id. Its size should be equal to the number of one bits in idBits.
- void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
-
- // Adds movement information for all pointers in a MotionEvent, including historical samples.
- void addMovement(const MotionEvent* event);
-
- // Gets the velocity of the specified pointer id in position units per second.
- // Returns false and sets the velocity components to zero if there is
- // insufficient movement information for the pointer.
- bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
-
- // Gets an estimator for the recent movements of the specified pointer id.
- // Returns false and clears the estimator if there is no information available
- // about the pointer.
- bool getEstimator(uint32_t id, Estimator* outEstimator) const;
-
- // Gets the active pointer id, or -1 if none.
- inline int32_t getActivePointerId() const { return mActivePointerId; }
-
- // Gets a bitset containing all pointer ids from the most recent movement.
- inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
-
-private:
- static const char* DEFAULT_STRATEGY;
-
- nsecs_t mLastEventTime;
- BitSet32 mCurrentPointerIdBits;
- int32_t mActivePointerId;
- VelocityTrackerStrategy* mStrategy;
-
- bool configureStrategy(const char* strategy);
-
- static VelocityTrackerStrategy* createStrategy(const char* strategy);
-};
-
-
-/*
- * Implements a particular velocity tracker algorithm.
- */
-class VelocityTrackerStrategy {
-protected:
- VelocityTrackerStrategy() { }
-
-public:
- virtual ~VelocityTrackerStrategy() { }
-
- virtual void clear() = 0;
- virtual void clearPointers(BitSet32 idBits) = 0;
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) = 0;
- virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
-};
-
-
-/*
- * Velocity tracker algorithm based on least-squares linear regression.
- */
-class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
-public:
- enum Weighting {
- // No weights applied. All data points are equally reliable.
- WEIGHTING_NONE,
-
- // Weight by time delta. Data points clustered together are weighted less.
- WEIGHTING_DELTA,
-
- // Weight such that points within a certain horizon are weighed more than those
- // outside of that horizon.
- WEIGHTING_CENTRAL,
-
- // Weight such that points older than a certain amount are weighed less.
- WEIGHTING_RECENT,
- };
-
- // Degree must be no greater than Estimator::MAX_DEGREE.
- LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE);
- virtual ~LeastSquaresVelocityTrackerStrategy();
-
- virtual void clear();
- virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
- virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
-
-private:
- // Sample horizon.
- // We don't use too much history by default since we want to react to quick
- // changes in direction.
- static const nsecs_t HORIZON = 100 * 1000000; // 100 ms
-
- // Number of samples to keep.
- static const uint32_t HISTORY_SIZE = 20;
-
- struct Movement {
- nsecs_t eventTime;
- BitSet32 idBits;
- VelocityTracker::Position positions[MAX_POINTERS];
-
- inline const VelocityTracker::Position& getPosition(uint32_t id) const {
- return positions[idBits.getIndexOfBit(id)];
- }
- };
-
- float chooseWeight(uint32_t index) const;
-
- const uint32_t mDegree;
- const Weighting mWeighting;
- uint32_t mIndex;
- Movement mMovements[HISTORY_SIZE];
-};
-
-
-/*
- * Velocity tracker algorithm that uses an IIR filter.
- */
-class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy {
-public:
- // Degree must be 1 or 2.
- IntegratingVelocityTrackerStrategy(uint32_t degree);
- ~IntegratingVelocityTrackerStrategy();
-
- virtual void clear();
- virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
- virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
-
-private:
- // Current state estimate for a particular pointer.
- struct State {
- nsecs_t updateTime;
- uint32_t degree;
-
- float xpos, xvel, xaccel;
- float ypos, yvel, yaccel;
- };
-
- const uint32_t mDegree;
- BitSet32 mPointerIdBits;
- State mPointerState[MAX_POINTER_ID + 1];
-
- void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
- void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
- void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
-};
-
-
-/*
- * Velocity tracker strategy used prior to ICS.
- */
-class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy {
-public:
- LegacyVelocityTrackerStrategy();
- virtual ~LegacyVelocityTrackerStrategy();
-
- virtual void clear();
- virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
- virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
-
-private:
- // Oldest sample to consider when calculating the velocity.
- static const nsecs_t HORIZON = 200 * 1000000; // 100 ms
-
- // Number of samples to keep.
- static const uint32_t HISTORY_SIZE = 20;
-
- // The minimum duration between samples when estimating velocity.
- static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
-
- struct Movement {
- nsecs_t eventTime;
- BitSet32 idBits;
- VelocityTracker::Position positions[MAX_POINTERS];
-
- inline const VelocityTracker::Position& getPosition(uint32_t id) const {
- return positions[idBits.getIndexOfBit(id)];
- }
- };
-
- uint32_t mIndex;
- Movement mMovements[HISTORY_SIZE];
-};
-
-} // namespace android
-
-#endif // _ANDROIDFW_VELOCITY_TRACKER_H
diff --git a/include/androidfw/VirtualKeyMap.h b/include/androidfw/VirtualKeyMap.h
deleted file mode 100644
index dd3ad1e9..0000000
--- a/include/androidfw/VirtualKeyMap.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROIDFW_VIRTUAL_KEY_MAP_H
-#define _ANDROIDFW_VIRTUAL_KEY_MAP_H
-
-#include <stdint.h>
-
-#include <androidfw/Input.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/Tokenizer.h>
-#include <utils/String8.h>
-#include <utils/Unicode.h>
-
-namespace android {
-
-/* Describes a virtual key. */
-struct VirtualKeyDefinition {
- int32_t scanCode;
-
- // configured position data, specified in display coords
- int32_t centerX;
- int32_t centerY;
- int32_t width;
- int32_t height;
-};
-
-
-/**
- * Describes a collection of virtual keys on a touch screen in terms of
- * virtual scan codes and hit rectangles.
- *
- * This object is immutable after it has been loaded.
- */
-class VirtualKeyMap {
-public:
- ~VirtualKeyMap();
-
- static status_t load(const String8& filename, VirtualKeyMap** outMap);
-
- inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const {
- return mVirtualKeys;
- }
-
-private:
- class Parser {
- VirtualKeyMap* mMap;
- Tokenizer* mTokenizer;
-
- public:
- Parser(VirtualKeyMap* map, Tokenizer* tokenizer);
- ~Parser();
- status_t parse();
-
- private:
- bool consumeFieldDelimiterAndSkipWhitespace();
- bool parseNextIntField(int32_t* outValue);
- };
-
- Vector<VirtualKeyDefinition> mVirtualKeys;
-
- VirtualKeyMap();
-};
-
-} // namespace android
-
-#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H
diff --git a/include/androidfw/ZipFileCRO.h b/include/androidfw/ZipFileCRO.h
new file mode 100644
index 0000000..3e42a95
--- /dev/null
+++ b/include/androidfw/ZipFileCRO.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+//
+// C API for ead-only access to Zip archives, with minimal heap allocation.
+//
+#ifndef __LIBS_ZIPFILECRO_H
+#define __LIBS_ZIPFILECRO_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <utils/Compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer.
+ */
+typedef void* ZipFileCRO;
+
+/*
+ * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple
+ * integer. We use NULL to indicate an invalid value.
+ */
+typedef void* ZipEntryCRO;
+
+extern ZipFileCRO ZipFileXRO_open(const char* path);
+
+extern void ZipFileCRO_destroy(ZipFileCRO zip);
+
+extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip,
+ const char* fileName);
+
+extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry,
+ int* pMethod, size_t* pUncompLen,
+ size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32);
+
+extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__LIBS_ZIPFILECRO_H*/
diff --git a/include/androidfw/ZipFileRO.h b/include/androidfw/ZipFileRO.h
new file mode 100644
index 0000000..547e36a
--- /dev/null
+++ b/include/androidfw/ZipFileRO.h
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ *
+ * This is similar to the more-complete ZipFile class, but no attempt
+ * has been made to make them interchangeable. This class operates under
+ * a very different set of assumptions and constraints.
+ *
+ * One such assumption is that if you're getting file descriptors for
+ * use with this class as a child of a fork() operation, you must be on
+ * a pread() to guarantee correct operation. This is because pread() can
+ * atomically read at a file offset without worrying about a lock around an
+ * lseek() + read() pair.
+ */
+#ifndef __LIBS_ZIPFILERO_H
+#define __LIBS_ZIPFILERO_H
+
+#include <utils/Compat.h>
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/threads.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+namespace android {
+
+/*
+ * Trivial typedef to ensure that ZipEntryRO is not treated as a simple
+ * integer. We use NULL to indicate an invalid value.
+ */
+typedef void* ZipEntryRO;
+
+/*
+ * Open a Zip archive for reading.
+ *
+ * We want "open" and "find entry by name" to be fast operations, and we
+ * want to use as little memory as possible. We memory-map the file,
+ * and load a hash table with pointers to the filenames (which aren't
+ * null-terminated). The other fields are at a fixed offset from the
+ * filename, so we don't need to extract those (but we do need to byte-read
+ * and endian-swap them every time we want them).
+ *
+ * To speed comparisons when doing a lookup by name, we could make the mapping
+ * "private" (copy-on-write) and null-terminate the filenames after verifying
+ * the record structure. However, this requires a private mapping of
+ * every page that the Central Directory touches. Easier to tuck a copy
+ * of the string length into the hash table entry.
+ *
+ * NOTE: If this is used on file descriptors inherited from a fork() operation,
+ * you must be on a platform that implements pread() to guarantee correctness
+ * on the shared file descriptors.
+ */
+class ZipFileRO {
+public:
+ ZipFileRO()
+ : mFd(-1), mFileName(NULL), mFileLength(-1),
+ mDirectoryMap(NULL),
+ mNumEntries(-1), mDirectoryOffset(-1),
+ mHashTableSize(-1), mHashTable(NULL)
+ {}
+
+ ~ZipFileRO();
+
+ /*
+ * Open an archive.
+ */
+ status_t open(const char* zipFileName);
+
+ /*
+ * Find an entry, by name. Returns the entry identifier, or NULL if
+ * not found.
+ *
+ * If two entries have the same name, one will be chosen at semi-random.
+ */
+ ZipEntryRO findEntryByName(const char* fileName) const;
+
+ /*
+ * Return the #of entries in the Zip archive.
+ */
+ int getNumEntries(void) const {
+ return mNumEntries;
+ }
+
+ /*
+ * Return the Nth entry. Zip file entries are not stored in sorted
+ * order, and updated entries may appear at the end, so anyone walking
+ * the archive needs to avoid making ordering assumptions. We take
+ * that further by returning the Nth non-empty entry in the hash table
+ * rather than the Nth entry in the archive.
+ *
+ * Valid values are [0..numEntries).
+ *
+ * [This is currently O(n). If it needs to be fast we can allocate an
+ * additional data structure or provide an iterator interface.]
+ */
+ ZipEntryRO findEntryByIndex(int idx) const;
+
+ /*
+ * Copy the filename into the supplied buffer. Returns 0 on success,
+ * -1 if "entry" is invalid, or the filename length if it didn't fit. The
+ * length, and the returned string, include the null-termination.
+ */
+ int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const;
+
+ /*
+ * Get the vital stats for an entry. Pass in NULL pointers for anything
+ * you don't need.
+ *
+ * "*pOffset" holds the Zip file offset of the entry's data.
+ *
+ * Returns "false" if "entry" is bogus or if the data in the Zip file
+ * appears to be bad.
+ */
+ bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen,
+ size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const;
+
+ /*
+ * Create a new FileMap object that maps a subset of the archive. For
+ * an uncompressed entry this effectively provides a pointer to the
+ * actual data, for a compressed entry this provides the input buffer
+ * for inflate().
+ */
+ FileMap* createEntryFileMap(ZipEntryRO entry) const;
+
+ /*
+ * Uncompress the data into a buffer. Depending on the compression
+ * format, this is either an "inflate" operation or a memcpy.
+ *
+ * Use "uncompLen" from getEntryInfo() to determine the required
+ * buffer size.
+ *
+ * Returns "true" on success.
+ */
+ bool uncompressEntry(ZipEntryRO entry, void* buffer) const;
+
+ /*
+ * Uncompress the data to an open file descriptor.
+ */
+ bool uncompressEntry(ZipEntryRO entry, int fd) const;
+
+ /* Zip compression methods we support */
+ enum {
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
+ };
+
+ /*
+ * Utility function: uncompress deflated data, buffer to buffer.
+ */
+ static bool inflateBuffer(void* outBuf, const void* inBuf,
+ size_t uncompLen, size_t compLen);
+
+ /*
+ * Utility function: uncompress deflated data, buffer to fd.
+ */
+ static bool inflateBuffer(int fd, const void* inBuf,
+ size_t uncompLen, size_t compLen);
+
+ /*
+ * Utility function to convert ZIP's time format to a timespec struct.
+ */
+ static inline void zipTimeToTimespec(long when, struct tm* timespec) {
+ const long date = when >> 16;
+ timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980
+ timespec->tm_mon = (date >> 5) & 0x0F;
+ timespec->tm_mday = date & 0x1F;
+
+ timespec->tm_hour = (when >> 11) & 0x1F;
+ timespec->tm_min = (when >> 5) & 0x3F;
+ timespec->tm_sec = (when & 0x1F) << 1;
+ }
+
+ /*
+ * Some basic functions for raw data manipulation. "LE" means
+ * Little Endian.
+ */
+ static inline unsigned short get2LE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8);
+ }
+ static inline unsigned long get4LE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ }
+
+private:
+ /* these are private and not defined */
+ ZipFileRO(const ZipFileRO& src);
+ ZipFileRO& operator=(const ZipFileRO& src);
+
+ /* locate and parse the central directory */
+ bool mapCentralDirectory(void);
+
+ /* parse the archive, prepping internal structures */
+ bool parseZipArchive(void);
+
+ /* add a new entry to the hash table */
+ void addToHash(const char* str, int strLen, unsigned int hash);
+
+ /* compute string hash code */
+ static unsigned int computeHash(const char* str, int len);
+
+ /* convert a ZipEntryRO back to a hash table index */
+ int entryToIndex(const ZipEntryRO entry) const;
+
+ /*
+ * One entry in the hash table.
+ */
+ typedef struct HashEntry {
+ const char* name;
+ unsigned short nameLen;
+ //unsigned int hash;
+ } HashEntry;
+
+ /* open Zip archive */
+ int mFd;
+
+ /* Lock for handling the file descriptor (seeks, etc) */
+ mutable Mutex mFdLock;
+
+ /* zip file name */
+ char* mFileName;
+
+ /* length of file */
+ size_t mFileLength;
+
+ /* mapped file */
+ FileMap* mDirectoryMap;
+
+ /* number of entries in the Zip archive */
+ int mNumEntries;
+
+ /* CD directory offset in the Zip archive */
+ off64_t mDirectoryOffset;
+
+ /*
+ * We know how many entries are in the Zip archive, so we have a
+ * fixed-size hash table. We probe for an empty slot.
+ */
+ int mHashTableSize;
+ HashEntry* mHashTable;
+};
+
+}; // namespace android
+
+#endif /*__LIBS_ZIPFILERO_H*/
diff --git a/include/androidfw/ZipUtils.h b/include/androidfw/ZipUtils.h
new file mode 100644
index 0000000..42c42b6
--- /dev/null
+++ b/include/androidfw/ZipUtils.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+//
+// Miscellaneous zip/gzip utility functions.
+//
+#ifndef __LIBS_ZIPUTILS_H
+#define __LIBS_ZIPUTILS_H
+
+#include <stdio.h>
+
+namespace android {
+
+/*
+ * Container class for utility functions, primarily for namespace reasons.
+ */
+class ZipUtils {
+public:
+ /*
+ * General utility function for uncompressing "deflate" data from a file
+ * to a buffer.
+ */
+ static bool inflateToBuffer(int fd, void* buf, long uncompressedLen,
+ long compressedLen);
+ static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen,
+ long compressedLen);
+
+ /*
+ * Someday we might want to make this generic and handle bzip2 ".bz2"
+ * files too.
+ *
+ * We could declare gzip to be a sub-class of zip that has exactly
+ * one always-compressed entry, but we currently want to treat Zip
+ * and gzip as distinct, so there's no value.
+ *
+ * The zlib library has some gzip utilities, but it has no interface
+ * for extracting the uncompressed length of the file (you do *not*
+ * want to gzseek to the end).
+ *
+ * Pass in a seeked file pointer for the gzip file. If this is a gzip
+ * file, we set our return values appropriately and return "true" with
+ * the file seeked to the start of the compressed data.
+ */
+ static bool examineGzip(FILE* fp, int* pCompressionMethod,
+ long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32);
+
+private:
+ ZipUtils() {}
+ ~ZipUtils() {}
+};
+
+}; // namespace android
+
+#endif /*__LIBS_ZIPUTILS_H*/
diff --git a/include/androidfw/misc.h b/include/androidfw/misc.h
new file mode 100644
index 0000000..5a5a0e2
--- /dev/null
+++ b/include/androidfw/misc.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+
+//
+// Handy utility functions and portability code.
+//
+#ifndef _LIBS_ANDROID_FW_MISC_H
+#define _LIBS_ANDROID_FW_MISC_H
+
+namespace android {
+
+/*
+ * Some utility functions for working with files. These could be made
+ * part of a "File" class.
+ */
+typedef enum FileType {
+ kFileTypeUnknown = 0,
+ kFileTypeNonexistent, // i.e. ENOENT
+ kFileTypeRegular,
+ kFileTypeDirectory,
+ kFileTypeCharDev,
+ kFileTypeBlockDev,
+ kFileTypeFifo,
+ kFileTypeSymlink,
+ kFileTypeSocket,
+} FileType;
+/* get the file's type; follows symlinks */
+FileType getFileType(const char* fileName);
+/* get the file's modification date; returns -1 w/errno set on failure */
+time_t getFileModDate(const char* fileName);
+
+}; // namespace android
+
+#endif // _LIBS_ANDROID_FW_MISC_H
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index 3ed75a2..4e126b8 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -14,44 +14,43 @@
LOCAL_PATH:= $(call my-dir)
-# libandroidfw is partially built for the host (used by build time keymap validation tool)
+# libandroidfw is partially built for the host (used by obbtool and others)
# These files are common to host and target builds.
-# formerly in libutils
-commonUtilsSources:= \
+commonSources := \
Asset.cpp \
AssetDir.cpp \
AssetManager.cpp \
+ misc.cpp \
ObbFile.cpp \
ResourceTypes.cpp \
- StreamingZipInflater.cpp
+ StreamingZipInflater.cpp \
+ ZipFileCRO.cpp \
+ ZipFileRO.cpp \
+ ZipUtils.cpp
-# formerly in libui
-commonUiSources:= \
- Input.cpp \
- InputDevice.cpp \
- Keyboard.cpp \
- KeyCharacterMap.cpp \
- KeyLayoutMap.cpp \
- VelocityControl.cpp \
- VelocityTracker.cpp \
- VirtualKeyMap.cpp
+deviceSources := \
+ $(commonSources) \
+ BackupData.cpp \
+ BackupHelpers.cpp \
+ CursorWindow.cpp
-commonSources:= \
- $(commonUtilsSources) \
- $(commonUiSources)
+hostSources := \
+ $(commonSources)
# For the host
# =====================================================
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= $(commonSources)
+LOCAL_SRC_FILES:= $(hostSources)
LOCAL_MODULE:= libandroidfw
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
+
LOCAL_C_INCLUDES := \
external/zlib
@@ -63,23 +62,16 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- $(commonSources) \
- BackupData.cpp \
- BackupHelpers.cpp \
- CursorWindow.cpp \
- InputTransport.cpp
+LOCAL_SRC_FILES:= $(deviceSources)
LOCAL_SHARED_LIBRARIES := \
+ libbinder \
liblog \
libcutils \
libutils \
- libbinder \
- libskia \
libz
LOCAL_C_INCLUDES := \
- external/skia/include/core \
external/icu4c/common \
external/zlib
@@ -90,20 +82,6 @@
include $(BUILD_SHARED_LIBRARY)
-ifeq ($(TARGET_OS),linux)
-include $(CLEAR_VARS)
-LOCAL_C_INCLUDES += \
- external/skia/include/core \
- external/zlib \
- external/icu4c/common \
- bionic/libc/private
-LOCAL_LDLIBS := -lrt -ldl -lpthread
-LOCAL_MODULE := libandroidfw
-LOCAL_SRC_FILES := $(commonUtilsSources) BackupData.cpp BackupHelpers.cpp
-include $(BUILD_STATIC_LIBRARY)
-endif
-
-
# Include subdirectory makefiles
# ============================================================
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index fd5b3e4..cb7628d 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -23,11 +23,11 @@
#include <androidfw/Asset.h>
#include <androidfw/StreamingZipInflater.h>
+#include <androidfw/ZipFileRO.h>
+#include <androidfw/ZipUtils.h>
#include <utils/Atomic.h>
#include <utils/FileMap.h>
#include <utils/Log.h>
-#include <utils/ZipFileRO.h>
-#include <utils/ZipUtils.h>
#include <utils/threads.h>
#include <assert.h>
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index b08c36b..6667daf 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -25,14 +25,15 @@
#include <androidfw/Asset.h>
#include <androidfw/AssetDir.h>
#include <androidfw/AssetManager.h>
+#include <androidfw/misc.h>
#include <androidfw/ResourceTypes.h>
+#include <androidfw/ZipFileRO.h>
#include <utils/Atomic.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Timers.h>
-#include <utils/ZipFileRO.h>
#ifdef HAVE_ANDROID_OS
#include <cutils/trace.h>
#endif
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 047a4c8..0f54edb 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -17,8 +17,9 @@
#undef LOG_TAG
#define LOG_TAG "CursorWindow"
-#include <utils/Log.h>
#include <androidfw/CursorWindow.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
#include <cutils/ashmem.h>
#include <sys/mman.h>
diff --git a/libs/androidfw/Input.cpp b/libs/androidfw/Input.cpp
deleted file mode 100644
index eca692a..0000000
--- a/libs/androidfw/Input.cpp
+++ /dev/null
@@ -1,634 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Input"
-//#define LOG_NDEBUG 0
-
-#include <math.h>
-#include <limits.h>
-
-#include <androidfw/Input.h>
-
-#ifdef HAVE_ANDROID_OS
-#include <binder/Parcel.h>
-
-#include "SkPoint.h"
-#include "SkMatrix.h"
-#include "SkScalar.h"
-#endif
-
-namespace android {
-
-// --- InputEvent ---
-
-void InputEvent::initialize(int32_t deviceId, int32_t source) {
- mDeviceId = deviceId;
- mSource = source;
-}
-
-void InputEvent::initialize(const InputEvent& from) {
- mDeviceId = from.mDeviceId;
- mSource = from.mSource;
-}
-
-// --- KeyEvent ---
-
-bool KeyEvent::hasDefaultAction(int32_t keyCode) {
- switch (keyCode) {
- case AKEYCODE_HOME:
- case AKEYCODE_BACK:
- case AKEYCODE_CALL:
- case AKEYCODE_ENDCALL:
- case AKEYCODE_VOLUME_UP:
- case AKEYCODE_VOLUME_DOWN:
- case AKEYCODE_VOLUME_MUTE:
- case AKEYCODE_POWER:
- case AKEYCODE_CAMERA:
- case AKEYCODE_HEADSETHOOK:
- case AKEYCODE_MENU:
- case AKEYCODE_NOTIFICATION:
- case AKEYCODE_FOCUS:
- case AKEYCODE_SEARCH:
- case AKEYCODE_MEDIA_PLAY:
- case AKEYCODE_MEDIA_PAUSE:
- case AKEYCODE_MEDIA_PLAY_PAUSE:
- case AKEYCODE_MEDIA_STOP:
- case AKEYCODE_MEDIA_NEXT:
- case AKEYCODE_MEDIA_PREVIOUS:
- case AKEYCODE_MEDIA_REWIND:
- case AKEYCODE_MEDIA_RECORD:
- case AKEYCODE_MEDIA_FAST_FORWARD:
- case AKEYCODE_MUTE:
- case AKEYCODE_BRIGHTNESS_DOWN:
- case AKEYCODE_BRIGHTNESS_UP:
- return true;
- }
-
- return false;
-}
-
-bool KeyEvent::hasDefaultAction() const {
- return hasDefaultAction(getKeyCode());
-}
-
-bool KeyEvent::isSystemKey(int32_t keyCode) {
- switch (keyCode) {
- case AKEYCODE_MENU:
- case AKEYCODE_SOFT_RIGHT:
- case AKEYCODE_HOME:
- case AKEYCODE_BACK:
- case AKEYCODE_CALL:
- case AKEYCODE_ENDCALL:
- case AKEYCODE_VOLUME_UP:
- case AKEYCODE_VOLUME_DOWN:
- case AKEYCODE_VOLUME_MUTE:
- case AKEYCODE_MUTE:
- case AKEYCODE_POWER:
- case AKEYCODE_HEADSETHOOK:
- case AKEYCODE_MEDIA_PLAY:
- case AKEYCODE_MEDIA_PAUSE:
- case AKEYCODE_MEDIA_PLAY_PAUSE:
- case AKEYCODE_MEDIA_STOP:
- case AKEYCODE_MEDIA_NEXT:
- case AKEYCODE_MEDIA_PREVIOUS:
- case AKEYCODE_MEDIA_REWIND:
- case AKEYCODE_MEDIA_RECORD:
- case AKEYCODE_MEDIA_FAST_FORWARD:
- case AKEYCODE_CAMERA:
- case AKEYCODE_FOCUS:
- case AKEYCODE_SEARCH:
- case AKEYCODE_BRIGHTNESS_DOWN:
- case AKEYCODE_BRIGHTNESS_UP:
- return true;
- }
-
- return false;
-}
-
-bool KeyEvent::isSystemKey() const {
- return isSystemKey(getKeyCode());
-}
-
-void KeyEvent::initialize(
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
- InputEvent::initialize(deviceId, source);
- mAction = action;
- mFlags = flags;
- mKeyCode = keyCode;
- mScanCode = scanCode;
- mMetaState = metaState;
- mRepeatCount = repeatCount;
- mDownTime = downTime;
- mEventTime = eventTime;
-}
-
-void KeyEvent::initialize(const KeyEvent& from) {
- InputEvent::initialize(from);
- mAction = from.mAction;
- mFlags = from.mFlags;
- mKeyCode = from.mKeyCode;
- mScanCode = from.mScanCode;
- mMetaState = from.mMetaState;
- mRepeatCount = from.mRepeatCount;
- mDownTime = from.mDownTime;
- mEventTime = from.mEventTime;
-}
-
-
-// --- PointerCoords ---
-
-float PointerCoords::getAxisValue(int32_t axis) const {
- if (axis < 0 || axis > 63) {
- return 0;
- }
-
- uint64_t axisBit = 1LL << axis;
- if (!(bits & axisBit)) {
- return 0;
- }
- uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
- return values[index];
-}
-
-status_t PointerCoords::setAxisValue(int32_t axis, float value) {
- if (axis < 0 || axis > 63) {
- return NAME_NOT_FOUND;
- }
-
- uint64_t axisBit = 1LL << axis;
- uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
- if (!(bits & axisBit)) {
- if (value == 0) {
- return OK; // axes with value 0 do not need to be stored
- }
- uint32_t count = __builtin_popcountll(bits);
- if (count >= MAX_AXES) {
- tooManyAxes(axis);
- return NO_MEMORY;
- }
- bits |= axisBit;
- for (uint32_t i = count; i > index; i--) {
- values[i] = values[i - 1];
- }
- }
- values[index] = value;
- return OK;
-}
-
-static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) {
- float value = c.getAxisValue(axis);
- if (value != 0) {
- c.setAxisValue(axis, value * scaleFactor);
- }
-}
-
-void PointerCoords::scale(float scaleFactor) {
- // No need to scale pressure or size since they are normalized.
- // No need to scale orientation since it is meaningless to do so.
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
-}
-
-#ifdef HAVE_ANDROID_OS
-status_t PointerCoords::readFromParcel(Parcel* parcel) {
- bits = parcel->readInt64();
-
- uint32_t count = __builtin_popcountll(bits);
- if (count > MAX_AXES) {
- return BAD_VALUE;
- }
-
- for (uint32_t i = 0; i < count; i++) {
- values[i] = parcel->readFloat();
- }
- return OK;
-}
-
-status_t PointerCoords::writeToParcel(Parcel* parcel) const {
- parcel->writeInt64(bits);
-
- uint32_t count = __builtin_popcountll(bits);
- for (uint32_t i = 0; i < count; i++) {
- parcel->writeFloat(values[i]);
- }
- return OK;
-}
-#endif
-
-void PointerCoords::tooManyAxes(int axis) {
- ALOGW("Could not set value for axis %d because the PointerCoords structure is full and "
- "cannot contain more than %d axis values.", axis, int(MAX_AXES));
-}
-
-bool PointerCoords::operator==(const PointerCoords& other) const {
- if (bits != other.bits) {
- return false;
- }
- uint32_t count = __builtin_popcountll(bits);
- for (uint32_t i = 0; i < count; i++) {
- if (values[i] != other.values[i]) {
- return false;
- }
- }
- return true;
-}
-
-void PointerCoords::copyFrom(const PointerCoords& other) {
- bits = other.bits;
- uint32_t count = __builtin_popcountll(bits);
- for (uint32_t i = 0; i < count; i++) {
- values[i] = other.values[i];
- }
-}
-
-
-// --- PointerProperties ---
-
-bool PointerProperties::operator==(const PointerProperties& other) const {
- return id == other.id
- && toolType == other.toolType;
-}
-
-void PointerProperties::copyFrom(const PointerProperties& other) {
- id = other.id;
- toolType = other.toolType;
-}
-
-
-// --- MotionEvent ---
-
-void MotionEvent::initialize(
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t edgeFlags,
- int32_t metaState,
- int32_t buttonState,
- float xOffset,
- float yOffset,
- float xPrecision,
- float yPrecision,
- nsecs_t downTime,
- nsecs_t eventTime,
- size_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords) {
- InputEvent::initialize(deviceId, source);
- mAction = action;
- mFlags = flags;
- mEdgeFlags = edgeFlags;
- mMetaState = metaState;
- mButtonState = buttonState;
- mXOffset = xOffset;
- mYOffset = yOffset;
- mXPrecision = xPrecision;
- mYPrecision = yPrecision;
- mDownTime = downTime;
- mPointerProperties.clear();
- mPointerProperties.appendArray(pointerProperties, pointerCount);
- mSampleEventTimes.clear();
- mSamplePointerCoords.clear();
- addSample(eventTime, pointerCoords);
-}
-
-void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
- InputEvent::initialize(other->mDeviceId, other->mSource);
- mAction = other->mAction;
- mFlags = other->mFlags;
- mEdgeFlags = other->mEdgeFlags;
- mMetaState = other->mMetaState;
- mButtonState = other->mButtonState;
- mXOffset = other->mXOffset;
- mYOffset = other->mYOffset;
- mXPrecision = other->mXPrecision;
- mYPrecision = other->mYPrecision;
- mDownTime = other->mDownTime;
- mPointerProperties = other->mPointerProperties;
-
- if (keepHistory) {
- mSampleEventTimes = other->mSampleEventTimes;
- mSamplePointerCoords = other->mSamplePointerCoords;
- } else {
- mSampleEventTimes.clear();
- mSampleEventTimes.push(other->getEventTime());
- mSamplePointerCoords.clear();
- size_t pointerCount = other->getPointerCount();
- size_t historySize = other->getHistorySize();
- mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array()
- + (historySize * pointerCount), pointerCount);
- }
-}
-
-void MotionEvent::addSample(
- int64_t eventTime,
- const PointerCoords* pointerCoords) {
- mSampleEventTimes.push(eventTime);
- mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
-}
-
-const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
- return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
-}
-
-float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const {
- return getRawPointerCoords(pointerIndex)->getAxisValue(axis);
-}
-
-float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
- float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
- switch (axis) {
- case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
- case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
- }
- return value;
-}
-
-const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
- size_t pointerIndex, size_t historicalIndex) const {
- return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex];
-}
-
-float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
- size_t historicalIndex) const {
- return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
-}
-
-float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
- size_t historicalIndex) const {
- float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
- switch (axis) {
- case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
- case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
- }
- return value;
-}
-
-ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
- size_t pointerCount = mPointerProperties.size();
- for (size_t i = 0; i < pointerCount; i++) {
- if (mPointerProperties.itemAt(i).id == pointerId) {
- return i;
- }
- }
- return -1;
-}
-
-void MotionEvent::offsetLocation(float xOffset, float yOffset) {
- mXOffset += xOffset;
- mYOffset += yOffset;
-}
-
-void MotionEvent::scale(float scaleFactor) {
- mXOffset *= scaleFactor;
- mYOffset *= scaleFactor;
- mXPrecision *= scaleFactor;
- mYPrecision *= scaleFactor;
-
- size_t numSamples = mSamplePointerCoords.size();
- for (size_t i = 0; i < numSamples; i++) {
- mSamplePointerCoords.editItemAt(i).scale(scaleFactor);
- }
-}
-
-#ifdef HAVE_ANDROID_OS
-static inline float transformAngle(const SkMatrix* matrix, float angleRadians) {
- // Construct and transform a vector oriented at the specified clockwise angle from vertical.
- // Coordinate system: down is increasing Y, right is increasing X.
- SkPoint vector;
- vector.fX = SkFloatToScalar(sinf(angleRadians));
- vector.fY = SkFloatToScalar(-cosf(angleRadians));
- matrix->mapVectors(& vector, 1);
-
- // Derive the transformed vector's clockwise angle from vertical.
- float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(-vector.fY));
- if (result < - M_PI_2) {
- result += M_PI;
- } else if (result > M_PI_2) {
- result -= M_PI;
- }
- return result;
-}
-
-void MotionEvent::transform(const SkMatrix* matrix) {
- float oldXOffset = mXOffset;
- float oldYOffset = mYOffset;
-
- // The tricky part of this implementation is to preserve the value of
- // rawX and rawY. So we apply the transformation to the first point
- // then derive an appropriate new X/Y offset that will preserve rawX and rawY.
- SkPoint point;
- float rawX = getRawX(0);
- float rawY = getRawY(0);
- matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset),
- & point);
- float newX = SkScalarToFloat(point.fX);
- float newY = SkScalarToFloat(point.fY);
- float newXOffset = newX - rawX;
- float newYOffset = newY - rawY;
-
- mXOffset = newXOffset;
- mYOffset = newYOffset;
-
- // Apply the transformation to all samples.
- size_t numSamples = mSamplePointerCoords.size();
- for (size_t i = 0; i < numSamples; i++) {
- PointerCoords& c = mSamplePointerCoords.editItemAt(i);
- float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
- float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
- matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), &point);
- c.setAxisValue(AMOTION_EVENT_AXIS_X, SkScalarToFloat(point.fX) - newXOffset);
- c.setAxisValue(AMOTION_EVENT_AXIS_Y, SkScalarToFloat(point.fY) - newYOffset);
-
- float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
- c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(matrix, orientation));
- }
-}
-
-status_t MotionEvent::readFromParcel(Parcel* parcel) {
- size_t pointerCount = parcel->readInt32();
- size_t sampleCount = parcel->readInt32();
- if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0) {
- return BAD_VALUE;
- }
-
- mDeviceId = parcel->readInt32();
- mSource = parcel->readInt32();
- mAction = parcel->readInt32();
- mFlags = parcel->readInt32();
- mEdgeFlags = parcel->readInt32();
- mMetaState = parcel->readInt32();
- mButtonState = parcel->readInt32();
- mXOffset = parcel->readFloat();
- mYOffset = parcel->readFloat();
- mXPrecision = parcel->readFloat();
- mYPrecision = parcel->readFloat();
- mDownTime = parcel->readInt64();
-
- mPointerProperties.clear();
- mPointerProperties.setCapacity(pointerCount);
- mSampleEventTimes.clear();
- mSampleEventTimes.setCapacity(sampleCount);
- mSamplePointerCoords.clear();
- mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
-
- for (size_t i = 0; i < pointerCount; i++) {
- mPointerProperties.push();
- PointerProperties& properties = mPointerProperties.editTop();
- properties.id = parcel->readInt32();
- properties.toolType = parcel->readInt32();
- }
-
- while (sampleCount-- > 0) {
- mSampleEventTimes.push(parcel->readInt64());
- for (size_t i = 0; i < pointerCount; i++) {
- mSamplePointerCoords.push();
- status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel);
- if (status) {
- return status;
- }
- }
- }
- return OK;
-}
-
-status_t MotionEvent::writeToParcel(Parcel* parcel) const {
- size_t pointerCount = mPointerProperties.size();
- size_t sampleCount = mSampleEventTimes.size();
-
- parcel->writeInt32(pointerCount);
- parcel->writeInt32(sampleCount);
-
- parcel->writeInt32(mDeviceId);
- parcel->writeInt32(mSource);
- parcel->writeInt32(mAction);
- parcel->writeInt32(mFlags);
- parcel->writeInt32(mEdgeFlags);
- parcel->writeInt32(mMetaState);
- parcel->writeInt32(mButtonState);
- parcel->writeFloat(mXOffset);
- parcel->writeFloat(mYOffset);
- parcel->writeFloat(mXPrecision);
- parcel->writeFloat(mYPrecision);
- parcel->writeInt64(mDownTime);
-
- for (size_t i = 0; i < pointerCount; i++) {
- const PointerProperties& properties = mPointerProperties.itemAt(i);
- parcel->writeInt32(properties.id);
- parcel->writeInt32(properties.toolType);
- }
-
- const PointerCoords* pc = mSamplePointerCoords.array();
- for (size_t h = 0; h < sampleCount; h++) {
- parcel->writeInt64(mSampleEventTimes.itemAt(h));
- for (size_t i = 0; i < pointerCount; i++) {
- status_t status = (pc++)->writeToParcel(parcel);
- if (status) {
- return status;
- }
- }
- }
- return OK;
-}
-#endif
-
-bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
- if (source & AINPUT_SOURCE_CLASS_POINTER) {
- // Specifically excludes HOVER_MOVE and SCROLL.
- switch (action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN:
- case AMOTION_EVENT_ACTION_MOVE:
- case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- case AMOTION_EVENT_ACTION_POINTER_UP:
- case AMOTION_EVENT_ACTION_CANCEL:
- case AMOTION_EVENT_ACTION_OUTSIDE:
- return true;
- }
- }
- return false;
-}
-
-
-// --- PooledInputEventFactory ---
-
-PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
- mMaxPoolSize(maxPoolSize) {
-}
-
-PooledInputEventFactory::~PooledInputEventFactory() {
- for (size_t i = 0; i < mKeyEventPool.size(); i++) {
- delete mKeyEventPool.itemAt(i);
- }
- for (size_t i = 0; i < mMotionEventPool.size(); i++) {
- delete mMotionEventPool.itemAt(i);
- }
-}
-
-KeyEvent* PooledInputEventFactory::createKeyEvent() {
- if (!mKeyEventPool.isEmpty()) {
- KeyEvent* event = mKeyEventPool.top();
- mKeyEventPool.pop();
- return event;
- }
- return new KeyEvent();
-}
-
-MotionEvent* PooledInputEventFactory::createMotionEvent() {
- if (!mMotionEventPool.isEmpty()) {
- MotionEvent* event = mMotionEventPool.top();
- mMotionEventPool.pop();
- return event;
- }
- return new MotionEvent();
-}
-
-void PooledInputEventFactory::recycle(InputEvent* event) {
- switch (event->getType()) {
- case AINPUT_EVENT_TYPE_KEY:
- if (mKeyEventPool.size() < mMaxPoolSize) {
- mKeyEventPool.push(static_cast<KeyEvent*>(event));
- return;
- }
- break;
- case AINPUT_EVENT_TYPE_MOTION:
- if (mMotionEventPool.size() < mMaxPoolSize) {
- mMotionEventPool.push(static_cast<MotionEvent*>(event));
- return;
- }
- break;
- }
- delete event;
-}
-
-} // namespace android
diff --git a/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp
deleted file mode 100644
index f742052..0000000
--- a/libs/androidfw/InputDevice.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "InputDevice"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-
-#include <androidfw/InputDevice.h>
-
-namespace android {
-
-static const char* CONFIGURATION_FILE_DIR[] = {
- "idc/",
- "keylayout/",
- "keychars/",
-};
-
-static const char* CONFIGURATION_FILE_EXTENSION[] = {
- ".idc",
- ".kl",
- ".kcm",
-};
-
-static bool isValidNameChar(char ch) {
- return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_');
-}
-
-static void appendInputDeviceConfigurationFileRelativePath(String8& path,
- const String8& name, InputDeviceConfigurationFileType type) {
- path.append(CONFIGURATION_FILE_DIR[type]);
- for (size_t i = 0; i < name.length(); i++) {
- char ch = name[i];
- if (!isValidNameChar(ch)) {
- ch = '_';
- }
- path.append(&ch, 1);
- }
- path.append(CONFIGURATION_FILE_EXTENSION[type]);
-}
-
-String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
- const InputDeviceIdentifier& deviceIdentifier,
- InputDeviceConfigurationFileType type) {
- if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
- if (deviceIdentifier.version != 0) {
- // Try vendor product version.
- String8 versionPath(getInputDeviceConfigurationFilePathByName(
- String8::format("Vendor_%04x_Product_%04x_Version_%04x",
- deviceIdentifier.vendor, deviceIdentifier.product,
- deviceIdentifier.version),
- type));
- if (!versionPath.isEmpty()) {
- return versionPath;
- }
- }
-
- // Try vendor product.
- String8 productPath(getInputDeviceConfigurationFilePathByName(
- String8::format("Vendor_%04x_Product_%04x",
- deviceIdentifier.vendor, deviceIdentifier.product),
- type));
- if (!productPath.isEmpty()) {
- return productPath;
- }
- }
-
- // Try device name.
- return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
-}
-
-String8 getInputDeviceConfigurationFilePathByName(
- const String8& name, InputDeviceConfigurationFileType type) {
- // Search system repository.
- String8 path;
- path.setTo(getenv("ANDROID_ROOT"));
- path.append("/usr/");
- appendInputDeviceConfigurationFileRelativePath(path, name, type);
-#if DEBUG_PROBE
- ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
-#endif
- if (!access(path.string(), R_OK)) {
-#if DEBUG_PROBE
- ALOGD("Found");
-#endif
- return path;
- }
-
- // Search user repository.
- // TODO Should only look here if not in safe mode.
- path.setTo(getenv("ANDROID_DATA"));
- path.append("/system/devices/");
- appendInputDeviceConfigurationFileRelativePath(path, name, type);
-#if DEBUG_PROBE
- ALOGD("Probing for system user input device configuration file: path='%s'", path.string());
-#endif
- if (!access(path.string(), R_OK)) {
-#if DEBUG_PROBE
- ALOGD("Found");
-#endif
- return path;
- }
-
- // Not found.
-#if DEBUG_PROBE
- ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
- name.string(), type);
-#endif
- return String8();
-}
-
-
-// --- InputDeviceInfo ---
-
-InputDeviceInfo::InputDeviceInfo() {
- initialize(-1, -1, InputDeviceIdentifier(), String8(), false);
-}
-
-InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
- mId(other.mId), mGeneration(other.mGeneration), mIdentifier(other.mIdentifier),
- mAlias(other.mAlias), mIsExternal(other.mIsExternal), mSources(other.mSources),
- mKeyboardType(other.mKeyboardType),
- mKeyCharacterMap(other.mKeyCharacterMap),
- mHasVibrator(other.mHasVibrator),
- mMotionRanges(other.mMotionRanges) {
-}
-
-InputDeviceInfo::~InputDeviceInfo() {
-}
-
-void InputDeviceInfo::initialize(int32_t id, int32_t generation,
- const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal) {
- mId = id;
- mGeneration = generation;
- mIdentifier = identifier;
- mAlias = alias;
- mIsExternal = isExternal;
- mSources = 0;
- mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
- mHasVibrator = false;
- mMotionRanges.clear();
-}
-
-const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
- int32_t axis, uint32_t source) const {
- size_t numRanges = mMotionRanges.size();
- for (size_t i = 0; i < numRanges; i++) {
- const MotionRange& range = mMotionRanges.itemAt(i);
- if (range.axis == axis && range.source == source) {
- return ⦥
- }
- }
- return NULL;
-}
-
-void InputDeviceInfo::addSource(uint32_t source) {
- mSources |= source;
-}
-
-void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max,
- float flat, float fuzz, float resolution) {
- MotionRange range = { axis, source, min, max, flat, fuzz, resolution };
- mMotionRanges.add(range);
-}
-
-void InputDeviceInfo::addMotionRange(const MotionRange& range) {
- mMotionRanges.add(range);
-}
-
-} // namespace android
diff --git a/libs/androidfw/InputTransport.cpp b/libs/androidfw/InputTransport.cpp
deleted file mode 100644
index 498389ea..0000000
--- a/libs/androidfw/InputTransport.cpp
+++ /dev/null
@@ -1,957 +0,0 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-// Provides a shared memory transport for input events.
-//
-#define LOG_TAG "InputTransport"
-
-//#define LOG_NDEBUG 0
-
-// Log debug messages about channel messages (send message, receive message)
-#define DEBUG_CHANNEL_MESSAGES 0
-
-// Log debug messages whenever InputChannel objects are created/destroyed
-#define DEBUG_CHANNEL_LIFECYCLE 0
-
-// Log debug messages about transport actions
-#define DEBUG_TRANSPORT_ACTIONS 0
-
-// Log debug messages about touch event resampling
-#define DEBUG_RESAMPLING 0
-
-
-#include <cutils/log.h>
-#include <cutils/properties.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <androidfw/InputTransport.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <math.h>
-
-
-namespace android {
-
-// Socket buffer size. The default is typically about 128KB, which is much larger than
-// we really need. So we make it smaller. It just needs to be big enough to hold
-// a few dozen large multi-finger motion events in the case where an application gets
-// behind processing touches.
-static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;
-
-// Nanoseconds per milliseconds.
-static const nsecs_t NANOS_PER_MS = 1000000;
-
-// Latency added during resampling. A few milliseconds doesn't hurt much but
-// reduces the impact of mispredicted touch positions.
-static const nsecs_t RESAMPLE_LATENCY = 5 * NANOS_PER_MS;
-
-// Minimum time difference between consecutive samples before attempting to resample.
-static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS;
-
-// Maximum time to predict forward from the last known state, to avoid predicting too
-// far into the future. This time is further bounded by 50% of the last time delta.
-static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS;
-
-template<typename T>
-inline static T min(const T& a, const T& b) {
- return a < b ? a : b;
-}
-
-inline static float lerp(float a, float b, float alpha) {
- return a + alpha * (b - a);
-}
-
-// --- InputMessage ---
-
-bool InputMessage::isValid(size_t actualSize) const {
- if (size() == actualSize) {
- switch (header.type) {
- case TYPE_KEY:
- return true;
- case TYPE_MOTION:
- return body.motion.pointerCount > 0
- && body.motion.pointerCount <= MAX_POINTERS;
- case TYPE_FINISHED:
- return true;
- }
- }
- return false;
-}
-
-size_t InputMessage::size() const {
- switch (header.type) {
- case TYPE_KEY:
- return sizeof(Header) + body.key.size();
- case TYPE_MOTION:
- return sizeof(Header) + body.motion.size();
- case TYPE_FINISHED:
- return sizeof(Header) + body.finished.size();
- }
- return sizeof(Header);
-}
-
-
-// --- InputChannel ---
-
-InputChannel::InputChannel(const String8& name, int fd) :
- mName(name), mFd(fd) {
-#if DEBUG_CHANNEL_LIFECYCLE
- ALOGD("Input channel constructed: name='%s', fd=%d",
- mName.string(), fd);
-#endif
-
- int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
- "non-blocking. errno=%d", mName.string(), errno);
-}
-
-InputChannel::~InputChannel() {
-#if DEBUG_CHANNEL_LIFECYCLE
- ALOGD("Input channel destroyed: name='%s', fd=%d",
- mName.string(), mFd);
-#endif
-
- ::close(mFd);
-}
-
-status_t InputChannel::openInputChannelPair(const String8& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
- int sockets[2];
- if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
- status_t result = -errno;
- ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
- name.string(), errno);
- outServerChannel.clear();
- outClientChannel.clear();
- return result;
- }
-
- int bufferSize = SOCKET_BUFFER_SIZE;
- setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
- setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
- setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
- setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
-
- String8 serverChannelName = name;
- serverChannelName.append(" (server)");
- outServerChannel = new InputChannel(serverChannelName, sockets[0]);
-
- String8 clientChannelName = name;
- clientChannelName.append(" (client)");
- outClientChannel = new InputChannel(clientChannelName, sockets[1]);
- return OK;
-}
-
-status_t InputChannel::sendMessage(const InputMessage* msg) {
- size_t msgLength = msg->size();
- ssize_t nWrite;
- do {
- nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
- } while (nWrite == -1 && errno == EINTR);
-
- if (nWrite < 0) {
- int error = errno;
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
- msg->header.type, error);
-#endif
- if (error == EAGAIN || error == EWOULDBLOCK) {
- return WOULD_BLOCK;
- }
- if (error == EPIPE || error == ENOTCONN) {
- return DEAD_OBJECT;
- }
- return -error;
- }
-
- if (size_t(nWrite) != msgLength) {
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
- mName.string(), msg->header.type);
-#endif
- return DEAD_OBJECT;
- }
-
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
-#endif
- return OK;
-}
-
-status_t InputChannel::receiveMessage(InputMessage* msg) {
- ssize_t nRead;
- do {
- nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
- } while (nRead == -1 && errno == EINTR);
-
- if (nRead < 0) {
- int error = errno;
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno);
-#endif
- if (error == EAGAIN || error == EWOULDBLOCK) {
- return WOULD_BLOCK;
- }
- if (error == EPIPE || error == ENOTCONN) {
- return DEAD_OBJECT;
- }
- return -error;
- }
-
- if (nRead == 0) { // check for EOF
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string());
-#endif
- return DEAD_OBJECT;
- }
-
- if (!msg->isValid(nRead)) {
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ received invalid message", mName.string());
-#endif
- return BAD_VALUE;
- }
-
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type);
-#endif
- return OK;
-}
-
-sp<InputChannel> InputChannel::dup() const {
- int fd = ::dup(getFd());
- return fd >= 0 ? new InputChannel(getName(), fd) : NULL;
-}
-
-
-// --- InputPublisher ---
-
-InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
- mChannel(channel) {
-}
-
-InputPublisher::~InputPublisher() {
-}
-
-status_t InputPublisher::publishKeyEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
- "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
- "downTime=%lld, eventTime=%lld",
- mChannel->getName().string(), seq,
- deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
- downTime, eventTime);
-#endif
-
- if (!seq) {
- ALOGE("Attempted to publish a key event with sequence number 0.");
- return BAD_VALUE;
- }
-
- InputMessage msg;
- msg.header.type = InputMessage::TYPE_KEY;
- msg.body.key.seq = seq;
- msg.body.key.deviceId = deviceId;
- msg.body.key.source = source;
- msg.body.key.action = action;
- msg.body.key.flags = flags;
- msg.body.key.keyCode = keyCode;
- msg.body.key.scanCode = scanCode;
- msg.body.key.metaState = metaState;
- msg.body.key.repeatCount = repeatCount;
- msg.body.key.downTime = downTime;
- msg.body.key.eventTime = eventTime;
- return mChannel->sendMessage(&msg);
-}
-
-status_t InputPublisher::publishMotionEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t edgeFlags,
- int32_t metaState,
- int32_t buttonState,
- float xOffset,
- float yOffset,
- float xPrecision,
- float yPrecision,
- nsecs_t downTime,
- nsecs_t eventTime,
- size_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
- "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, "
- "xOffset=%f, yOffset=%f, "
- "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
- "pointerCount=%d",
- mChannel->getName().string(), seq,
- deviceId, source, action, flags, edgeFlags, metaState, buttonState,
- xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
-#endif
-
- if (!seq) {
- ALOGE("Attempted to publish a motion event with sequence number 0.");
- return BAD_VALUE;
- }
-
- if (pointerCount > MAX_POINTERS || pointerCount < 1) {
- ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.",
- mChannel->getName().string(), pointerCount);
- return BAD_VALUE;
- }
-
- InputMessage msg;
- msg.header.type = InputMessage::TYPE_MOTION;
- msg.body.motion.seq = seq;
- msg.body.motion.deviceId = deviceId;
- msg.body.motion.source = source;
- msg.body.motion.action = action;
- msg.body.motion.flags = flags;
- msg.body.motion.edgeFlags = edgeFlags;
- msg.body.motion.metaState = metaState;
- msg.body.motion.buttonState = buttonState;
- msg.body.motion.xOffset = xOffset;
- msg.body.motion.yOffset = yOffset;
- msg.body.motion.xPrecision = xPrecision;
- msg.body.motion.yPrecision = yPrecision;
- msg.body.motion.downTime = downTime;
- msg.body.motion.eventTime = eventTime;
- msg.body.motion.pointerCount = pointerCount;
- for (size_t i = 0; i < pointerCount; i++) {
- msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
- msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
- }
- return mChannel->sendMessage(&msg);
-}
-
-status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
- mChannel->getName().string());
-#endif
-
- InputMessage msg;
- status_t result = mChannel->receiveMessage(&msg);
- if (result) {
- *outSeq = 0;
- *outHandled = false;
- return result;
- }
- if (msg.header.type != InputMessage::TYPE_FINISHED) {
- ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
- mChannel->getName().string(), msg.header.type);
- return UNKNOWN_ERROR;
- }
- *outSeq = msg.body.finished.seq;
- *outHandled = msg.body.finished.handled;
- return OK;
-}
-
-// --- InputConsumer ---
-
-InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
- mResampleTouch(isTouchResamplingEnabled()),
- mChannel(channel), mMsgDeferred(false) {
-}
-
-InputConsumer::~InputConsumer() {
-}
-
-bool InputConsumer::isTouchResamplingEnabled() {
- char value[PROPERTY_VALUE_MAX];
- int length = property_get("debug.inputconsumer.resample", value, NULL);
- if (length > 0) {
- if (!strcmp("0", value)) {
- return false;
- }
- if (strcmp("1", value)) {
- ALOGD("Unrecognized property value for 'debug.inputconsumer.resample'. "
- "Use '1' or '0'.");
- }
- }
- return true;
-}
-
-status_t InputConsumer::consume(InputEventFactoryInterface* factory,
- bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld",
- mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime);
-#endif
-
- *outSeq = 0;
- *outEvent = NULL;
-
- // Fetch the next input message.
- // Loop until an event can be returned or no additional events are received.
- while (!*outEvent) {
- if (mMsgDeferred) {
- // mMsg contains a valid input message from the previous call to consume
- // that has not yet been processed.
- mMsgDeferred = false;
- } else {
- // Receive a fresh message.
- status_t result = mChannel->receiveMessage(&mMsg);
- if (result) {
- // Consume the next batched event unless batches are being held for later.
- if (consumeBatches || result != WOULD_BLOCK) {
- result = consumeBatch(factory, frameTime, outSeq, outEvent);
- if (*outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
- mChannel->getName().string(), *outSeq);
-#endif
- break;
- }
- }
- return result;
- }
- }
-
- switch (mMsg.header.type) {
- case InputMessage::TYPE_KEY: {
- KeyEvent* keyEvent = factory->createKeyEvent();
- if (!keyEvent) return NO_MEMORY;
-
- initializeKeyEvent(keyEvent, &mMsg);
- *outSeq = mMsg.body.key.seq;
- *outEvent = keyEvent;
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
- mChannel->getName().string(), *outSeq);
-#endif
- break;
- }
-
- case AINPUT_EVENT_TYPE_MOTION: {
- ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
- if (batchIndex >= 0) {
- Batch& batch = mBatches.editItemAt(batchIndex);
- if (canAddSample(batch, &mMsg)) {
- batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ appended to batch event",
- mChannel->getName().string());
-#endif
- break;
- } else {
- // We cannot append to the batch in progress, so we need to consume
- // the previous batch right now and defer the new message until later.
- mMsgDeferred = true;
- status_t result = consumeSamples(factory,
- batch, batch.samples.size(), outSeq, outEvent);
- mBatches.removeAt(batchIndex);
- if (result) {
- return result;
- }
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed batch event and "
- "deferred current event, seq=%u",
- mChannel->getName().string(), *outSeq);
-#endif
- break;
- }
- }
-
- // Start a new batch if needed.
- if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
- || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mBatches.push();
- Batch& batch = mBatches.editTop();
- batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ started batch event",
- mChannel->getName().string());
-#endif
- break;
- }
-
- MotionEvent* motionEvent = factory->createMotionEvent();
- if (! motionEvent) return NO_MEMORY;
-
- updateTouchState(&mMsg);
- initializeMotionEvent(motionEvent, &mMsg);
- *outSeq = mMsg.body.motion.seq;
- *outEvent = motionEvent;
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
- mChannel->getName().string(), *outSeq);
-#endif
- break;
- }
-
- default:
- ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
- mChannel->getName().string(), mMsg.header.type);
- return UNKNOWN_ERROR;
- }
- }
- return OK;
-}
-
-status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
- status_t result;
- for (size_t i = mBatches.size(); i-- > 0; ) {
- Batch& batch = mBatches.editItemAt(i);
- if (frameTime < 0) {
- result = consumeSamples(factory, batch, batch.samples.size(),
- outSeq, outEvent);
- mBatches.removeAt(i);
- return result;
- }
-
- nsecs_t sampleTime = frameTime - RESAMPLE_LATENCY;
- ssize_t split = findSampleNoLaterThan(batch, sampleTime);
- if (split < 0) {
- continue;
- }
-
- result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
- const InputMessage* next;
- if (batch.samples.isEmpty()) {
- mBatches.removeAt(i);
- next = NULL;
- } else {
- next = &batch.samples.itemAt(0);
- }
- if (!result) {
- resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
- }
- return result;
- }
-
- return WOULD_BLOCK;
-}
-
-status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory,
- Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) {
- MotionEvent* motionEvent = factory->createMotionEvent();
- if (! motionEvent) return NO_MEMORY;
-
- uint32_t chain = 0;
- for (size_t i = 0; i < count; i++) {
- InputMessage& msg = batch.samples.editItemAt(i);
- updateTouchState(&msg);
- if (i) {
- SeqChain seqChain;
- seqChain.seq = msg.body.motion.seq;
- seqChain.chain = chain;
- mSeqChains.push(seqChain);
- addSample(motionEvent, &msg);
- } else {
- initializeMotionEvent(motionEvent, &msg);
- }
- chain = msg.body.motion.seq;
- }
- batch.samples.removeItemsAt(0, count);
-
- *outSeq = chain;
- *outEvent = motionEvent;
- return OK;
-}
-
-void InputConsumer::updateTouchState(InputMessage* msg) {
- if (!mResampleTouch ||
- !(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) {
- return;
- }
-
- int32_t deviceId = msg->body.motion.deviceId;
- int32_t source = msg->body.motion.source;
- nsecs_t eventTime = msg->body.motion.eventTime;
-
- // Update the touch state history to incorporate the new input message.
- // If the message is in the past relative to the most recently produced resampled
- // touch, then use the resampled time and coordinates instead.
- switch (msg->body.motion.action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN: {
- ssize_t index = findTouchState(deviceId, source);
- if (index < 0) {
- mTouchStates.push();
- index = mTouchStates.size() - 1;
- }
- TouchState& touchState = mTouchStates.editItemAt(index);
- touchState.initialize(deviceId, source);
- touchState.addHistory(msg);
- break;
- }
-
- case AMOTION_EVENT_ACTION_MOVE: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
- touchState.addHistory(msg);
- if (eventTime < touchState.lastResample.eventTime) {
- rewriteMessage(touchState, msg);
- } else {
- touchState.lastResample.idBits.clear();
- }
- }
- break;
- }
-
- case AMOTION_EVENT_ACTION_POINTER_DOWN: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
- touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId());
- rewriteMessage(touchState, msg);
- }
- break;
- }
-
- case AMOTION_EVENT_ACTION_POINTER_UP: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
- rewriteMessage(touchState, msg);
- touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId());
- }
- break;
- }
-
- case AMOTION_EVENT_ACTION_SCROLL: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- const TouchState& touchState = mTouchStates.itemAt(index);
- rewriteMessage(touchState, msg);
- }
- break;
- }
-
- case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- const TouchState& touchState = mTouchStates.itemAt(index);
- rewriteMessage(touchState, msg);
- mTouchStates.removeAt(index);
- }
- break;
- }
- }
-}
-
-void InputConsumer::rewriteMessage(const TouchState& state, InputMessage* msg) {
- for (size_t i = 0; i < msg->body.motion.pointerCount; i++) {
- uint32_t id = msg->body.motion.pointers[i].properties.id;
- if (state.lastResample.idBits.hasBit(id)) {
- PointerCoords& msgCoords = msg->body.motion.pointers[i].coords;
- const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
-#if DEBUG_RESAMPLING
- ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
- resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
- resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_Y),
- msgCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
- msgCoords.getAxisValue(AMOTION_EVENT_AXIS_Y));
-#endif
- msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
- msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY());
- }
- }
-}
-
-void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
- const InputMessage* next) {
- if (!mResampleTouch
- || !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER)
- || event->getAction() != AMOTION_EVENT_ACTION_MOVE) {
- return;
- }
-
- ssize_t index = findTouchState(event->getDeviceId(), event->getSource());
- if (index < 0) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, no touch state for device.");
-#endif
- return;
- }
-
- TouchState& touchState = mTouchStates.editItemAt(index);
- if (touchState.historySize < 1) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, no history for device.");
-#endif
- return;
- }
-
- // Ensure that the current sample has all of the pointers that need to be reported.
- const History* current = touchState.getHistory(0);
- size_t pointerCount = event->getPointerCount();
- for (size_t i = 0; i < pointerCount; i++) {
- uint32_t id = event->getPointerId(i);
- if (!current->idBits.hasBit(id)) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, missing id %d", id);
-#endif
- return;
- }
- }
-
- // Find the data to use for resampling.
- const History* other;
- History future;
- float alpha;
- if (next) {
- // Interpolate between current sample and future sample.
- // So current->eventTime <= sampleTime <= future.eventTime.
- future.initializeFrom(next);
- other = &future;
- nsecs_t delta = future.eventTime - current->eventTime;
- if (delta < RESAMPLE_MIN_DELTA) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, delta time is %lld ns.", delta);
-#endif
- return;
- }
- alpha = float(sampleTime - current->eventTime) / delta;
- } else if (touchState.historySize >= 2) {
- // Extrapolate future sample using current sample and past sample.
- // So other->eventTime <= current->eventTime <= sampleTime.
- other = touchState.getHistory(1);
- nsecs_t delta = current->eventTime - other->eventTime;
- if (delta < RESAMPLE_MIN_DELTA) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, delta time is %lld ns.", delta);
-#endif
- return;
- }
- nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION);
- if (sampleTime > maxPredict) {
-#if DEBUG_RESAMPLING
- ALOGD("Sample time is too far in the future, adjusting prediction "
- "from %lld to %lld ns.",
- sampleTime - current->eventTime, maxPredict - current->eventTime);
-#endif
- sampleTime = maxPredict;
- }
- alpha = float(current->eventTime - sampleTime) / delta;
- } else {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, insufficient data.");
-#endif
- return;
- }
-
- // Resample touch coordinates.
- touchState.lastResample.eventTime = sampleTime;
- touchState.lastResample.idBits.clear();
- for (size_t i = 0; i < pointerCount; i++) {
- uint32_t id = event->getPointerId(i);
- touchState.lastResample.idToIndex[id] = i;
- touchState.lastResample.idBits.markBit(id);
- PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
- const PointerCoords& currentCoords = current->getPointerById(id);
- if (other->idBits.hasBit(id)
- && shouldResampleTool(event->getToolType(i))) {
- const PointerCoords& otherCoords = other->getPointerById(id);
- resampledCoords.copyFrom(currentCoords);
- resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
- lerp(currentCoords.getX(), otherCoords.getX(), alpha));
- resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
- lerp(currentCoords.getY(), otherCoords.getY(), alpha));
-#if DEBUG_RESAMPLING
- ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
- "other (%0.3f, %0.3f), alpha %0.3f",
- id, resampledCoords.getX(), resampledCoords.getY(),
- currentCoords.getX(), currentCoords.getY(),
- otherCoords.getX(), otherCoords.getY(),
- alpha);
-#endif
- } else {
- resampledCoords.copyFrom(currentCoords);
-#if DEBUG_RESAMPLING
- ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)",
- id, resampledCoords.getX(), resampledCoords.getY(),
- currentCoords.getX(), currentCoords.getY());
-#endif
- }
- }
-
- event->addSample(sampleTime, touchState.lastResample.pointers);
-}
-
-bool InputConsumer::shouldResampleTool(int32_t toolType) {
- return toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
- || toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-}
-
-status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
- mChannel->getName().string(), seq, handled ? "true" : "false");
-#endif
-
- if (!seq) {
- ALOGE("Attempted to send a finished signal with sequence number 0.");
- return BAD_VALUE;
- }
-
- // Send finished signals for the batch sequence chain first.
- size_t seqChainCount = mSeqChains.size();
- if (seqChainCount) {
- uint32_t currentSeq = seq;
- uint32_t chainSeqs[seqChainCount];
- size_t chainIndex = 0;
- for (size_t i = seqChainCount; i-- > 0; ) {
- const SeqChain& seqChain = mSeqChains.itemAt(i);
- if (seqChain.seq == currentSeq) {
- currentSeq = seqChain.chain;
- chainSeqs[chainIndex++] = currentSeq;
- mSeqChains.removeAt(i);
- }
- }
- status_t status = OK;
- while (!status && chainIndex-- > 0) {
- status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
- }
- if (status) {
- // An error occurred so at least one signal was not sent, reconstruct the chain.
- do {
- SeqChain seqChain;
- seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
- seqChain.chain = chainSeqs[chainIndex];
- mSeqChains.push(seqChain);
- } while (chainIndex-- > 0);
- return status;
- }
- }
-
- // Send finished signal for the last message in the batch.
- return sendUnchainedFinishedSignal(seq, handled);
-}
-
-status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
- InputMessage msg;
- msg.header.type = InputMessage::TYPE_FINISHED;
- msg.body.finished.seq = seq;
- msg.body.finished.handled = handled;
- return mChannel->sendMessage(&msg);
-}
-
-bool InputConsumer::hasDeferredEvent() const {
- return mMsgDeferred;
-}
-
-bool InputConsumer::hasPendingBatch() const {
- return !mBatches.isEmpty();
-}
-
-ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
- for (size_t i = 0; i < mBatches.size(); i++) {
- const Batch& batch = mBatches.itemAt(i);
- const InputMessage& head = batch.samples.itemAt(0);
- if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) {
- return i;
- }
- }
- return -1;
-}
-
-ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const {
- for (size_t i = 0; i < mTouchStates.size(); i++) {
- const TouchState& touchState = mTouchStates.itemAt(i);
- if (touchState.deviceId == deviceId && touchState.source == source) {
- return i;
- }
- }
- return -1;
-}
-
-void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
- event->initialize(
- msg->body.key.deviceId,
- msg->body.key.source,
- msg->body.key.action,
- msg->body.key.flags,
- msg->body.key.keyCode,
- msg->body.key.scanCode,
- msg->body.key.metaState,
- msg->body.key.repeatCount,
- msg->body.key.downTime,
- msg->body.key.eventTime);
-}
-
-void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
- size_t pointerCount = msg->body.motion.pointerCount;
- PointerProperties pointerProperties[pointerCount];
- PointerCoords pointerCoords[pointerCount];
- for (size_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);
- pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
- }
-
- event->initialize(
- msg->body.motion.deviceId,
- msg->body.motion.source,
- msg->body.motion.action,
- msg->body.motion.flags,
- msg->body.motion.edgeFlags,
- msg->body.motion.metaState,
- msg->body.motion.buttonState,
- msg->body.motion.xOffset,
- msg->body.motion.yOffset,
- msg->body.motion.xPrecision,
- msg->body.motion.yPrecision,
- msg->body.motion.downTime,
- msg->body.motion.eventTime,
- pointerCount,
- pointerProperties,
- pointerCoords);
-}
-
-void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
- size_t pointerCount = msg->body.motion.pointerCount;
- PointerCoords pointerCoords[pointerCount];
- for (size_t i = 0; i < pointerCount; i++) {
- pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
- }
-
- event->setMetaState(event->getMetaState() | msg->body.motion.metaState);
- event->addSample(msg->body.motion.eventTime, pointerCoords);
-}
-
-bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) {
- const InputMessage& head = batch.samples.itemAt(0);
- size_t pointerCount = msg->body.motion.pointerCount;
- if (head.body.motion.pointerCount != pointerCount
- || head.body.motion.action != msg->body.motion.action) {
- return false;
- }
- for (size_t i = 0; i < pointerCount; i++) {
- if (head.body.motion.pointers[i].properties
- != msg->body.motion.pointers[i].properties) {
- return false;
- }
- }
- return true;
-}
-
-ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) {
- size_t numSamples = batch.samples.size();
- size_t index = 0;
- while (index < numSamples
- && batch.samples.itemAt(index).body.motion.eventTime <= time) {
- index += 1;
- }
- return ssize_t(index) - 1;
-}
-
-} // namespace android
diff --git a/libs/androidfw/KeyCharacterMap.cpp b/libs/androidfw/KeyCharacterMap.cpp
deleted file mode 100644
index 36cb6e1..0000000
--- a/libs/androidfw/KeyCharacterMap.cpp
+++ /dev/null
@@ -1,1153 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "KeyCharacterMap"
-
-#include <stdlib.h>
-#include <string.h>
-#include <android/keycodes.h>
-#include <androidfw/Keyboard.h>
-#include <androidfw/KeyCharacterMap.h>
-
-#if HAVE_ANDROID_OS
-#include <binder/Parcel.h>
-#endif
-
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/Tokenizer.h>
-#include <utils/Timers.h>
-
-// Enables debug output for the parser.
-#define DEBUG_PARSER 0
-
-// Enables debug output for parser performance.
-#define DEBUG_PARSER_PERFORMANCE 0
-
-// Enables debug output for mapping.
-#define DEBUG_MAPPING 0
-
-
-namespace android {
-
-static const char* WHITESPACE = " \t\r";
-static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:";
-
-struct Modifier {
- const char* label;
- int32_t metaState;
-};
-static const Modifier modifiers[] = {
- { "shift", AMETA_SHIFT_ON },
- { "lshift", AMETA_SHIFT_LEFT_ON },
- { "rshift", AMETA_SHIFT_RIGHT_ON },
- { "alt", AMETA_ALT_ON },
- { "lalt", AMETA_ALT_LEFT_ON },
- { "ralt", AMETA_ALT_RIGHT_ON },
- { "ctrl", AMETA_CTRL_ON },
- { "lctrl", AMETA_CTRL_LEFT_ON },
- { "rctrl", AMETA_CTRL_RIGHT_ON },
- { "meta", AMETA_META_ON },
- { "lmeta", AMETA_META_LEFT_ON },
- { "rmeta", AMETA_META_RIGHT_ON },
- { "sym", AMETA_SYM_ON },
- { "fn", AMETA_FUNCTION_ON },
- { "capslock", AMETA_CAPS_LOCK_ON },
- { "numlock", AMETA_NUM_LOCK_ON },
- { "scrolllock", AMETA_SCROLL_LOCK_ON },
-};
-
-#if DEBUG_MAPPING
-static String8 toString(const char16_t* chars, size_t numChars) {
- String8 result;
- for (size_t i = 0; i < numChars; i++) {
- result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]);
- }
- return result;
-}
-#endif
-
-
-// --- KeyCharacterMap ---
-
-sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
-
-KeyCharacterMap::KeyCharacterMap() :
- mType(KEYBOARD_TYPE_UNKNOWN) {
-}
-
-KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
- RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode),
- mKeysByUsageCode(other.mKeysByUsageCode) {
- for (size_t i = 0; i < other.mKeys.size(); i++) {
- mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
- }
-}
-
-KeyCharacterMap::~KeyCharacterMap() {
- for (size_t i = 0; i < mKeys.size(); i++) {
- Key* key = mKeys.editValueAt(i);
- delete key;
- }
-}
-
-status_t KeyCharacterMap::load(const String8& filename,
- Format format, sp<KeyCharacterMap>* outMap) {
- outMap->clear();
-
- Tokenizer* tokenizer;
- status_t status = Tokenizer::open(filename, &tokenizer);
- if (status) {
- ALOGE("Error %d opening key character map file %s.", status, filename.string());
- } else {
- status = load(tokenizer, format, outMap);
- delete tokenizer;
- }
- return status;
-}
-
-status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
- Format format, sp<KeyCharacterMap>* outMap) {
- outMap->clear();
-
- Tokenizer* tokenizer;
- status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
- if (status) {
- ALOGE("Error %d opening key character map.", status);
- } else {
- status = load(tokenizer, format, outMap);
- delete tokenizer;
- }
- return status;
-}
-
-status_t KeyCharacterMap::load(Tokenizer* tokenizer,
- Format format, sp<KeyCharacterMap>* outMap) {
- status_t status = OK;
- sp<KeyCharacterMap> map = new KeyCharacterMap();
- if (!map.get()) {
- ALOGE("Error allocating key character map.");
- status = NO_MEMORY;
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(map.get(), tokenizer, format);
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (!status) {
- *outMap = map;
- }
- }
- return status;
-}
-
-sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
- const sp<KeyCharacterMap>& overlay) {
- if (overlay == NULL) {
- return base;
- }
- if (base == NULL) {
- return overlay;
- }
-
- sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
- for (size_t i = 0; i < overlay->mKeys.size(); i++) {
- int32_t keyCode = overlay->mKeys.keyAt(i);
- Key* key = overlay->mKeys.valueAt(i);
- ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
- if (oldIndex >= 0) {
- delete map->mKeys.valueAt(oldIndex);
- map->mKeys.editValueAt(oldIndex) = new Key(*key);
- } else {
- map->mKeys.add(keyCode, new Key(*key));
- }
- }
-
- for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) {
- map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i),
- overlay->mKeysByScanCode.valueAt(i));
- }
-
- for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) {
- map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i),
- overlay->mKeysByUsageCode.valueAt(i));
- }
- return map;
-}
-
-sp<KeyCharacterMap> KeyCharacterMap::empty() {
- return sEmpty;
-}
-
-int32_t KeyCharacterMap::getKeyboardType() const {
- return mType;
-}
-
-char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
- char16_t result = 0;
- const Key* key;
- if (getKey(keyCode, &key)) {
- result = key->label;
- }
-#if DEBUG_MAPPING
- ALOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result);
-#endif
- return result;
-}
-
-char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
- char16_t result = 0;
- const Key* key;
- if (getKey(keyCode, &key)) {
- result = key->number;
- }
-#if DEBUG_MAPPING
- ALOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result);
-#endif
- return result;
-}
-
-char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const {
- char16_t result = 0;
- const Key* key;
- const Behavior* behavior;
- if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
- result = behavior->character;
- }
-#if DEBUG_MAPPING
- ALOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result);
-#endif
- return result;
-}
-
-bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState,
- FallbackAction* outFallbackAction) const {
- outFallbackAction->keyCode = 0;
- outFallbackAction->metaState = 0;
-
- bool result = false;
- const Key* key;
- const Behavior* behavior;
- if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
- if (behavior->fallbackKeyCode) {
- outFallbackAction->keyCode = behavior->fallbackKeyCode;
- outFallbackAction->metaState = metaState & ~behavior->metaState;
- result = true;
- }
- }
-#if DEBUG_MAPPING
- ALOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, "
- "fallback keyCode=%d, fallback metaState=0x%08x.",
- keyCode, metaState, result ? "true" : "false",
- outFallbackAction->keyCode, outFallbackAction->metaState);
-#endif
- return result;
-}
-
-char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars,
- int32_t metaState) const {
- char16_t result = 0;
- const Key* key;
- if (getKey(keyCode, &key)) {
- // Try to find the most general behavior that maps to this character.
- // For example, the base key behavior will usually be last in the list.
- // However, if we find a perfect meta state match for one behavior then use that one.
- for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
- if (behavior->character) {
- for (size_t i = 0; i < numChars; i++) {
- if (behavior->character == chars[i]) {
- result = behavior->character;
- if ((behavior->metaState & metaState) == behavior->metaState) {
- goto ExactMatch;
- }
- break;
- }
- }
- }
- }
- ExactMatch: ;
- }
-#if DEBUG_MAPPING
- ALOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.",
- keyCode, toString(chars, numChars).string(), metaState, result);
-#endif
- return result;
-}
-
-bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
- Vector<KeyEvent>& outEvents) const {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-
- for (size_t i = 0; i < numChars; i++) {
- int32_t keyCode, metaState;
- char16_t ch = chars[i];
- if (!findKey(ch, &keyCode, &metaState)) {
-#if DEBUG_MAPPING
- ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
- deviceId, toString(chars, numChars).string(), ch);
-#endif
- return false;
- }
-
- int32_t currentMetaState = 0;
- addMetaKeys(outEvents, deviceId, metaState, true, now, ¤tMetaState);
- addKey(outEvents, deviceId, keyCode, currentMetaState, true, now);
- addKey(outEvents, deviceId, keyCode, currentMetaState, false, now);
- addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState);
- }
-#if DEBUG_MAPPING
- ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
- deviceId, toString(chars, numChars).string(), int32_t(outEvents.size()));
- for (size_t i = 0; i < outEvents.size(); i++) {
- ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.",
- outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
- outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up");
- }
-#endif
- return true;
-}
-
-status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
- if (usageCode) {
- ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
-#if DEBUG_MAPPING
- ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
- scanCode, usageCode, *outKeyCode);
-#endif
- *outKeyCode = mKeysByUsageCode.valueAt(index);
- return OK;
- }
- }
- if (scanCode) {
- ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
-#if DEBUG_MAPPING
- ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
- scanCode, usageCode, *outKeyCode);
-#endif
- *outKeyCode = mKeysByScanCode.valueAt(index);
- return OK;
- }
- }
-
-#if DEBUG_MAPPING
- ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
-#endif
- *outKeyCode = AKEYCODE_UNKNOWN;
- return NAME_NOT_FOUND;
-}
-
-bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
- ssize_t index = mKeys.indexOfKey(keyCode);
- if (index >= 0) {
- *outKey = mKeys.valueAt(index);
- return true;
- }
- return false;
-}
-
-bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState,
- const Key** outKey, const Behavior** outBehavior) const {
- const Key* key;
- if (getKey(keyCode, &key)) {
- const Behavior* behavior = key->firstBehavior;
- while (behavior) {
- if (matchesMetaState(metaState, behavior->metaState)) {
- *outKey = key;
- *outBehavior = behavior;
- return true;
- }
- behavior = behavior->next;
- }
- }
- return false;
-}
-
-bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) {
- // Behavior must have at least the set of meta states specified.
- // And if the key event has CTRL, ALT or META then the behavior must exactly
- // match those, taking into account that a behavior can specify that it handles
- // one, both or either of a left/right modifier pair.
- if ((eventMetaState & behaviorMetaState) == behaviorMetaState) {
- const int32_t EXACT_META_STATES =
- AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON
- | AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON
- | AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON;
- int32_t unmatchedMetaState = eventMetaState & ~behaviorMetaState & EXACT_META_STATES;
- if (behaviorMetaState & AMETA_CTRL_ON) {
- unmatchedMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
- } else if (behaviorMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
- unmatchedMetaState &= ~AMETA_CTRL_ON;
- }
- if (behaviorMetaState & AMETA_ALT_ON) {
- unmatchedMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
- } else if (behaviorMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
- unmatchedMetaState &= ~AMETA_ALT_ON;
- }
- if (behaviorMetaState & AMETA_META_ON) {
- unmatchedMetaState &= ~(AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
- } else if (behaviorMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
- unmatchedMetaState &= ~AMETA_META_ON;
- }
- return !unmatchedMetaState;
- }
- return false;
-}
-
-bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
- if (!ch) {
- return false;
- }
-
- for (size_t i = 0; i < mKeys.size(); i++) {
- const Key* key = mKeys.valueAt(i);
-
- // Try to find the most general behavior that maps to this character.
- // For example, the base key behavior will usually be last in the list.
- const Behavior* found = NULL;
- for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
- if (behavior->character == ch) {
- found = behavior;
- }
- }
- if (found) {
- *outKeyCode = mKeys.keyAt(i);
- *outMetaState = found->metaState;
- return true;
- }
- }
- return false;
-}
-
-void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
- outEvents.push();
- KeyEvent& event = outEvents.editTop();
- event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- 0, keyCode, 0, metaState, 0, time, time);
-}
-
-void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
- int32_t* currentMetaState) {
- // Add and remove meta keys symmetrically.
- if (down) {
- addLockedMetaKey(outEvents, deviceId, metaState, time,
- AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
- addLockedMetaKey(outEvents, deviceId, metaState, time,
- AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
- addLockedMetaKey(outEvents, deviceId, metaState, time,
- AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
-
- addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
- AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
- AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
- AMETA_SHIFT_ON, currentMetaState);
- addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
- AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
- AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
- AMETA_ALT_ON, currentMetaState);
- addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
- AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
- AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
- AMETA_CTRL_ON, currentMetaState);
- addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
- AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
- AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
- AMETA_META_ON, currentMetaState);
-
- addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
- AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
- addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
- AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
- } else {
- addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
- AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
- addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
- AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
-
- addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
- AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
- AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
- AMETA_META_ON, currentMetaState);
- addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
- AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
- AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
- AMETA_CTRL_ON, currentMetaState);
- addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
- AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
- AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
- AMETA_ALT_ON, currentMetaState);
- addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
- AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
- AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
- AMETA_SHIFT_ON, currentMetaState);
-
- addLockedMetaKey(outEvents, deviceId, metaState, time,
- AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
- addLockedMetaKey(outEvents, deviceId, metaState, time,
- AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
- addLockedMetaKey(outEvents, deviceId, metaState, time,
- AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
- }
-}
-
-bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
- int32_t keyCode, int32_t keyMetaState,
- int32_t* currentMetaState) {
- if ((metaState & keyMetaState) == keyMetaState) {
- *currentMetaState = updateMetaState(keyCode, down, *currentMetaState);
- addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time);
- return true;
- }
- return false;
-}
-
-void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
- int32_t leftKeyCode, int32_t leftKeyMetaState,
- int32_t rightKeyCode, int32_t rightKeyMetaState,
- int32_t eitherKeyMetaState,
- int32_t* currentMetaState) {
- bool specific = false;
- specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
- leftKeyCode, leftKeyMetaState, currentMetaState);
- specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
- rightKeyCode, rightKeyMetaState, currentMetaState);
-
- if (!specific) {
- addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
- leftKeyCode, eitherKeyMetaState, currentMetaState);
- }
-}
-
-void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t metaState, nsecs_t time,
- int32_t keyCode, int32_t keyMetaState,
- int32_t* currentMetaState) {
- if ((metaState & keyMetaState) == keyMetaState) {
- *currentMetaState = updateMetaState(keyCode, true, *currentMetaState);
- addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time);
- *currentMetaState = updateMetaState(keyCode, false, *currentMetaState);
- addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time);
- }
-}
-
-#if HAVE_ANDROID_OS
-sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
- sp<KeyCharacterMap> map = new KeyCharacterMap();
- map->mType = parcel->readInt32();
- size_t numKeys = parcel->readInt32();
- if (parcel->errorCheck()) {
- return NULL;
- }
-
- for (size_t i = 0; i < numKeys; i++) {
- int32_t keyCode = parcel->readInt32();
- char16_t label = parcel->readInt32();
- char16_t number = parcel->readInt32();
- if (parcel->errorCheck()) {
- return NULL;
- }
-
- Key* key = new Key();
- key->label = label;
- key->number = number;
- map->mKeys.add(keyCode, key);
-
- Behavior* lastBehavior = NULL;
- while (parcel->readInt32()) {
- int32_t metaState = parcel->readInt32();
- char16_t character = parcel->readInt32();
- int32_t fallbackKeyCode = parcel->readInt32();
- if (parcel->errorCheck()) {
- return NULL;
- }
-
- Behavior* behavior = new Behavior();
- behavior->metaState = metaState;
- behavior->character = character;
- behavior->fallbackKeyCode = fallbackKeyCode;
- if (lastBehavior) {
- lastBehavior->next = behavior;
- } else {
- key->firstBehavior = behavior;
- }
- lastBehavior = behavior;
- }
-
- if (parcel->errorCheck()) {
- return NULL;
- }
- }
- return map;
-}
-
-void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
- parcel->writeInt32(mType);
-
- size_t numKeys = mKeys.size();
- parcel->writeInt32(numKeys);
- for (size_t i = 0; i < numKeys; i++) {
- int32_t keyCode = mKeys.keyAt(i);
- const Key* key = mKeys.valueAt(i);
- parcel->writeInt32(keyCode);
- parcel->writeInt32(key->label);
- parcel->writeInt32(key->number);
- for (const Behavior* behavior = key->firstBehavior; behavior != NULL;
- behavior = behavior->next) {
- parcel->writeInt32(1);
- parcel->writeInt32(behavior->metaState);
- parcel->writeInt32(behavior->character);
- parcel->writeInt32(behavior->fallbackKeyCode);
- }
- parcel->writeInt32(0);
- }
-}
-#endif
-
-
-// --- KeyCharacterMap::Key ---
-
-KeyCharacterMap::Key::Key() :
- label(0), number(0), firstBehavior(NULL) {
-}
-
-KeyCharacterMap::Key::Key(const Key& other) :
- label(other.label), number(other.number),
- firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
-}
-
-KeyCharacterMap::Key::~Key() {
- Behavior* behavior = firstBehavior;
- while (behavior) {
- Behavior* next = behavior->next;
- delete behavior;
- behavior = next;
- }
-}
-
-
-// --- KeyCharacterMap::Behavior ---
-
-KeyCharacterMap::Behavior::Behavior() :
- next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
-}
-
-KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
- next(other.next ? new Behavior(*other.next) : NULL),
- metaState(other.metaState), character(other.character),
- fallbackKeyCode(other.fallbackKeyCode) {
-}
-
-
-// --- KeyCharacterMap::Parser ---
-
-KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
- mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
-}
-
-KeyCharacterMap::Parser::~Parser() {
-}
-
-status_t KeyCharacterMap::Parser::parse() {
- while (!mTokenizer->isEof()) {
-#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
-#endif
-
- mTokenizer->skipDelimiters(WHITESPACE);
-
- if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
- switch (mState) {
- case STATE_TOP: {
- String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
- if (keywordToken == "type") {
- mTokenizer->skipDelimiters(WHITESPACE);
- status_t status = parseType();
- if (status) return status;
- } else if (keywordToken == "map") {
- mTokenizer->skipDelimiters(WHITESPACE);
- status_t status = parseMap();
- if (status) return status;
- } else if (keywordToken == "key") {
- mTokenizer->skipDelimiters(WHITESPACE);
- status_t status = parseKey();
- if (status) return status;
- } else {
- ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
- keywordToken.string());
- return BAD_VALUE;
- }
- break;
- }
-
- case STATE_KEY: {
- status_t status = parseKeyProperty();
- if (status) return status;
- break;
- }
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
- ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
- return BAD_VALUE;
- }
- }
-
- mTokenizer->nextLine();
- }
-
- if (mState != STATE_TOP) {
- ALOGE("%s: Unterminated key description at end of file.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
-
- if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
- ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
-
- if (mFormat == FORMAT_BASE) {
- if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
- ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- } else if (mFormat == FORMAT_OVERLAY) {
- if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
- ALOGE("%s: Overlay keyboard layout missing required keyboard "
- "'type OVERLAY' declaration.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- }
-
- return NO_ERROR;
-}
-
-status_t KeyCharacterMap::Parser::parseType() {
- if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
- ALOGE("%s: Duplicate keyboard 'type' declaration.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
-
- KeyboardType type;
- String8 typeToken = mTokenizer->nextToken(WHITESPACE);
- if (typeToken == "NUMERIC") {
- type = KEYBOARD_TYPE_NUMERIC;
- } else if (typeToken == "PREDICTIVE") {
- type = KEYBOARD_TYPE_PREDICTIVE;
- } else if (typeToken == "ALPHA") {
- type = KEYBOARD_TYPE_ALPHA;
- } else if (typeToken == "FULL") {
- type = KEYBOARD_TYPE_FULL;
- } else if (typeToken == "SPECIAL_FUNCTION") {
- type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
- } else if (typeToken == "OVERLAY") {
- type = KEYBOARD_TYPE_OVERLAY;
- } else {
- ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
- typeToken.string());
- return BAD_VALUE;
- }
-
-#if DEBUG_PARSER
- ALOGD("Parsed type: type=%d.", type);
-#endif
- mMap->mType = type;
- return NO_ERROR;
-}
-
-status_t KeyCharacterMap::Parser::parseMap() {
- String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
- if (keywordToken == "key") {
- mTokenizer->skipDelimiters(WHITESPACE);
- return parseMapKey();
- }
- ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(),
- keywordToken.string());
- return BAD_VALUE;
-}
-
-status_t KeyCharacterMap::Parser::parseMapKey() {
- String8 codeToken = mTokenizer->nextToken(WHITESPACE);
- bool mapUsage = false;
- if (codeToken == "usage") {
- mapUsage = true;
- mTokenizer->skipDelimiters(WHITESPACE);
- codeToken = mTokenizer->nextToken(WHITESPACE);
- }
-
- char* end;
- int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
- if (*end) {
- ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
- return BAD_VALUE;
- }
- KeyedVector<int32_t, int32_t>& map =
- mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
- if (map.indexOfKey(code) >= 0) {
- ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
- return BAD_VALUE;
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
- if (!keyCode) {
- ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
- return BAD_VALUE;
- }
-
-#if DEBUG_PARSER
- ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
- mapUsage ? "usage" : "scan code", code, keyCode);
-#endif
- map.add(code, keyCode);
- return NO_ERROR;
-}
-
-status_t KeyCharacterMap::Parser::parseKey() {
- String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
- if (!keyCode) {
- ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
- return BAD_VALUE;
- }
- if (mMap->mKeys.indexOfKey(keyCode) >= 0) {
- ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
- return BAD_VALUE;
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
- if (openBraceToken != "{") {
- ALOGE("%s: Expected '{' after key code label, got '%s'.",
- mTokenizer->getLocation().string(), openBraceToken.string());
- return BAD_VALUE;
- }
-
-#if DEBUG_PARSER
- ALOGD("Parsed beginning of key: keyCode=%d.", keyCode);
-#endif
- mKeyCode = keyCode;
- mMap->mKeys.add(keyCode, new Key());
- mState = STATE_KEY;
- return NO_ERROR;
-}
-
-status_t KeyCharacterMap::Parser::parseKeyProperty() {
- Key* key = mMap->mKeys.valueFor(mKeyCode);
- String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
- if (token == "}") {
- mState = STATE_TOP;
- return finishKey(key);
- }
-
- Vector<Property> properties;
-
- // Parse all comma-delimited property names up to the first colon.
- for (;;) {
- if (token == "label") {
- properties.add(Property(PROPERTY_LABEL));
- } else if (token == "number") {
- properties.add(Property(PROPERTY_NUMBER));
- } else {
- int32_t metaState;
- status_t status = parseModifier(token, &metaState);
- if (status) {
- ALOGE("%s: Expected a property name or modifier, got '%s'.",
- mTokenizer->getLocation().string(), token.string());
- return status;
- }
- properties.add(Property(PROPERTY_META, metaState));
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- if (!mTokenizer->isEol()) {
- char ch = mTokenizer->nextChar();
- if (ch == ':') {
- break;
- } else if (ch == ',') {
- mTokenizer->skipDelimiters(WHITESPACE);
- token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
- continue;
- }
- }
-
- ALOGE("%s: Expected ',' or ':' after property name.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
-
- // Parse behavior after the colon.
- mTokenizer->skipDelimiters(WHITESPACE);
-
- Behavior behavior;
- bool haveCharacter = false;
- bool haveFallback = false;
-
- do {
- char ch = mTokenizer->peekChar();
- if (ch == '\'') {
- char16_t character;
- status_t status = parseCharacterLiteral(&character);
- if (status || !character) {
- ALOGE("%s: Invalid character literal for key.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- if (haveCharacter) {
- ALOGE("%s: Cannot combine multiple character literals or 'none'.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- behavior.character = character;
- haveCharacter = true;
- } else {
- token = mTokenizer->nextToken(WHITESPACE);
- if (token == "none") {
- if (haveCharacter) {
- ALOGE("%s: Cannot combine multiple character literals or 'none'.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- haveCharacter = true;
- } else if (token == "fallback") {
- mTokenizer->skipDelimiters(WHITESPACE);
- token = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(token.string());
- if (!keyCode) {
- ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
- mTokenizer->getLocation().string(),
- token.string());
- return BAD_VALUE;
- }
- if (haveFallback) {
- ALOGE("%s: Cannot combine multiple fallback key codes.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- behavior.fallbackKeyCode = keyCode;
- haveFallback = true;
- } else {
- ALOGE("%s: Expected a key behavior after ':'.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- } while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#');
-
- // Add the behavior.
- for (size_t i = 0; i < properties.size(); i++) {
- const Property& property = properties.itemAt(i);
- switch (property.property) {
- case PROPERTY_LABEL:
- if (key->label) {
- ALOGE("%s: Duplicate label for key.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- key->label = behavior.character;
-#if DEBUG_PARSER
- ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label);
-#endif
- break;
- case PROPERTY_NUMBER:
- if (key->number) {
- ALOGE("%s: Duplicate number for key.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- key->number = behavior.character;
-#if DEBUG_PARSER
- ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number);
-#endif
- break;
- case PROPERTY_META: {
- for (Behavior* b = key->firstBehavior; b; b = b->next) {
- if (b->metaState == property.metaState) {
- ALOGE("%s: Duplicate key behavior for modifier.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- }
- Behavior* newBehavior = new Behavior(behavior);
- newBehavior->metaState = property.metaState;
- newBehavior->next = key->firstBehavior;
- key->firstBehavior = newBehavior;
-#if DEBUG_PARSER
- ALOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode,
- newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode);
-#endif
- break;
- }
- }
- }
- return NO_ERROR;
-}
-
-status_t KeyCharacterMap::Parser::finishKey(Key* key) {
- // Fill in default number property.
- if (!key->number) {
- char16_t digit = 0;
- char16_t symbol = 0;
- for (Behavior* b = key->firstBehavior; b; b = b->next) {
- char16_t ch = b->character;
- if (ch) {
- if (ch >= '0' && ch <= '9') {
- digit = ch;
- } else if (ch == '(' || ch == ')' || ch == '#' || ch == '*'
- || ch == '-' || ch == '+' || ch == ',' || ch == '.'
- || ch == '\'' || ch == ':' || ch == ';' || ch == '/') {
- symbol = ch;
- }
- }
- }
- key->number = digit ? digit : symbol;
- }
- return NO_ERROR;
-}
-
-status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
- if (token == "base") {
- *outMetaState = 0;
- return NO_ERROR;
- }
-
- int32_t combinedMeta = 0;
-
- const char* str = token.string();
- const char* start = str;
- for (const char* cur = str; ; cur++) {
- char ch = *cur;
- if (ch == '+' || ch == '\0') {
- size_t len = cur - start;
- int32_t metaState = 0;
- for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) {
- if (strlen(modifiers[i].label) == len
- && strncmp(modifiers[i].label, start, len) == 0) {
- metaState = modifiers[i].metaState;
- break;
- }
- }
- if (!metaState) {
- return BAD_VALUE;
- }
- if (combinedMeta & metaState) {
- ALOGE("%s: Duplicate modifier combination '%s'.",
- mTokenizer->getLocation().string(), token.string());
- return BAD_VALUE;
- }
-
- combinedMeta |= metaState;
- start = cur + 1;
-
- if (ch == '\0') {
- break;
- }
- }
- }
- *outMetaState = combinedMeta;
- return NO_ERROR;
-}
-
-status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) {
- char ch = mTokenizer->nextChar();
- if (ch != '\'') {
- goto Error;
- }
-
- ch = mTokenizer->nextChar();
- if (ch == '\\') {
- // Escape sequence.
- ch = mTokenizer->nextChar();
- if (ch == 'n') {
- *outCharacter = '\n';
- } else if (ch == 't') {
- *outCharacter = '\t';
- } else if (ch == '\\') {
- *outCharacter = '\\';
- } else if (ch == '\'') {
- *outCharacter = '\'';
- } else if (ch == '"') {
- *outCharacter = '"';
- } else if (ch == 'u') {
- *outCharacter = 0;
- for (int i = 0; i < 4; i++) {
- ch = mTokenizer->nextChar();
- int digit;
- if (ch >= '0' && ch <= '9') {
- digit = ch - '0';
- } else if (ch >= 'A' && ch <= 'F') {
- digit = ch - 'A' + 10;
- } else if (ch >= 'a' && ch <= 'f') {
- digit = ch - 'a' + 10;
- } else {
- goto Error;
- }
- *outCharacter = (*outCharacter << 4) | digit;
- }
- } else {
- goto Error;
- }
- } else if (ch >= 32 && ch <= 126 && ch != '\'') {
- // ASCII literal character.
- *outCharacter = ch;
- } else {
- goto Error;
- }
-
- ch = mTokenizer->nextChar();
- if (ch != '\'') {
- goto Error;
- }
-
- // Ensure that we consumed the entire token.
- if (mTokenizer->nextToken(WHITESPACE).isEmpty()) {
- return NO_ERROR;
- }
-
-Error:
- ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string());
- return BAD_VALUE;
-}
-
-} // namespace android
diff --git a/libs/androidfw/KeyLayoutMap.cpp b/libs/androidfw/KeyLayoutMap.cpp
deleted file mode 100644
index ae14f23..0000000
--- a/libs/androidfw/KeyLayoutMap.cpp
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "KeyLayoutMap"
-
-#include <stdlib.h>
-#include <android/keycodes.h>
-#include <androidfw/Keyboard.h>
-#include <androidfw/KeyLayoutMap.h>
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/Tokenizer.h>
-#include <utils/Timers.h>
-
-// Enables debug output for the parser.
-#define DEBUG_PARSER 0
-
-// Enables debug output for parser performance.
-#define DEBUG_PARSER_PERFORMANCE 0
-
-// Enables debug output for mapping.
-#define DEBUG_MAPPING 0
-
-
-namespace android {
-
-static const char* WHITESPACE = " \t\r";
-
-// --- KeyLayoutMap ---
-
-KeyLayoutMap::KeyLayoutMap() {
-}
-
-KeyLayoutMap::~KeyLayoutMap() {
-}
-
-status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {
- outMap->clear();
-
- Tokenizer* tokenizer;
- status_t status = Tokenizer::open(filename, &tokenizer);
- if (status) {
- ALOGE("Error %d opening key layout map file %s.", status, filename.string());
- } else {
- sp<KeyLayoutMap> map = new KeyLayoutMap();
- if (!map.get()) {
- ALOGE("Error allocating key layout map.");
- status = NO_MEMORY;
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(map.get(), tokenizer);
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (!status) {
- *outMap = map;
- }
- }
- delete tokenizer;
- }
- return status;
-}
-
-status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
- int32_t* outKeyCode, uint32_t* outFlags) const {
- const Key* key = getKey(scanCode, usageCode);
- if (!key) {
-#if DEBUG_MAPPING
- ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
-#endif
- *outKeyCode = AKEYCODE_UNKNOWN;
- *outFlags = 0;
- return NAME_NOT_FOUND;
- }
-
- *outKeyCode = key->keyCode;
- *outFlags = key->flags;
-
-#if DEBUG_MAPPING
- ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.",
- scanCode, usageCode, *outKeyCode, *outFlags);
-#endif
- return NO_ERROR;
-}
-
-const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const {
- if (usageCode) {
- ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
- return &mKeysByUsageCode.valueAt(index);
- }
- }
- if (scanCode) {
- ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
- return &mKeysByScanCode.valueAt(index);
- }
- }
- return NULL;
-}
-
-status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
- const size_t N = mKeysByScanCode.size();
- for (size_t i=0; i<N; i++) {
- if (mKeysByScanCode.valueAt(i).keyCode == keyCode) {
- outScanCodes->add(mKeysByScanCode.keyAt(i));
- }
- }
- return NO_ERROR;
-}
-
-status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
- ssize_t index = mAxes.indexOfKey(scanCode);
- if (index < 0) {
-#if DEBUG_MAPPING
- ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
-#endif
- return NAME_NOT_FOUND;
- }
-
- *outAxisInfo = mAxes.valueAt(index);
-
-#if DEBUG_MAPPING
- ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, "
- "splitValue=%d, flatOverride=%d.",
- scanCode,
- outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis,
- outAxisInfo->splitValue, outAxisInfo->flatOverride);
-#endif
- return NO_ERROR;
-}
-
-
-// --- KeyLayoutMap::Parser ---
-
-KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
- mMap(map), mTokenizer(tokenizer) {
-}
-
-KeyLayoutMap::Parser::~Parser() {
-}
-
-status_t KeyLayoutMap::Parser::parse() {
- while (!mTokenizer->isEof()) {
-#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
-#endif
-
- mTokenizer->skipDelimiters(WHITESPACE);
-
- if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
- String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
- if (keywordToken == "key") {
- mTokenizer->skipDelimiters(WHITESPACE);
- status_t status = parseKey();
- if (status) return status;
- } else if (keywordToken == "axis") {
- mTokenizer->skipDelimiters(WHITESPACE);
- status_t status = parseAxis();
- if (status) return status;
- } else {
- ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
- keywordToken.string());
- return BAD_VALUE;
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
- ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
- return BAD_VALUE;
- }
- }
-
- mTokenizer->nextLine();
- }
- return NO_ERROR;
-}
-
-status_t KeyLayoutMap::Parser::parseKey() {
- String8 codeToken = mTokenizer->nextToken(WHITESPACE);
- bool mapUsage = false;
- if (codeToken == "usage") {
- mapUsage = true;
- mTokenizer->skipDelimiters(WHITESPACE);
- codeToken = mTokenizer->nextToken(WHITESPACE);
- }
-
- char* end;
- int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
- if (*end) {
- ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
- return BAD_VALUE;
- }
- KeyedVector<int32_t, Key>& map =
- mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
- if (map.indexOfKey(code) >= 0) {
- ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
- return BAD_VALUE;
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
- if (!keyCode) {
- ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
- return BAD_VALUE;
- }
-
- uint32_t flags = 0;
- for (;;) {
- mTokenizer->skipDelimiters(WHITESPACE);
- if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
-
- String8 flagToken = mTokenizer->nextToken(WHITESPACE);
- uint32_t flag = getKeyFlagByLabel(flagToken.string());
- if (!flag) {
- ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
- flagToken.string());
- return BAD_VALUE;
- }
- if (flags & flag) {
- ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
- flagToken.string());
- return BAD_VALUE;
- }
- flags |= flag;
- }
-
-#if DEBUG_PARSER
- ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
- mapUsage ? "usage" : "scan code", code, keyCode, flags);
-#endif
- Key key;
- key.keyCode = keyCode;
- key.flags = flags;
- map.add(code, key);
- return NO_ERROR;
-}
-
-status_t KeyLayoutMap::Parser::parseAxis() {
- String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
- char* end;
- int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
- if (*end) {
- ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
- scanCodeToken.string());
- return BAD_VALUE;
- }
- if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
- ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
- scanCodeToken.string());
- return BAD_VALUE;
- }
-
- AxisInfo axisInfo;
-
- mTokenizer->skipDelimiters(WHITESPACE);
- String8 token = mTokenizer->nextToken(WHITESPACE);
- if (token == "invert") {
- axisInfo.mode = AxisInfo::MODE_INVERT;
-
- mTokenizer->skipDelimiters(WHITESPACE);
- String8 axisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.axis = getAxisByLabel(axisToken.string());
- if (axisInfo.axis < 0) {
- ALOGE("%s: Expected inverted axis label, got '%s'.",
- mTokenizer->getLocation().string(), axisToken.string());
- return BAD_VALUE;
- }
- } else if (token == "split") {
- axisInfo.mode = AxisInfo::MODE_SPLIT;
-
- mTokenizer->skipDelimiters(WHITESPACE);
- String8 splitToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0));
- if (*end) {
- ALOGE("%s: Expected split value, got '%s'.",
- mTokenizer->getLocation().string(), splitToken.string());
- return BAD_VALUE;
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.axis = getAxisByLabel(lowAxisToken.string());
- if (axisInfo.axis < 0) {
- ALOGE("%s: Expected low axis label, got '%s'.",
- mTokenizer->getLocation().string(), lowAxisToken.string());
- return BAD_VALUE;
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.highAxis = getAxisByLabel(highAxisToken.string());
- if (axisInfo.highAxis < 0) {
- ALOGE("%s: Expected high axis label, got '%s'.",
- mTokenizer->getLocation().string(), highAxisToken.string());
- return BAD_VALUE;
- }
- } else {
- axisInfo.axis = getAxisByLabel(token.string());
- if (axisInfo.axis < 0) {
- ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
- mTokenizer->getLocation().string(), token.string());
- return BAD_VALUE;
- }
- }
-
- for (;;) {
- mTokenizer->skipDelimiters(WHITESPACE);
- if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') {
- break;
- }
- String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
- if (keywordToken == "flat") {
- mTokenizer->skipDelimiters(WHITESPACE);
- String8 flatToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0));
- if (*end) {
- ALOGE("%s: Expected flat value, got '%s'.",
- mTokenizer->getLocation().string(), flatToken.string());
- return BAD_VALUE;
- }
- } else {
- ALOGE("%s: Expected keyword 'flat', got '%s'.",
- mTokenizer->getLocation().string(), keywordToken.string());
- return BAD_VALUE;
- }
- }
-
-#if DEBUG_PARSER
- ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
- "splitValue=%d, flatOverride=%d.",
- scanCode,
- axisInfo.mode, axisInfo.axis, axisInfo.highAxis,
- axisInfo.splitValue, axisInfo.flatOverride);
-#endif
- mMap->mAxes.add(scanCode, axisInfo);
- return NO_ERROR;
-}
-
-};
diff --git a/libs/androidfw/Keyboard.cpp b/libs/androidfw/Keyboard.cpp
deleted file mode 100644
index 4ddbeab..0000000
--- a/libs/androidfw/Keyboard.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Keyboard"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include <androidfw/Keyboard.h>
-#include <androidfw/KeycodeLabels.h>
-#include <androidfw/KeyLayoutMap.h>
-#include <androidfw/KeyCharacterMap.h>
-#include <androidfw/InputDevice.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <cutils/properties.h>
-
-namespace android {
-
-// --- KeyMap ---
-
-KeyMap::KeyMap() {
-}
-
-KeyMap::~KeyMap() {
-}
-
-status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
- const PropertyMap* deviceConfiguration) {
- // Use the configured key layout if available.
- if (deviceConfiguration) {
- String8 keyLayoutName;
- if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
- keyLayoutName)) {
- status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
- if (status == NAME_NOT_FOUND) {
- ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
- "it was not found.",
- deviceIdenfifier.name.string(), keyLayoutName.string());
- }
- }
-
- String8 keyCharacterMapName;
- if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
- keyCharacterMapName)) {
- status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
- if (status == NAME_NOT_FOUND) {
- ALOGE("Configuration for keyboard device '%s' requested keyboard character "
- "map '%s' but it was not found.",
- deviceIdenfifier.name.string(), keyLayoutName.string());
- }
- }
-
- if (isComplete()) {
- return OK;
- }
- }
-
- // Try searching by device identifier.
- if (probeKeyMap(deviceIdenfifier, String8::empty())) {
- return OK;
- }
-
- // Fall back on the Generic key map.
- // TODO Apply some additional heuristics here to figure out what kind of
- // generic key map to use (US English, etc.) for typical external keyboards.
- if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
- return OK;
- }
-
- // Try the Virtual key map as a last resort.
- if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
- return OK;
- }
-
- // Give up!
- ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
- deviceIdenfifier.name.string());
- return NAME_NOT_FOUND;
-}
-
-bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
- const String8& keyMapName) {
- if (!haveKeyLayout()) {
- loadKeyLayout(deviceIdentifier, keyMapName);
- }
- if (!haveKeyCharacterMap()) {
- loadKeyCharacterMap(deviceIdentifier, keyMapName);
- }
- return isComplete();
-}
-
-status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name) {
- String8 path(getPath(deviceIdentifier, name,
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
- if (path.isEmpty()) {
- return NAME_NOT_FOUND;
- }
-
- status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
- if (status) {
- return status;
- }
-
- keyLayoutFile.setTo(path);
- return OK;
-}
-
-status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name) {
- String8 path(getPath(deviceIdentifier, name,
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
- if (path.isEmpty()) {
- return NAME_NOT_FOUND;
- }
-
- status_t status = KeyCharacterMap::load(path,
- KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
- if (status) {
- return status;
- }
-
- keyCharacterMapFile.setTo(path);
- return OK;
-}
-
-String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name, InputDeviceConfigurationFileType type) {
- return name.isEmpty()
- ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
- : getInputDeviceConfigurationFilePathByName(name, type);
-}
-
-
-// --- Global functions ---
-
-bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
- const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
- if (!keyMap->haveKeyCharacterMap()
- || keyMap->keyCharacterMap->getKeyboardType()
- == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
- return false;
- }
-
- if (deviceConfiguration) {
- bool builtIn = false;
- if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn)
- && builtIn) {
- return true;
- }
- }
-
- return strstr(deviceIdentifier.name.string(), "-keypad");
-}
-
-static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) {
- while (list->literal) {
- if (strcmp(literal, list->literal) == 0) {
- return list->value;
- }
- list++;
- }
- return list->value;
-}
-
-static const char* lookupLabelByValue(int value, const KeycodeLabel *list) {
- while (list->literal) {
- if (list->value == value) {
- return list->literal;
- }
- list++;
- }
- return NULL;
-}
-
-int32_t getKeyCodeByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, KEYCODES));
-}
-
-uint32_t getKeyFlagByLabel(const char* label) {
- return uint32_t(lookupValueByLabel(label, FLAGS));
-}
-
-int32_t getAxisByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, AXES));
-}
-
-const char* getAxisLabel(int32_t axisId) {
- return lookupLabelByValue(axisId, AXES);
-}
-
-static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
- int32_t newMetaState;
- if (down) {
- newMetaState = oldMetaState | mask;
- } else {
- newMetaState = oldMetaState &
- ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
- }
-
- if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
- newMetaState |= AMETA_ALT_ON;
- }
-
- if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
- newMetaState |= AMETA_SHIFT_ON;
- }
-
- if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
- newMetaState |= AMETA_CTRL_ON;
- }
-
- if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
- newMetaState |= AMETA_META_ON;
- }
- return newMetaState;
-}
-
-static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
- if (down) {
- return oldMetaState;
- } else {
- return oldMetaState ^ mask;
- }
-}
-
-int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
- int32_t mask;
- switch (keyCode) {
- case AKEYCODE_ALT_LEFT:
- return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
- case AKEYCODE_ALT_RIGHT:
- return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
- case AKEYCODE_SHIFT_LEFT:
- return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
- case AKEYCODE_SHIFT_RIGHT:
- return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
- case AKEYCODE_SYM:
- return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
- case AKEYCODE_FUNCTION:
- return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
- case AKEYCODE_CTRL_LEFT:
- return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
- case AKEYCODE_CTRL_RIGHT:
- return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
- case AKEYCODE_META_LEFT:
- return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
- case AKEYCODE_META_RIGHT:
- return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
- case AKEYCODE_CAPS_LOCK:
- return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
- case AKEYCODE_NUM_LOCK:
- return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
- case AKEYCODE_SCROLL_LOCK:
- return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
- default:
- return oldMetaState;
- }
-}
-
-bool isMetaKey(int32_t keyCode) {
- switch (keyCode) {
- case AKEYCODE_ALT_LEFT:
- case AKEYCODE_ALT_RIGHT:
- case AKEYCODE_SHIFT_LEFT:
- case AKEYCODE_SHIFT_RIGHT:
- case AKEYCODE_SYM:
- case AKEYCODE_FUNCTION:
- case AKEYCODE_CTRL_LEFT:
- case AKEYCODE_CTRL_RIGHT:
- case AKEYCODE_META_LEFT:
- case AKEYCODE_META_RIGHT:
- case AKEYCODE_CAPS_LOCK:
- case AKEYCODE_NUM_LOCK:
- case AKEYCODE_SCROLL_LOCK:
- return true;
- default:
- return false;
- }
-}
-
-
-} // namespace android
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index a730065..1128e02 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -24,7 +24,6 @@
#include <utils/Log.h>
#include <utils/String16.h>
#include <utils/String8.h>
-#include <utils/TextOutput.h>
#include <stdlib.h>
#include <string.h>
@@ -5324,13 +5323,12 @@
}
-#ifndef HAVE_ANDROID_OS
#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string())
#define CHAR16_ARRAY_EQ(constant, var, len) \
((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len))))
-void print_complex(uint32_t complex, bool isFraction)
+static void print_complex(uint32_t complex, bool isFraction)
{
const float MANTISSA_MULT =
1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT);
@@ -5621,6 +5619,4 @@
}
}
-#endif // HAVE_ANDROID_OS
-
} // namespace android
diff --git a/libs/androidfw/VelocityControl.cpp b/libs/androidfw/VelocityControl.cpp
deleted file mode 100644
index cde2b76..0000000
--- a/libs/androidfw/VelocityControl.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "VelocityControl"
-//#define LOG_NDEBUG 0
-
-// Log debug messages about acceleration.
-#define DEBUG_ACCELERATION 0
-
-#include <math.h>
-#include <limits.h>
-
-#include <androidfw/VelocityControl.h>
-#include <utils/BitSet.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-// --- VelocityControl ---
-
-const nsecs_t VelocityControl::STOP_TIME;
-
-VelocityControl::VelocityControl() {
- reset();
-}
-
-void VelocityControl::setParameters(const VelocityControlParameters& parameters) {
- mParameters = parameters;
- reset();
-}
-
-void VelocityControl::reset() {
- mLastMovementTime = LLONG_MIN;
- mRawPosition.x = 0;
- mRawPosition.y = 0;
- mVelocityTracker.clear();
-}
-
-void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) {
- if ((deltaX && *deltaX) || (deltaY && *deltaY)) {
- if (eventTime >= mLastMovementTime + STOP_TIME) {
-#if DEBUG_ACCELERATION
- ALOGD("VelocityControl: stopped, last movement was %0.3fms ago",
- (eventTime - mLastMovementTime) * 0.000001f);
-#endif
- reset();
- }
-
- mLastMovementTime = eventTime;
- if (deltaX) {
- mRawPosition.x += *deltaX;
- }
- if (deltaY) {
- mRawPosition.y += *deltaY;
- }
- mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);
-
- float vx, vy;
- float scale = mParameters.scale;
- if (mVelocityTracker.getVelocity(0, &vx, &vy)) {
- float speed = hypotf(vx, vy) * scale;
- if (speed >= mParameters.highThreshold) {
- // Apply full acceleration above the high speed threshold.
- scale *= mParameters.acceleration;
- } else if (speed > mParameters.lowThreshold) {
- // Linearly interpolate the acceleration to apply between the low and high
- // speed thresholds.
- scale *= 1 + (speed - mParameters.lowThreshold)
- / (mParameters.highThreshold - mParameters.lowThreshold)
- * (mParameters.acceleration - 1);
- }
-
-#if DEBUG_ACCELERATION
- ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
- "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
- mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
- mParameters.acceleration,
- vx, vy, speed, scale / mParameters.scale);
-#endif
- } else {
-#if DEBUG_ACCELERATION
- ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity",
- mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
- mParameters.acceleration);
-#endif
- }
-
- if (deltaX) {
- *deltaX *= scale;
- }
- if (deltaY) {
- *deltaY *= scale;
- }
- }
-}
-
-} // namespace android
diff --git a/libs/androidfw/VelocityTracker.cpp b/libs/androidfw/VelocityTracker.cpp
deleted file mode 100644
index f48ec62..0000000
--- a/libs/androidfw/VelocityTracker.cpp
+++ /dev/null
@@ -1,928 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "VelocityTracker"
-//#define LOG_NDEBUG 0
-
-// Log debug messages about velocity tracking.
-#define DEBUG_VELOCITY 0
-
-// Log debug messages about the progress of the algorithm itself.
-#define DEBUG_STRATEGY 0
-
-#include <math.h>
-#include <limits.h>
-
-#include <androidfw/VelocityTracker.h>
-#include <utils/BitSet.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-
-#include <cutils/properties.h>
-
-namespace android {
-
-// Nanoseconds per milliseconds.
-static const nsecs_t NANOS_PER_MS = 1000000;
-
-// Threshold for determining that a pointer has stopped moving.
-// Some input devices do not send ACTION_MOVE events in the case where a pointer has
-// stopped. We need to detect this case so that we can accurately predict the
-// velocity after the pointer starts moving again.
-static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS;
-
-
-static float vectorDot(const float* a, const float* b, uint32_t m) {
- float r = 0;
- while (m--) {
- r += *(a++) * *(b++);
- }
- return r;
-}
-
-static float vectorNorm(const float* a, uint32_t m) {
- float r = 0;
- while (m--) {
- float t = *(a++);
- r += t * t;
- }
- return sqrtf(r);
-}
-
-#if DEBUG_STRATEGY || DEBUG_VELOCITY
-static String8 vectorToString(const float* a, uint32_t m) {
- String8 str;
- str.append("[");
- while (m--) {
- str.appendFormat(" %f", *(a++));
- if (m) {
- str.append(",");
- }
- }
- str.append(" ]");
- return str;
-}
-
-static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
- String8 str;
- str.append("[");
- for (size_t i = 0; i < m; i++) {
- if (i) {
- str.append(",");
- }
- str.append(" [");
- for (size_t j = 0; j < n; j++) {
- if (j) {
- str.append(",");
- }
- str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]);
- }
- str.append(" ]");
- }
- str.append(" ]");
- return str;
-}
-#endif
-
-
-// --- VelocityTracker ---
-
-// The default velocity tracker strategy.
-// Although other strategies are available for testing and comparison purposes,
-// this is the strategy that applications will actually use. Be very careful
-// when adjusting the default strategy because it can dramatically affect
-// (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
-
-VelocityTracker::VelocityTracker(const char* strategy) :
- mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
- char value[PROPERTY_VALUE_MAX];
-
- // Allow the default strategy to be overridden using a system property for debugging.
- if (!strategy) {
- int length = property_get("debug.velocitytracker.strategy", value, NULL);
- if (length > 0) {
- strategy = value;
- } else {
- strategy = DEFAULT_STRATEGY;
- }
- }
-
- // Configure the strategy.
- if (!configureStrategy(strategy)) {
- ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
- if (!configureStrategy(DEFAULT_STRATEGY)) {
- LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
- strategy);
- }
- }
-}
-
-VelocityTracker::~VelocityTracker() {
- delete mStrategy;
-}
-
-bool VelocityTracker::configureStrategy(const char* strategy) {
- mStrategy = createStrategy(strategy);
- return mStrategy != NULL;
-}
-
-VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
- if (!strcmp("lsq1", strategy)) {
- // 1st order least squares. Quality: POOR.
- // Frequently underfits the touch data especially when the finger accelerates
- // or changes direction. Often underestimates velocity. The direction
- // is overly influenced by historical touch points.
- return new LeastSquaresVelocityTrackerStrategy(1);
- }
- if (!strcmp("lsq2", strategy)) {
- // 2nd order least squares. Quality: VERY GOOD.
- // Pretty much ideal, but can be confused by certain kinds of touch data,
- // particularly if the panel has a tendency to generate delayed,
- // duplicate or jittery touch coordinates when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(2);
- }
- if (!strcmp("lsq3", strategy)) {
- // 3rd order least squares. Quality: UNUSABLE.
- // Frequently overfits the touch data yielding wildly divergent estimates
- // of the velocity when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(3);
- }
- if (!strcmp("wlsq2-delta", strategy)) {
- // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
- }
- if (!strcmp("wlsq2-central", strategy)) {
- // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
- }
- if (!strcmp("wlsq2-recent", strategy)) {
- // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
- }
- if (!strcmp("int1", strategy)) {
- // 1st order integrating filter. Quality: GOOD.
- // Not as good as 'lsq2' because it cannot estimate acceleration but it is
- // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
- // the velocity of a fling but this strategy tends to respond to changes in
- // direction more quickly and accurately.
- return new IntegratingVelocityTrackerStrategy(1);
- }
- if (!strcmp("int2", strategy)) {
- // 2nd order integrating filter. Quality: EXPERIMENTAL.
- // For comparison purposes only. Unlike 'int1' this strategy can compensate
- // for acceleration but it typically overestimates the effect.
- return new IntegratingVelocityTrackerStrategy(2);
- }
- if (!strcmp("legacy", strategy)) {
- // Legacy velocity tracker algorithm. Quality: POOR.
- // For comparison purposes only. This algorithm is strongly influenced by
- // old data points, consistently underestimates velocity and takes a very long
- // time to adjust to changes in direction.
- return new LegacyVelocityTrackerStrategy();
- }
- return NULL;
-}
-
-void VelocityTracker::clear() {
- mCurrentPointerIdBits.clear();
- mActivePointerId = -1;
-
- mStrategy->clear();
-}
-
-void VelocityTracker::clearPointers(BitSet32 idBits) {
- BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value);
- mCurrentPointerIdBits = remainingIdBits;
-
- if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) {
- mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
- }
-
- mStrategy->clearPointers(idBits);
-}
-
-void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
- while (idBits.count() > MAX_POINTERS) {
- idBits.clearLastMarkedBit();
- }
-
- if ((mCurrentPointerIdBits.value & idBits.value)
- && eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
-#if DEBUG_VELOCITY
- ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
- (eventTime - mLastEventTime) * 0.000001f);
-#endif
- // We have not received any movements for too long. Assume that all pointers
- // have stopped.
- mStrategy->clear();
- }
- mLastEventTime = eventTime;
-
- mCurrentPointerIdBits = idBits;
- if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) {
- mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit();
- }
-
- mStrategy->addMovement(eventTime, idBits, positions);
-
-#if DEBUG_VELOCITY
- ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d",
- eventTime, idBits.value, mActivePointerId);
- for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
- uint32_t id = iterBits.firstMarkedBit();
- uint32_t index = idBits.getIndexOfBit(id);
- iterBits.clearBit(id);
- Estimator estimator;
- getEstimator(id, &estimator);
- ALOGD(" %d: position (%0.3f, %0.3f), "
- "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
- id, positions[index].x, positions[index].y,
- int(estimator.degree),
- vectorToString(estimator.xCoeff, estimator.degree + 1).string(),
- vectorToString(estimator.yCoeff, estimator.degree + 1).string(),
- estimator.confidence);
- }
-#endif
-}
-
-void VelocityTracker::addMovement(const MotionEvent* event) {
- int32_t actionMasked = event->getActionMasked();
-
- switch (actionMasked) {
- case AMOTION_EVENT_ACTION_DOWN:
- case AMOTION_EVENT_ACTION_HOVER_ENTER:
- // Clear all pointers on down before adding the new movement.
- clear();
- break;
- case AMOTION_EVENT_ACTION_POINTER_DOWN: {
- // Start a new movement trace for a pointer that just went down.
- // We do this on down instead of on up because the client may want to query the
- // final velocity for a pointer that just went up.
- BitSet32 downIdBits;
- downIdBits.markBit(event->getPointerId(event->getActionIndex()));
- clearPointers(downIdBits);
- break;
- }
- case AMOTION_EVENT_ACTION_MOVE:
- case AMOTION_EVENT_ACTION_HOVER_MOVE:
- break;
- default:
- // Ignore all other actions because they do not convey any new information about
- // pointer movement. We also want to preserve the last known velocity of the pointers.
- // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
- // of the pointers that went up. ACTION_POINTER_UP does include the new position of
- // pointers that remained down but we will also receive an ACTION_MOVE with this
- // information if any of them actually moved. Since we don't know how many pointers
- // will be going up at once it makes sense to just wait for the following ACTION_MOVE
- // before adding the movement.
- return;
- }
-
- size_t pointerCount = event->getPointerCount();
- if (pointerCount > MAX_POINTERS) {
- pointerCount = MAX_POINTERS;
- }
-
- BitSet32 idBits;
- for (size_t i = 0; i < pointerCount; i++) {
- idBits.markBit(event->getPointerId(i));
- }
-
- uint32_t pointerIndex[MAX_POINTERS];
- for (size_t i = 0; i < pointerCount; i++) {
- pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
- }
-
- nsecs_t eventTime;
- Position positions[pointerCount];
-
- size_t historySize = event->getHistorySize();
- for (size_t h = 0; h < historySize; h++) {
- eventTime = event->getHistoricalEventTime(h);
- for (size_t i = 0; i < pointerCount; i++) {
- uint32_t index = pointerIndex[i];
- positions[index].x = event->getHistoricalX(i, h);
- positions[index].y = event->getHistoricalY(i, h);
- }
- addMovement(eventTime, idBits, positions);
- }
-
- eventTime = event->getEventTime();
- for (size_t i = 0; i < pointerCount; i++) {
- uint32_t index = pointerIndex[i];
- positions[index].x = event->getX(i);
- positions[index].y = event->getY(i);
- }
- addMovement(eventTime, idBits, positions);
-}
-
-bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
- Estimator estimator;
- if (getEstimator(id, &estimator) && estimator.degree >= 1) {
- *outVx = estimator.xCoeff[1];
- *outVy = estimator.yCoeff[1];
- return true;
- }
- *outVx = 0;
- *outVy = 0;
- return false;
-}
-
-bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const {
- return mStrategy->getEstimator(id, outEstimator);
-}
-
-
-// --- LeastSquaresVelocityTrackerStrategy ---
-
-const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON;
-const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE;
-
-LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(
- uint32_t degree, Weighting weighting) :
- mDegree(degree), mWeighting(weighting) {
- clear();
-}
-
-LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {
-}
-
-void LeastSquaresVelocityTrackerStrategy::clear() {
- mIndex = 0;
- mMovements[0].idBits.clear();
-}
-
-void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
- BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
- mMovements[mIndex].idBits = remainingIdBits;
-}
-
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
- if (++mIndex == HISTORY_SIZE) {
- mIndex = 0;
- }
-
- Movement& movement = mMovements[mIndex];
- movement.eventTime = eventTime;
- movement.idBits = idBits;
- uint32_t count = idBits.count();
- for (uint32_t i = 0; i < count; i++) {
- movement.positions[i] = positions[i];
- }
-}
-
-/**
- * Solves a linear least squares problem to obtain a N degree polynomial that fits
- * the specified input data as nearly as possible.
- *
- * Returns true if a solution is found, false otherwise.
- *
- * The input consists of two vectors of data points X and Y with indices 0..m-1
- * along with a weight vector W of the same size.
- *
- * The output is a vector B with indices 0..n that describes a polynomial
- * that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1] X[i]
- * + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is minimized.
- *
- * Accordingly, the weight vector W should be initialized by the caller with the
- * reciprocal square root of the variance of the error in each input data point.
- * In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 / stddev(Y[i]).
- * The weights express the relative importance of each data point. If the weights are
- * all 1, then the data points are considered to be of equal importance when fitting
- * the polynomial. It is a good idea to choose weights that diminish the importance
- * of data points that may have higher than usual error margins.
- *
- * Errors among data points are assumed to be independent. W is represented here
- * as a vector although in the literature it is typically taken to be a diagonal matrix.
- *
- * That is to say, the function that generated the input data can be approximated
- * by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
- *
- * The coefficient of determination (R^2) is also returned to describe the goodness
- * of fit of the model for the given data. It is a value between 0 and 1, where 1
- * indicates perfect correspondence.
- *
- * This function first expands the X vector to a m by n matrix A such that
- * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then
- * multiplies it by w[i]./
- *
- * Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q
- * and an m by n upper triangular matrix R. Because R is upper triangular (lower
- * part is all zeroes), we can simplify the decomposition into an m by n matrix
- * Q1 and a n by n matrix R1 such that A = Q1 R1.
- *
- * Finally we solve the system of linear equations given by R1 B = (Qtranspose W Y)
- * to find B.
- *
- * For efficiency, we lay out A and Q column-wise in memory because we frequently
- * operate on the column vectors. Conversely, we lay out R row-wise.
- *
- * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
- * http://en.wikipedia.org/wiki/Gram-Schmidt
- */
-static bool solveLeastSquares(const float* x, const float* y,
- const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) {
-#if DEBUG_STRATEGY
- ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
- vectorToString(x, m).string(), vectorToString(y, m).string(),
- vectorToString(w, m).string());
-#endif
-
- // Expand the X vector to a matrix A, pre-multiplied by the weights.
- float a[n][m]; // column-major order
- for (uint32_t h = 0; h < m; h++) {
- a[0][h] = w[h];
- for (uint32_t i = 1; i < n; i++) {
- a[i][h] = a[i - 1][h] * x[h];
- }
- }
-#if DEBUG_STRATEGY
- ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string());
-#endif
-
- // Apply the Gram-Schmidt process to A to obtain its QR decomposition.
- float q[n][m]; // orthonormal basis, column-major order
- float r[n][n]; // upper triangular matrix, row-major order
- for (uint32_t j = 0; j < n; j++) {
- for (uint32_t h = 0; h < m; h++) {
- q[j][h] = a[j][h];
- }
- for (uint32_t i = 0; i < j; i++) {
- float dot = vectorDot(&q[j][0], &q[i][0], m);
- for (uint32_t h = 0; h < m; h++) {
- q[j][h] -= dot * q[i][h];
- }
- }
-
- float norm = vectorNorm(&q[j][0], m);
- if (norm < 0.000001f) {
- // vectors are linearly dependent or zero so no solution
-#if DEBUG_STRATEGY
- ALOGD(" - no solution, norm=%f", norm);
-#endif
- return false;
- }
-
- float invNorm = 1.0f / norm;
- for (uint32_t h = 0; h < m; h++) {
- q[j][h] *= invNorm;
- }
- for (uint32_t i = 0; i < n; i++) {
- r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
- }
- }
-#if DEBUG_STRATEGY
- ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string());
- ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string());
-
- // calculate QR, if we factored A correctly then QR should equal A
- float qr[n][m];
- for (uint32_t h = 0; h < m; h++) {
- for (uint32_t i = 0; i < n; i++) {
- qr[i][h] = 0;
- for (uint32_t j = 0; j < n; j++) {
- qr[i][h] += q[j][h] * r[j][i];
- }
- }
- }
- ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string());
-#endif
-
- // Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
- // We just work from bottom-right to top-left calculating B's coefficients.
- float wy[m];
- for (uint32_t h = 0; h < m; h++) {
- wy[h] = y[h] * w[h];
- }
- for (uint32_t i = n; i-- != 0; ) {
- outB[i] = vectorDot(&q[i][0], wy, m);
- for (uint32_t j = n - 1; j > i; j--) {
- outB[i] -= r[i][j] * outB[j];
- }
- outB[i] /= r[i][i];
- }
-#if DEBUG_STRATEGY
- ALOGD(" - b=%s", vectorToString(outB, n).string());
-#endif
-
- // Calculate the coefficient of determination as 1 - (SSerr / SStot) where
- // SSerr is the residual sum of squares (variance of the error),
- // and SStot is the total sum of squares (variance of the data) where each
- // has been weighted.
- float ymean = 0;
- for (uint32_t h = 0; h < m; h++) {
- ymean += y[h];
- }
- ymean /= m;
-
- float sserr = 0;
- float sstot = 0;
- for (uint32_t h = 0; h < m; h++) {
- float err = y[h] - outB[0];
- float term = 1;
- for (uint32_t i = 1; i < n; i++) {
- term *= x[h];
- err -= term * outB[i];
- }
- sserr += w[h] * w[h] * err * err;
- float var = y[h] - ymean;
- sstot += w[h] * w[h] * var * var;
- }
- *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
-#if DEBUG_STRATEGY
- ALOGD(" - sserr=%f", sserr);
- ALOGD(" - sstot=%f", sstot);
- ALOGD(" - det=%f", *outDet);
-#endif
- return true;
-}
-
-bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
- VelocityTracker::Estimator* outEstimator) const {
- outEstimator->clear();
-
- // Iterate over movement samples in reverse time order and collect samples.
- float x[HISTORY_SIZE];
- float y[HISTORY_SIZE];
- float w[HISTORY_SIZE];
- float time[HISTORY_SIZE];
- uint32_t m = 0;
- uint32_t index = mIndex;
- const Movement& newestMovement = mMovements[mIndex];
- do {
- const Movement& movement = mMovements[index];
- if (!movement.idBits.hasBit(id)) {
- break;
- }
-
- nsecs_t age = newestMovement.eventTime - movement.eventTime;
- if (age > HORIZON) {
- break;
- }
-
- const VelocityTracker::Position& position = movement.getPosition(id);
- x[m] = position.x;
- y[m] = position.y;
- w[m] = chooseWeight(index);
- time[m] = -age * 0.000000001f;
- index = (index == 0 ? HISTORY_SIZE : index) - 1;
- } while (++m < HISTORY_SIZE);
-
- if (m == 0) {
- return false; // no data
- }
-
- // Calculate a least squares polynomial fit.
- uint32_t degree = mDegree;
- if (degree > m - 1) {
- degree = m - 1;
- }
- if (degree >= 1) {
- float xdet, ydet;
- uint32_t n = degree + 1;
- if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
- && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) {
- outEstimator->time = newestMovement.eventTime;
- outEstimator->degree = degree;
- outEstimator->confidence = xdet * ydet;
-#if DEBUG_STRATEGY
- ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
- int(outEstimator->degree),
- vectorToString(outEstimator->xCoeff, n).string(),
- vectorToString(outEstimator->yCoeff, n).string(),
- outEstimator->confidence);
-#endif
- return true;
- }
- }
-
- // No velocity data available for this pointer, but we do have its current position.
- outEstimator->xCoeff[0] = x[0];
- outEstimator->yCoeff[0] = y[0];
- outEstimator->time = newestMovement.eventTime;
- outEstimator->degree = 0;
- outEstimator->confidence = 1;
- return true;
-}
-
-float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const {
- switch (mWeighting) {
- case WEIGHTING_DELTA: {
- // Weight points based on how much time elapsed between them and the next
- // point so that points that "cover" a shorter time span are weighed less.
- // delta 0ms: 0.5
- // delta 10ms: 1.0
- if (index == mIndex) {
- return 1.0f;
- }
- uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
- float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime)
- * 0.000001f;
- if (deltaMillis < 0) {
- return 0.5f;
- }
- if (deltaMillis < 10) {
- return 0.5f + deltaMillis * 0.05;
- }
- return 1.0f;
- }
-
- case WEIGHTING_CENTRAL: {
- // Weight points based on their age, weighing very recent and very old points less.
- // age 0ms: 0.5
- // age 10ms: 1.0
- // age 50ms: 1.0
- // age 60ms: 0.5
- float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
- * 0.000001f;
- if (ageMillis < 0) {
- return 0.5f;
- }
- if (ageMillis < 10) {
- return 0.5f + ageMillis * 0.05;
- }
- if (ageMillis < 50) {
- return 1.0f;
- }
- if (ageMillis < 60) {
- return 0.5f + (60 - ageMillis) * 0.05;
- }
- return 0.5f;
- }
-
- case WEIGHTING_RECENT: {
- // Weight points based on their age, weighing older points less.
- // age 0ms: 1.0
- // age 50ms: 1.0
- // age 100ms: 0.5
- float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
- * 0.000001f;
- if (ageMillis < 50) {
- return 1.0f;
- }
- if (ageMillis < 100) {
- return 0.5f + (100 - ageMillis) * 0.01f;
- }
- return 0.5f;
- }
-
- case WEIGHTING_NONE:
- default:
- return 1.0f;
- }
-}
-
-
-// --- IntegratingVelocityTrackerStrategy ---
-
-IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) :
- mDegree(degree) {
-}
-
-IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() {
-}
-
-void IntegratingVelocityTrackerStrategy::clear() {
- mPointerIdBits.clear();
-}
-
-void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
- mPointerIdBits.value &= ~idBits.value;
-}
-
-void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
- uint32_t index = 0;
- for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
- uint32_t id = iterIdBits.clearFirstMarkedBit();
- State& state = mPointerState[id];
- const VelocityTracker::Position& position = positions[index++];
- if (mPointerIdBits.hasBit(id)) {
- updateState(state, eventTime, position.x, position.y);
- } else {
- initState(state, eventTime, position.x, position.y);
- }
- }
-
- mPointerIdBits = idBits;
-}
-
-bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id,
- VelocityTracker::Estimator* outEstimator) const {
- outEstimator->clear();
-
- if (mPointerIdBits.hasBit(id)) {
- const State& state = mPointerState[id];
- populateEstimator(state, outEstimator);
- return true;
- }
-
- return false;
-}
-
-void IntegratingVelocityTrackerStrategy::initState(State& state,
- nsecs_t eventTime, float xpos, float ypos) const {
- state.updateTime = eventTime;
- state.degree = 0;
-
- state.xpos = xpos;
- state.xvel = 0;
- state.xaccel = 0;
- state.ypos = ypos;
- state.yvel = 0;
- state.yaccel = 0;
-}
-
-void IntegratingVelocityTrackerStrategy::updateState(State& state,
- nsecs_t eventTime, float xpos, float ypos) const {
- const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS;
- const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds
-
- if (eventTime <= state.updateTime + MIN_TIME_DELTA) {
- return;
- }
-
- float dt = (eventTime - state.updateTime) * 0.000000001f;
- state.updateTime = eventTime;
-
- float xvel = (xpos - state.xpos) / dt;
- float yvel = (ypos - state.ypos) / dt;
- if (state.degree == 0) {
- state.xvel = xvel;
- state.yvel = yvel;
- state.degree = 1;
- } else {
- float alpha = dt / (FILTER_TIME_CONSTANT + dt);
- if (mDegree == 1) {
- state.xvel += (xvel - state.xvel) * alpha;
- state.yvel += (yvel - state.yvel) * alpha;
- } else {
- float xaccel = (xvel - state.xvel) / dt;
- float yaccel = (yvel - state.yvel) / dt;
- if (state.degree == 1) {
- state.xaccel = xaccel;
- state.yaccel = yaccel;
- state.degree = 2;
- } else {
- state.xaccel += (xaccel - state.xaccel) * alpha;
- state.yaccel += (yaccel - state.yaccel) * alpha;
- }
- state.xvel += (state.xaccel * dt) * alpha;
- state.yvel += (state.yaccel * dt) * alpha;
- }
- }
- state.xpos = xpos;
- state.ypos = ypos;
-}
-
-void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
- VelocityTracker::Estimator* outEstimator) const {
- outEstimator->time = state.updateTime;
- outEstimator->confidence = 1.0f;
- outEstimator->degree = state.degree;
- outEstimator->xCoeff[0] = state.xpos;
- outEstimator->xCoeff[1] = state.xvel;
- outEstimator->xCoeff[2] = state.xaccel / 2;
- outEstimator->yCoeff[0] = state.ypos;
- outEstimator->yCoeff[1] = state.yvel;
- outEstimator->yCoeff[2] = state.yaccel / 2;
-}
-
-
-// --- LegacyVelocityTrackerStrategy ---
-
-const nsecs_t LegacyVelocityTrackerStrategy::HORIZON;
-const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE;
-const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION;
-
-LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {
- clear();
-}
-
-LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
-}
-
-void LegacyVelocityTrackerStrategy::clear() {
- mIndex = 0;
- mMovements[0].idBits.clear();
-}
-
-void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
- BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
- mMovements[mIndex].idBits = remainingIdBits;
-}
-
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
- if (++mIndex == HISTORY_SIZE) {
- mIndex = 0;
- }
-
- Movement& movement = mMovements[mIndex];
- movement.eventTime = eventTime;
- movement.idBits = idBits;
- uint32_t count = idBits.count();
- for (uint32_t i = 0; i < count; i++) {
- movement.positions[i] = positions[i];
- }
-}
-
-bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id,
- VelocityTracker::Estimator* outEstimator) const {
- outEstimator->clear();
-
- const Movement& newestMovement = mMovements[mIndex];
- if (!newestMovement.idBits.hasBit(id)) {
- return false; // no data
- }
-
- // Find the oldest sample that contains the pointer and that is not older than HORIZON.
- nsecs_t minTime = newestMovement.eventTime - HORIZON;
- uint32_t oldestIndex = mIndex;
- uint32_t numTouches = 1;
- do {
- uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
- const Movement& nextOldestMovement = mMovements[nextOldestIndex];
- if (!nextOldestMovement.idBits.hasBit(id)
- || nextOldestMovement.eventTime < minTime) {
- break;
- }
- oldestIndex = nextOldestIndex;
- } while (++numTouches < HISTORY_SIZE);
-
- // Calculate an exponentially weighted moving average of the velocity estimate
- // at different points in time measured relative to the oldest sample.
- // This is essentially an IIR filter. Newer samples are weighted more heavily
- // than older samples. Samples at equal time points are weighted more or less
- // equally.
- //
- // One tricky problem is that the sample data may be poorly conditioned.
- // Sometimes samples arrive very close together in time which can cause us to
- // overestimate the velocity at that time point. Most samples might be measured
- // 16ms apart but some consecutive samples could be only 0.5sm apart because
- // the hardware or driver reports them irregularly or in bursts.
- float accumVx = 0;
- float accumVy = 0;
- uint32_t index = oldestIndex;
- uint32_t samplesUsed = 0;
- const Movement& oldestMovement = mMovements[oldestIndex];
- const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id);
- nsecs_t lastDuration = 0;
-
- while (numTouches-- > 1) {
- if (++index == HISTORY_SIZE) {
- index = 0;
- }
- const Movement& movement = mMovements[index];
- nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
-
- // If the duration between samples is small, we may significantly overestimate
- // the velocity. Consequently, we impose a minimum duration constraint on the
- // samples that we include in the calculation.
- if (duration >= MIN_DURATION) {
- const VelocityTracker::Position& position = movement.getPosition(id);
- float scale = 1000000000.0f / duration; // one over time delta in seconds
- float vx = (position.x - oldestPosition.x) * scale;
- float vy = (position.y - oldestPosition.y) * scale;
- accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
- accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
- lastDuration = duration;
- samplesUsed += 1;
- }
- }
-
- // Report velocity.
- const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id);
- outEstimator->time = newestMovement.eventTime;
- outEstimator->confidence = 1;
- outEstimator->xCoeff[0] = newestPosition.x;
- outEstimator->yCoeff[0] = newestPosition.y;
- if (samplesUsed) {
- outEstimator->xCoeff[1] = accumVx;
- outEstimator->yCoeff[1] = accumVy;
- outEstimator->degree = 1;
- } else {
- outEstimator->degree = 0;
- }
- return true;
-}
-
-} // namespace android
diff --git a/libs/androidfw/VirtualKeyMap.cpp b/libs/androidfw/VirtualKeyMap.cpp
deleted file mode 100644
index 2ba1673..0000000
--- a/libs/androidfw/VirtualKeyMap.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "VirtualKeyMap"
-
-#include <stdlib.h>
-#include <string.h>
-#include <androidfw/VirtualKeyMap.h>
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/Tokenizer.h>
-#include <utils/Timers.h>
-
-// Enables debug output for the parser.
-#define DEBUG_PARSER 0
-
-// Enables debug output for parser performance.
-#define DEBUG_PARSER_PERFORMANCE 0
-
-
-namespace android {
-
-static const char* WHITESPACE = " \t\r";
-static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:";
-
-
-// --- VirtualKeyMap ---
-
-VirtualKeyMap::VirtualKeyMap() {
-}
-
-VirtualKeyMap::~VirtualKeyMap() {
-}
-
-status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) {
- *outMap = NULL;
-
- Tokenizer* tokenizer;
- status_t status = Tokenizer::open(filename, &tokenizer);
- if (status) {
- ALOGE("Error %d opening virtual key map file %s.", status, filename.string());
- } else {
- VirtualKeyMap* map = new VirtualKeyMap();
- if (!map) {
- ALOGE("Error allocating virtual key map.");
- status = NO_MEMORY;
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(map, tokenizer);
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (status) {
- delete map;
- } else {
- *outMap = map;
- }
- }
- delete tokenizer;
- }
- return status;
-}
-
-
-// --- VirtualKeyMap::Parser ---
-
-VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) :
- mMap(map), mTokenizer(tokenizer) {
-}
-
-VirtualKeyMap::Parser::~Parser() {
-}
-
-status_t VirtualKeyMap::Parser::parse() {
- while (!mTokenizer->isEof()) {
-#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
-#endif
-
- mTokenizer->skipDelimiters(WHITESPACE);
-
- if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
- // Multiple keys can appear on one line or they can be broken up across multiple lines.
- do {
- String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
- if (token != "0x01") {
- ALOGE("%s: Unknown virtual key type, expected 0x01.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
-
- VirtualKeyDefinition defn;
- bool success = parseNextIntField(&defn.scanCode)
- && parseNextIntField(&defn.centerX)
- && parseNextIntField(&defn.centerY)
- && parseNextIntField(&defn.width)
- && parseNextIntField(&defn.height);
- if (!success) {
- ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
-
-#if DEBUG_PARSER
- ALOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, "
- "width=%d, height=%d",
- defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height);
-#endif
- mMap->mVirtualKeys.push(defn);
- } while (consumeFieldDelimiterAndSkipWhitespace());
-
- if (!mTokenizer->isEol()) {
- ALOGE("%s: Expected end of line, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
- return BAD_VALUE;
- }
- }
-
- mTokenizer->nextLine();
- }
-
- return NO_ERROR;
-}
-
-bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() {
- mTokenizer->skipDelimiters(WHITESPACE);
- if (mTokenizer->peekChar() == ':') {
- mTokenizer->nextChar();
- mTokenizer->skipDelimiters(WHITESPACE);
- return true;
- }
- return false;
-}
-
-bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) {
- if (!consumeFieldDelimiterAndSkipWhitespace()) {
- return false;
- }
-
- String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
- char* end;
- *outValue = strtol(token.string(), &end, 0);
- if (token.isEmpty() || *end != '\0') {
- ALOGE("Expected an integer, got '%s'.", token.string());
- return false;
- }
- return true;
-}
-
-} // namespace android
diff --git a/libs/androidfw/ZipFileCRO.cpp b/libs/androidfw/ZipFileCRO.cpp
new file mode 100644
index 0000000..c8df845
--- /dev/null
+++ b/libs/androidfw/ZipFileCRO.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/ZipFileCRO.h>
+#include <androidfw/ZipFileRO.h>
+
+using namespace android;
+
+ZipFileCRO ZipFileXRO_open(const char* path) {
+ ZipFileRO* zip = new ZipFileRO();
+ if (zip->open(path) == NO_ERROR) {
+ return (ZipFileCRO)zip;
+ }
+ return NULL;
+}
+
+void ZipFileCRO_destroy(ZipFileCRO zipToken) {
+ ZipFileRO* zip = (ZipFileRO*)zipToken;
+ delete zip;
+}
+
+ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken,
+ const char* fileName) {
+ ZipFileRO* zip = (ZipFileRO*)zipToken;
+ return (ZipEntryCRO)zip->findEntryByName(fileName);
+}
+
+bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken,
+ int* pMethod, size_t* pUncompLen,
+ size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) {
+ ZipFileRO* zip = (ZipFileRO*)zipToken;
+ ZipEntryRO entry = (ZipEntryRO)entryToken;
+ return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset,
+ pModWhen, pCrc32);
+}
+
+bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) {
+ ZipFileRO* zip = (ZipFileRO*)zipToken;
+ ZipEntryRO entry = (ZipEntryRO)entryToken;
+ return zip->uncompressEntry(entry, fd);
+}
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
new file mode 100644
index 0000000..b84f3b0
--- /dev/null
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -0,0 +1,931 @@
+/*
+ * 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.
+ */
+
+//
+// Read-only access to Zip archives, with minimal heap allocation.
+//
+#define LOG_TAG "zipro"
+//#define LOG_NDEBUG 0
+#include <androidfw/ZipFileRO.h>
+#include <utils/Log.h>
+#include <utils/Compat.h>
+#include <utils/misc.h>
+#include <utils/threads.h>
+
+#include <zlib.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+
+/*
+ * We must open binary files using open(path, ... | O_BINARY) under Windows.
+ * Otherwise strange read errors will happen.
+ */
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+using namespace android;
+
+/*
+ * Zip file constants.
+ */
+#define kEOCDSignature 0x06054b50
+#define kEOCDLen 22
+#define kEOCDNumEntries 8 // offset to #of entries in file
+#define kEOCDSize 12 // size of the central directory
+#define kEOCDFileOffset 16 // offset to central directory
+
+#define kMaxCommentLen 65535 // longest possible in ushort
+#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen)
+
+#define kLFHSignature 0x04034b50
+#define kLFHLen 30 // excluding variable-len fields
+#define kLFHNameLen 26 // offset to filename length
+#define kLFHExtraLen 28 // offset to extra length
+
+#define kCDESignature 0x02014b50
+#define kCDELen 46 // excluding variable-len fields
+#define kCDEMethod 10 // offset to compression method
+#define kCDEModWhen 12 // offset to modification timestamp
+#define kCDECRC 16 // offset to entry CRC
+#define kCDECompLen 20 // offset to compressed length
+#define kCDEUncompLen 24 // offset to uncompressed length
+#define kCDENameLen 28 // offset to filename length
+#define kCDEExtraLen 30 // offset to extra length
+#define kCDECommentLen 32 // offset to comment length
+#define kCDELocalOffset 42 // offset to local hdr
+
+/*
+ * The values we return for ZipEntryRO use 0 as an invalid value, so we
+ * want to adjust the hash table index by a fixed amount. Using a large
+ * value helps insure that people don't mix & match arguments, e.g. to
+ * findEntryByIndex().
+ */
+#define kZipEntryAdj 10000
+
+ZipFileRO::~ZipFileRO() {
+ free(mHashTable);
+ if (mDirectoryMap)
+ mDirectoryMap->release();
+ if (mFd >= 0)
+ TEMP_FAILURE_RETRY(close(mFd));
+ if (mFileName)
+ free(mFileName);
+}
+
+/*
+ * Convert a ZipEntryRO to a hash table index, verifying that it's in a
+ * valid range.
+ */
+int ZipFileRO::entryToIndex(const ZipEntryRO entry) const
+{
+ long ent = ((intptr_t) entry) - kZipEntryAdj;
+ if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) {
+ ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent);
+ return -1;
+ }
+ return ent;
+}
+
+
+/*
+ * Open the specified file read-only. We memory-map the entire thing and
+ * close the file before returning.
+ */
+status_t ZipFileRO::open(const char* zipFileName)
+{
+ int fd = -1;
+
+ assert(mDirectoryMap == NULL);
+
+ /*
+ * Open and map the specified file.
+ */
+ fd = TEMP_FAILURE_RETRY(::open(zipFileName, O_RDONLY | O_BINARY));
+ if (fd < 0) {
+ ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno));
+ return NAME_NOT_FOUND;
+ }
+
+ mFileLength = lseek64(fd, 0, SEEK_END);
+ if (mFileLength < kEOCDLen) {
+ TEMP_FAILURE_RETRY(close(fd));
+ return UNKNOWN_ERROR;
+ }
+
+ if (mFileName != NULL) {
+ free(mFileName);
+ }
+ mFileName = strdup(zipFileName);
+
+ mFd = fd;
+
+ /*
+ * Find the Central Directory and store its size and number of entries.
+ */
+ if (!mapCentralDirectory()) {
+ goto bail;
+ }
+
+ /*
+ * Verify Central Directory and create data structures for fast access.
+ */
+ if (!parseZipArchive()) {
+ goto bail;
+ }
+
+ return OK;
+
+bail:
+ free(mFileName);
+ mFileName = NULL;
+ TEMP_FAILURE_RETRY(close(fd));
+ return UNKNOWN_ERROR;
+}
+
+/*
+ * Parse the Zip archive, verifying its contents and initializing internal
+ * data structures.
+ */
+bool ZipFileRO::mapCentralDirectory(void)
+{
+ ssize_t readAmount = kMaxEOCDSearch;
+ if (readAmount > (ssize_t) mFileLength)
+ readAmount = mFileLength;
+
+ unsigned char* scanBuf = (unsigned char*) malloc(readAmount);
+ if (scanBuf == NULL) {
+ ALOGW("couldn't allocate scanBuf: %s", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ /*
+ * Make sure this is a Zip archive.
+ */
+ if (lseek64(mFd, 0, SEEK_SET) != 0) {
+ ALOGW("seek to start failed: %s", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t)));
+ if (actual != (ssize_t) sizeof(int32_t)) {
+ ALOGI("couldn't read first signature from zip archive: %s", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ {
+ unsigned int header = get4LE(scanBuf);
+ if (header == kEOCDSignature) {
+ ALOGI("Found Zip archive, but it looks empty\n");
+ free(scanBuf);
+ return false;
+ } else if (header != kLFHSignature) {
+ ALOGV("Not a Zip archive (found 0x%08x)\n", header);
+ free(scanBuf);
+ return false;
+ }
+ }
+
+ /*
+ * Perform the traditional EOCD snipe hunt.
+ *
+ * We're searching for the End of Central Directory magic number,
+ * which appears at the start of the EOCD block. It's followed by
+ * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
+ * need to read the last part of the file into a buffer, dig through
+ * it to find the magic number, parse some values out, and use those
+ * to determine the extent of the CD.
+ *
+ * We start by pulling in the last part of the file.
+ */
+ off64_t searchStart = mFileLength - readAmount;
+
+ if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) {
+ ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+ actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount));
+ if (actual != (ssize_t) readAmount) {
+ ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n",
+ (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ /*
+ * Scan backward for the EOCD magic. In an archive without a trailing
+ * comment, we'll find it on the first try. (We may want to consider
+ * doing an initial minimal read; if we don't find it, retry with a
+ * second read as above.)
+ */
+ int i;
+ for (i = readAmount - kEOCDLen; i >= 0; i--) {
+ if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
+ ALOGV("+++ Found EOCD at buf+%d\n", i);
+ break;
+ }
+ }
+ if (i < 0) {
+ ALOGD("Zip: EOCD not found, %s is not zip\n", mFileName);
+ free(scanBuf);
+ return false;
+ }
+
+ off64_t eocdOffset = searchStart + i;
+ const unsigned char* eocdPtr = scanBuf + i;
+
+ assert(eocdOffset < mFileLength);
+
+ /*
+ * Grab the CD offset and size, and the number of entries in the
+ * archive. After that, we can release our EOCD hunt buffer.
+ */
+ unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries);
+ unsigned int dirSize = get4LE(eocdPtr + kEOCDSize);
+ unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset);
+ free(scanBuf);
+
+ // Verify that they look reasonable.
+ if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) {
+ ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n",
+ (long) dirOffset, dirSize, (long) eocdOffset);
+ return false;
+ }
+ if (numEntries == 0) {
+ ALOGW("empty archive?\n");
+ return false;
+ }
+
+ ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
+ numEntries, dirSize, dirOffset);
+
+ mDirectoryMap = new FileMap();
+ if (mDirectoryMap == NULL) {
+ ALOGW("Unable to create directory map: %s", strerror(errno));
+ return false;
+ }
+
+ if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) {
+ ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName,
+ (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno));
+ return false;
+ }
+
+ mNumEntries = numEntries;
+ mDirectoryOffset = dirOffset;
+
+ return true;
+}
+
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+static unsigned int roundUpPower2(unsigned int val)
+{
+ val--;
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ val++;
+
+ return val;
+}
+
+bool ZipFileRO::parseZipArchive(void)
+{
+ bool result = false;
+ const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr();
+ size_t cdLength = mDirectoryMap->getDataLength();
+ int numEntries = mNumEntries;
+
+ /*
+ * Create hash table. We have a minimum 75% load factor, possibly as
+ * low as 50% after we round off to a power of 2.
+ */
+ mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3);
+ mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry));
+
+ /*
+ * Walk through the central directory, adding entries to the hash
+ * table.
+ */
+ const unsigned char* ptr = cdPtr;
+ for (int i = 0; i < numEntries; i++) {
+ if (get4LE(ptr) != kCDESignature) {
+ ALOGW("Missed a central dir sig (at %d)\n", i);
+ goto bail;
+ }
+ if (ptr + kCDELen > cdPtr + cdLength) {
+ ALOGW("Ran off the end (at %d)\n", i);
+ goto bail;
+ }
+
+ long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
+ if (localHdrOffset >= mDirectoryOffset) {
+ ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i);
+ goto bail;
+ }
+
+ unsigned int fileNameLen, extraLen, commentLen, hash;
+
+ fileNameLen = get2LE(ptr + kCDENameLen);
+ extraLen = get2LE(ptr + kCDEExtraLen);
+ commentLen = get2LE(ptr + kCDECommentLen);
+
+ /* add the CDE filename to the hash table */
+ hash = computeHash((const char*)ptr + kCDELen, fileNameLen);
+ addToHash((const char*)ptr + kCDELen, fileNameLen, hash);
+
+ ptr += kCDELen + fileNameLen + extraLen + commentLen;
+ if ((size_t)(ptr - cdPtr) > cdLength) {
+ ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n",
+ (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i);
+ goto bail;
+ }
+ }
+ ALOGV("+++ zip good scan %d entries\n", numEntries);
+ result = true;
+
+bail:
+ return result;
+}
+
+/*
+ * Simple string hash function for non-null-terminated strings.
+ */
+/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len)
+{
+ unsigned int hash = 0;
+
+ while (len--)
+ hash = hash * 31 + *str++;
+
+ return hash;
+}
+
+/*
+ * Add a new entry to the hash table.
+ */
+void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash)
+{
+ int ent = hash & (mHashTableSize-1);
+
+ /*
+ * We over-allocate the table, so we're guaranteed to find an empty slot.
+ */
+ while (mHashTable[ent].name != NULL)
+ ent = (ent + 1) & (mHashTableSize-1);
+
+ mHashTable[ent].name = str;
+ mHashTable[ent].nameLen = strLen;
+}
+
+/*
+ * Find a matching entry.
+ *
+ * Returns NULL if not found.
+ */
+ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const
+{
+ /*
+ * If the ZipFileRO instance is not initialized, the entry number will
+ * end up being garbage since mHashTableSize is -1.
+ */
+ if (mHashTableSize <= 0) {
+ return NULL;
+ }
+
+ int nameLen = strlen(fileName);
+ unsigned int hash = computeHash(fileName, nameLen);
+ int ent = hash & (mHashTableSize-1);
+
+ while (mHashTable[ent].name != NULL) {
+ if (mHashTable[ent].nameLen == nameLen &&
+ memcmp(mHashTable[ent].name, fileName, nameLen) == 0)
+ {
+ /* match */
+ return (ZipEntryRO)(long)(ent + kZipEntryAdj);
+ }
+
+ ent = (ent + 1) & (mHashTableSize-1);
+ }
+
+ return NULL;
+}
+
+/*
+ * Find the Nth entry.
+ *
+ * This currently involves walking through the sparse hash table, counting
+ * non-empty entries. If we need to speed this up we can either allocate
+ * a parallel lookup table or (perhaps better) provide an iterator interface.
+ */
+ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const
+{
+ if (idx < 0 || idx >= mNumEntries) {
+ ALOGW("Invalid index %d\n", idx);
+ return NULL;
+ }
+
+ for (int ent = 0; ent < mHashTableSize; ent++) {
+ if (mHashTable[ent].name != NULL) {
+ if (idx-- == 0)
+ return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Get the useful fields from the zip entry.
+ *
+ * Returns "false" if the offsets to the fields or the contents of the fields
+ * appear to be bogus.
+ */
+bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen,
+ size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const
+{
+ bool ret = false;
+
+ const int ent = entryToIndex(entry);
+ if (ent < 0)
+ return false;
+
+ HashEntry hashEntry = mHashTable[ent];
+
+ /*
+ * Recover the start of the central directory entry from the filename
+ * pointer. The filename is the first entry past the fixed-size data,
+ * so we can just subtract back from that.
+ */
+ const unsigned char* ptr = (const unsigned char*) hashEntry.name;
+ off64_t cdOffset = mDirectoryOffset;
+
+ ptr -= kCDELen;
+
+ int method = get2LE(ptr + kCDEMethod);
+ if (pMethod != NULL)
+ *pMethod = method;
+
+ if (pModWhen != NULL)
+ *pModWhen = get4LE(ptr + kCDEModWhen);
+ if (pCrc32 != NULL)
+ *pCrc32 = get4LE(ptr + kCDECRC);
+
+ size_t compLen = get4LE(ptr + kCDECompLen);
+ if (pCompLen != NULL)
+ *pCompLen = compLen;
+ size_t uncompLen = get4LE(ptr + kCDEUncompLen);
+ if (pUncompLen != NULL)
+ *pUncompLen = uncompLen;
+
+ /*
+ * If requested, determine the offset of the start of the data. All we
+ * have is the offset to the Local File Header, which is variable size,
+ * so we have to read the contents of the struct to figure out where
+ * the actual data starts.
+ *
+ * We also need to make sure that the lengths are not so large that
+ * somebody trying to map the compressed or uncompressed data runs
+ * off the end of the mapped region.
+ *
+ * Note we don't verify compLen/uncompLen if they don't request the
+ * dataOffset, because dataOffset is expensive to determine. However,
+ * if they don't have the file offset, they're not likely to be doing
+ * anything with the contents.
+ */
+ if (pOffset != NULL) {
+ long localHdrOffset = get4LE(ptr + kCDELocalOffset);
+ if (localHdrOffset + kLFHLen >= cdOffset) {
+ ALOGE("ERROR: bad local hdr offset in zip\n");
+ return false;
+ }
+
+ unsigned char lfhBuf[kLFHLen];
+
+#ifdef HAVE_PREAD
+ /*
+ * This file descriptor might be from zygote's preloaded assets,
+ * so we need to do an pread64() instead of a lseek64() + read() to
+ * guarantee atomicity across the processes with the shared file
+ * descriptors.
+ */
+ ssize_t actual =
+ TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset));
+
+ if (actual != sizeof(lfhBuf)) {
+ ALOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ if (get4LE(lfhBuf) != kLFHSignature) {
+ ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+ "got: data=0x%08lx\n",
+ localHdrOffset, kLFHSignature, get4LE(lfhBuf));
+ return false;
+ }
+#else /* HAVE_PREAD */
+ /*
+ * For hosts don't have pread64() we cannot guarantee atomic reads from
+ * an offset in a file. Android should never run on those platforms.
+ * File descriptors inherited from a fork() share file offsets and
+ * there would be nothing to protect from two different processes
+ * calling lseek64() concurrently.
+ */
+
+ {
+ AutoMutex _l(mFdLock);
+
+ if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+ ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ ssize_t actual =
+ TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
+ if (actual != sizeof(lfhBuf)) {
+ ALOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ if (get4LE(lfhBuf) != kLFHSignature) {
+ off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR);
+ ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+ "got: offset=" ZD " data=0x%08lx\n",
+ localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf));
+ return false;
+ }
+ }
+#endif /* HAVE_PREAD */
+
+ off64_t dataOffset = localHdrOffset + kLFHLen
+ + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
+ if (dataOffset >= cdOffset) {
+ ALOGW("bad data offset %ld in zip\n", (long) dataOffset);
+ return false;
+ }
+
+ /* check lengths */
+ if ((off64_t)(dataOffset + compLen) > cdOffset) {
+ ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n",
+ (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset);
+ return false;
+ }
+
+ if (method == kCompressStored &&
+ (off64_t)(dataOffset + uncompLen) > cdOffset)
+ {
+ ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n",
+ (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset);
+ return false;
+ }
+
+ *pOffset = dataOffset;
+ }
+
+ return true;
+}
+
+/*
+ * Copy the entry's filename to the buffer.
+ */
+int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen)
+ const
+{
+ int ent = entryToIndex(entry);
+ if (ent < 0)
+ return -1;
+
+ int nameLen = mHashTable[ent].nameLen;
+ if (bufLen < nameLen+1)
+ return nameLen+1;
+
+ memcpy(buffer, mHashTable[ent].name, nameLen);
+ buffer[nameLen] = '\0';
+ return 0;
+}
+
+/*
+ * Create a new FileMap object that spans the data in "entry".
+ */
+FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
+{
+ /*
+ * TODO: the efficient way to do this is to modify FileMap to allow
+ * sub-regions of a file to be mapped. A reference-counting scheme
+ * can manage the base memory mapping. For now, we just create a brand
+ * new mapping off of the Zip archive file descriptor.
+ */
+
+ FileMap* newMap;
+ size_t compLen;
+ off64_t offset;
+
+ if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL))
+ return NULL;
+
+ newMap = new FileMap();
+ if (!newMap->create(mFileName, mFd, offset, compLen, true)) {
+ newMap->release();
+ return NULL;
+ }
+
+ return newMap;
+}
+
+/*
+ * Uncompress an entry, in its entirety, into the provided output buffer.
+ *
+ * This doesn't verify the data's CRC, which might be useful for
+ * uncompressed data. The caller should be able to manage it.
+ */
+bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const
+{
+ const size_t kSequentialMin = 32768;
+ bool result = false;
+ int ent = entryToIndex(entry);
+ if (ent < 0)
+ return -1;
+
+ int method;
+ size_t uncompLen, compLen;
+ off64_t offset;
+ const unsigned char* ptr;
+
+ getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
+
+ FileMap* file = createEntryFileMap(entry);
+ if (file == NULL) {
+ goto bail;
+ }
+
+ ptr = (const unsigned char*) file->getDataPtr();
+
+ /*
+ * Experiment with madvise hint. When we want to uncompress a file,
+ * we pull some stuff out of the central dir entry and then hit a
+ * bunch of compressed or uncompressed data sequentially. The CDE
+ * visit will cause a limited amount of read-ahead because it's at
+ * the end of the file. We could end up doing lots of extra disk
+ * access if the file we're prying open is small. Bottom line is we
+ * probably don't want to turn MADV_SEQUENTIAL on and leave it on.
+ *
+ * So, if the compressed size of the file is above a certain minimum
+ * size, temporarily boost the read-ahead in the hope that the extra
+ * pair of system calls are negated by a reduction in page faults.
+ */
+ if (compLen > kSequentialMin)
+ file->advise(FileMap::SEQUENTIAL);
+
+ if (method == kCompressStored) {
+ memcpy(buffer, ptr, uncompLen);
+ } else {
+ if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
+ goto unmap;
+ }
+
+ if (compLen > kSequentialMin)
+ file->advise(FileMap::NORMAL);
+
+ result = true;
+
+unmap:
+ file->release();
+bail:
+ return result;
+}
+
+/*
+ * Uncompress an entry, in its entirety, to an open file descriptor.
+ *
+ * This doesn't verify the data's CRC, but probably should.
+ */
+bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const
+{
+ bool result = false;
+ int ent = entryToIndex(entry);
+ if (ent < 0)
+ return -1;
+
+ int method;
+ size_t uncompLen, compLen;
+ off64_t offset;
+ const unsigned char* ptr;
+
+ getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
+
+ FileMap* file = createEntryFileMap(entry);
+ if (file == NULL) {
+ goto bail;
+ }
+
+ ptr = (const unsigned char*) file->getDataPtr();
+
+ if (method == kCompressStored) {
+ ssize_t actual = TEMP_FAILURE_RETRY(write(fd, ptr, uncompLen));
+ if (actual < 0) {
+ ALOGE("Write failed: %s\n", strerror(errno));
+ goto unmap;
+ } else if ((size_t) actual != uncompLen) {
+ ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n",
+ (ZD_TYPE) actual, (ZD_TYPE) uncompLen);
+ goto unmap;
+ } else {
+ ALOGI("+++ successful write\n");
+ }
+ } else {
+ if (!inflateBuffer(fd, ptr, uncompLen, compLen))
+ goto unmap;
+ }
+
+ result = true;
+
+unmap:
+ file->release();
+bail:
+ return result;
+}
+
+/*
+ * Uncompress "deflate" data from one buffer to another.
+ */
+/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf,
+ size_t uncompLen, size_t compLen)
+{
+ bool result = false;
+ z_stream zstream;
+ int zerr;
+
+ /*
+ * Initialize the zlib stream struct.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = (Bytef*)inBuf;
+ zstream.avail_in = compLen;
+ zstream.next_out = (Bytef*) outBuf;
+ zstream.avail_out = uncompLen;
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Expand data.
+ */
+ zerr = inflate(&zstream, Z_FINISH);
+ if (zerr != Z_STREAM_END) {
+ ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
+ zerr, zstream.next_in, zstream.avail_in,
+ zstream.next_out, zstream.avail_out);
+ goto z_bail;
+ }
+
+ /* paranoia */
+ if (zstream.total_out != uncompLen) {
+ ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
+ zstream.total_out, (ZD_TYPE) uncompLen);
+ goto z_bail;
+ }
+
+ result = true;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ return result;
+}
+
+/*
+ * Uncompress "deflate" data from one buffer to an open file descriptor.
+ */
+/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf,
+ size_t uncompLen, size_t compLen)
+{
+ bool result = false;
+ const size_t kWriteBufSize = 32768;
+ unsigned char writeBuf[kWriteBufSize];
+ z_stream zstream;
+ int zerr;
+
+ /*
+ * Initialize the zlib stream struct.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = (Bytef*)inBuf;
+ zstream.avail_in = compLen;
+ zstream.next_out = (Bytef*) writeBuf;
+ zstream.avail_out = sizeof(writeBuf);
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Loop while we have more to do.
+ */
+ do {
+ /*
+ * Expand data.
+ */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
+ zerr, zstream.next_in, zstream.avail_in,
+ zstream.next_out, zstream.avail_out);
+ goto z_bail;
+ }
+
+ /* write when we're full or when we're done */
+ if (zstream.avail_out == 0 ||
+ (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf)))
+ {
+ long writeSize = zstream.next_out - writeBuf;
+ int cc = TEMP_FAILURE_RETRY(write(fd, writeBuf, writeSize));
+ if (cc < 0) {
+ ALOGW("write failed in inflate: %s", strerror(errno));
+ goto z_bail;
+ } else if (cc != (int) writeSize) {
+ ALOGW("write failed in inflate (%d vs %ld)", cc, writeSize);
+ goto z_bail;
+ }
+
+ zstream.next_out = writeBuf;
+ zstream.avail_out = sizeof(writeBuf);
+ }
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ /* paranoia */
+ if (zstream.total_out != uncompLen) {
+ ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
+ zstream.total_out, (ZD_TYPE) uncompLen);
+ goto z_bail;
+ }
+
+ result = true;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ return result;
+}
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
new file mode 100644
index 0000000..997eb7d
--- /dev/null
+++ b/libs/androidfw/ZipUtils.cpp
@@ -0,0 +1,345 @@
+/*
+ * 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.
+ */
+
+//
+// Misc zip/gzip utility functions.
+//
+
+#define LOG_TAG "ziputil"
+
+#include <androidfw/ZipUtils.h>
+#include <androidfw/ZipFileRO.h>
+#include <utils/Log.h>
+#include <utils/Compat.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <zlib.h>
+
+using namespace android;
+
+/*
+ * Utility function that expands zip/gzip "deflate" compressed data
+ * into a buffer.
+ *
+ * "fd" is an open file positioned at the start of the "deflate" data
+ * "buf" must hold at least "uncompressedLen" bytes.
+ */
+/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
+ long uncompressedLen, long compressedLen)
+{
+ bool result = false;
+ const unsigned long kReadBufSize = 32768;
+ unsigned char* readBuf = NULL;
+ z_stream zstream;
+ int zerr;
+ unsigned long compRemaining;
+
+ assert(uncompressedLen >= 0);
+ assert(compressedLen >= 0);
+
+ readBuf = new unsigned char[kReadBufSize];
+ if (readBuf == NULL)
+ goto bail;
+ compRemaining = compressedLen;
+
+ /*
+ * Initialize the zlib stream.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = NULL;
+ zstream.avail_in = 0;
+ zstream.next_out = (Bytef*) buf;
+ zstream.avail_out = uncompressedLen;
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Loop while we have data.
+ */
+ do {
+ unsigned long getSize;
+
+ /* read as much as we can */
+ if (zstream.avail_in == 0) {
+ getSize = (compRemaining > kReadBufSize) ?
+ kReadBufSize : compRemaining;
+ ALOGV("+++ reading %ld bytes (%ld left)\n",
+ getSize, compRemaining);
+
+ int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize));
+ if (cc < 0) {
+ ALOGW("inflate read failed: %s", strerror(errno));
+ } else if (cc != (int) getSize) {
+ ALOGW("inflate read failed (%d vs %ld)", cc, getSize);
+ goto z_bail;
+ }
+
+ compRemaining -= getSize;
+
+ zstream.next_in = readBuf;
+ zstream.avail_in = getSize;
+ }
+
+ /* uncompress the data */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
+ goto z_bail;
+ }
+
+ /* output buffer holds all, so no need to write the output */
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ if ((long) zstream.total_out != uncompressedLen) {
+ ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
+ zstream.total_out, uncompressedLen);
+ goto z_bail;
+ }
+
+ // success!
+ result = true;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ delete[] readBuf;
+ return result;
+}
+
+/*
+ * Utility function that expands zip/gzip "deflate" compressed data
+ * into a buffer.
+ *
+ * (This is a clone of the previous function, but it takes a FILE* instead
+ * of an fd. We could pass fileno(fd) to the above, but we can run into
+ * trouble when "fp" has a different notion of what fd's file position is.)
+ *
+ * "fp" is an open file positioned at the start of the "deflate" data
+ * "buf" must hold at least "uncompressedLen" bytes.
+ */
+/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
+ long uncompressedLen, long compressedLen)
+{
+ bool result = false;
+ const unsigned long kReadBufSize = 32768;
+ unsigned char* readBuf = NULL;
+ z_stream zstream;
+ int zerr;
+ unsigned long compRemaining;
+
+ assert(uncompressedLen >= 0);
+ assert(compressedLen >= 0);
+
+ readBuf = new unsigned char[kReadBufSize];
+ if (readBuf == NULL)
+ goto bail;
+ compRemaining = compressedLen;
+
+ /*
+ * Initialize the zlib stream.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = NULL;
+ zstream.avail_in = 0;
+ zstream.next_out = (Bytef*) buf;
+ zstream.avail_out = uncompressedLen;
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Loop while we have data.
+ */
+ do {
+ unsigned long getSize;
+
+ /* read as much as we can */
+ if (zstream.avail_in == 0) {
+ getSize = (compRemaining > kReadBufSize) ?
+ kReadBufSize : compRemaining;
+ ALOGV("+++ reading %ld bytes (%ld left)\n",
+ getSize, compRemaining);
+
+ int cc = fread(readBuf, 1, getSize, fp);
+ if (cc != (int) getSize) {
+ ALOGD("inflate read failed (%d vs %ld)\n",
+ cc, getSize);
+ goto z_bail;
+ }
+
+ compRemaining -= getSize;
+
+ zstream.next_in = readBuf;
+ zstream.avail_in = getSize;
+ }
+
+ /* uncompress the data */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
+ goto z_bail;
+ }
+
+ /* output buffer holds all, so no need to write the output */
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ if ((long) zstream.total_out != uncompressedLen) {
+ ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
+ zstream.total_out, uncompressedLen);
+ goto z_bail;
+ }
+
+ // success!
+ result = true;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ delete[] readBuf;
+ return result;
+}
+
+/*
+ * Look at the contents of a gzip archive. We want to know where the
+ * data starts, and how long it will be after it is uncompressed.
+ *
+ * We expect to find the CRC and length as the last 8 bytes on the file.
+ * This is a pretty reasonable thing to expect for locally-compressed
+ * files, but there's a small chance that some extra padding got thrown
+ * on (the man page talks about compressed data written to tape). We
+ * don't currently deal with that here. If "gzip -l" whines, we're going
+ * to fail too.
+ *
+ * On exit, "fp" is pointing at the start of the compressed data.
+ */
+/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod,
+ long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32)
+{
+ enum { // flags
+ FTEXT = 0x01,
+ FHCRC = 0x02,
+ FEXTRA = 0x04,
+ FNAME = 0x08,
+ FCOMMENT = 0x10,
+ };
+ int ic;
+ int method, flags;
+ int i;
+
+ ic = getc(fp);
+ if (ic != 0x1f || getc(fp) != 0x8b)
+ return false; // not gzip
+ method = getc(fp);
+ flags = getc(fp);
+
+ /* quick sanity checks */
+ if (method == EOF || flags == EOF)
+ return false;
+ if (method != ZipFileRO::kCompressDeflated)
+ return false;
+
+ /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */
+ for (i = 0; i < 6; i++)
+ (void) getc(fp);
+ /* consume "extra" field, if present */
+ if ((flags & FEXTRA) != 0) {
+ int len;
+
+ len = getc(fp);
+ len |= getc(fp) << 8;
+ while (len-- && getc(fp) != EOF)
+ ;
+ }
+ /* consume filename, if present */
+ if ((flags & FNAME) != 0) {
+ do {
+ ic = getc(fp);
+ } while (ic != 0 && ic != EOF);
+ }
+ /* consume comment, if present */
+ if ((flags & FCOMMENT) != 0) {
+ do {
+ ic = getc(fp);
+ } while (ic != 0 && ic != EOF);
+ }
+ /* consume 16-bit header CRC, if present */
+ if ((flags & FHCRC) != 0) {
+ (void) getc(fp);
+ (void) getc(fp);
+ }
+
+ if (feof(fp) || ferror(fp))
+ return false;
+
+ /* seek to the end; CRC and length are in the last 8 bytes */
+ long curPosn = ftell(fp);
+ unsigned char buf[8];
+ fseek(fp, -8, SEEK_END);
+ *pCompressedLen = ftell(fp) - curPosn;
+
+ if (fread(buf, 1, 8, fp) != 8)
+ return false;
+ /* seek back to start of compressed data */
+ fseek(fp, curPosn, SEEK_SET);
+
+ *pCompressionMethod = method;
+ *pCRC32 = ZipFileRO::get4LE(&buf[0]);
+ *pUncompressedLen = ZipFileRO::get4LE(&buf[4]);
+
+ return true;
+}
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
new file mode 100644
index 0000000..29686ef
--- /dev/null
+++ b/libs/androidfw/misc.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "misc"
+
+//
+// Miscellaneous utility functions.
+//
+#include <androidfw/misc.h>
+
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+using namespace android;
+
+namespace android {
+
+/*
+ * Get a file's type.
+ */
+FileType getFileType(const char* fileName)
+{
+ struct stat sb;
+
+ if (stat(fileName, &sb) < 0) {
+ if (errno == ENOENT || errno == ENOTDIR)
+ return kFileTypeNonexistent;
+ else {
+ fprintf(stderr, "getFileType got errno=%d on '%s'\n",
+ errno, fileName);
+ return kFileTypeUnknown;
+ }
+ } else {
+ if (S_ISREG(sb.st_mode))
+ return kFileTypeRegular;
+ else if (S_ISDIR(sb.st_mode))
+ return kFileTypeDirectory;
+ else if (S_ISCHR(sb.st_mode))
+ return kFileTypeCharDev;
+ else if (S_ISBLK(sb.st_mode))
+ return kFileTypeBlockDev;
+ else if (S_ISFIFO(sb.st_mode))
+ return kFileTypeFifo;
+#ifdef HAVE_SYMLINKS
+ else if (S_ISLNK(sb.st_mode))
+ return kFileTypeSymlink;
+ else if (S_ISSOCK(sb.st_mode))
+ return kFileTypeSocket;
+#endif
+ else
+ return kFileTypeUnknown;
+ }
+}
+
+/*
+ * Get a file's modification date.
+ */
+time_t getFileModDate(const char* fileName)
+{
+ struct stat sb;
+
+ if (stat(fileName, &sb) < 0)
+ return (time_t) -1;
+
+ return sb.st_mtime;
+}
+
+}; // namespace android
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 4ae23ec..0522212 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -4,19 +4,15 @@
# Build the unit tests.
test_src_files := \
- InputChannel_test.cpp \
- InputEvent_test.cpp \
- InputPublisherAndConsumer_test.cpp \
- ObbFile_test.cpp
+ ObbFile_test.cpp \
+ ZipFileRO_test.cpp
shared_libraries := \
libandroidfw \
libcutils \
libutils \
- libbinder \
libui \
- libstlport \
- libskia
+ libstlport
static_libraries := \
libgtest \
diff --git a/libs/androidfw/tests/InputChannel_test.cpp b/libs/androidfw/tests/InputChannel_test.cpp
deleted file mode 100644
index 7fff8af..0000000
--- a/libs/androidfw/tests/InputChannel_test.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <androidfw/InputTransport.h>
-#include <utils/Timers.h>
-#include <utils/StopWatch.h>
-#include <utils/StrongPointer.h>
-#include <gtest/gtest.h>
-#include <unistd.h>
-#include <time.h>
-#include <errno.h>
-
-#include "TestHelpers.h"
-
-namespace android {
-
-class InputChannelTest : public testing::Test {
-protected:
- virtual void SetUp() { }
- virtual void TearDown() { }
-};
-
-
-TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) {
- // Our purpose here is to verify that the input channel destructor closes the
- // file descriptor provided to it. One easy way is to provide it with one end
- // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
- Pipe pipe;
-
- sp<InputChannel> inputChannel = new InputChannel(String8("channel name"), pipe.sendFd);
-
- EXPECT_STREQ("channel name", inputChannel->getName().string())
- << "channel should have provided name";
- EXPECT_EQ(pipe.sendFd, inputChannel->getFd())
- << "channel should have provided fd";
-
- inputChannel.clear(); // destroys input channel
-
- EXPECT_EQ(-EPIPE, pipe.readSignal())
- << "channel should have closed fd when destroyed";
-
- // clean up fds of Pipe endpoints that were closed so we don't try to close them again
- pipe.sendFd = -1;
-}
-
-TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
- sp<InputChannel> serverChannel, clientChannel;
-
- status_t result = InputChannel::openInputChannelPair(String8("channel name"),
- serverChannel, clientChannel);
-
- ASSERT_EQ(OK, result)
- << "should have successfully opened a channel pair";
-
- // Name
- EXPECT_STREQ("channel name (server)", serverChannel->getName().string())
- << "server channel should have suffixed name";
- EXPECT_STREQ("channel name (client)", clientChannel->getName().string())
- << "client channel should have suffixed name";
-
- // Server->Client communication
- InputMessage serverMsg;
- memset(&serverMsg, 0, sizeof(InputMessage));
- serverMsg.header.type = InputMessage::TYPE_KEY;
- serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN;
- EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg))
- << "server channel should be able to send message to client channel";
-
- InputMessage clientMsg;
- EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg))
- << "client channel should be able to receive message from server channel";
- EXPECT_EQ(serverMsg.header.type, clientMsg.header.type)
- << "client channel should receive the correct message from server channel";
- EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action)
- << "client channel should receive the correct message from server channel";
-
- // Client->Server communication
- InputMessage clientReply;
- memset(&clientReply, 0, sizeof(InputMessage));
- clientReply.header.type = InputMessage::TYPE_FINISHED;
- clientReply.body.finished.seq = 0x11223344;
- clientReply.body.finished.handled = true;
- EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply))
- << "client channel should be able to send message to server channel";
-
- InputMessage serverReply;
- EXPECT_EQ(OK, serverChannel->receiveMessage(&serverReply))
- << "server channel should be able to receive message from client channel";
- EXPECT_EQ(clientReply.header.type, serverReply.header.type)
- << "server channel should receive the correct message from client channel";
- EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq)
- << "server channel should receive the correct message from client channel";
- EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled)
- << "server channel should receive the correct message from client channel";
-}
-
-TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
-
- status_t result = InputChannel::openInputChannelPair(String8("channel name"),
- serverChannel, clientChannel);
-
- ASSERT_EQ(OK, result)
- << "should have successfully opened a channel pair";
-
- InputMessage msg;
- EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveMessage(&msg))
- << "receiveMessage should have returned WOULD_BLOCK";
-}
-
-TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
-
- status_t result = InputChannel::openInputChannelPair(String8("channel name"),
- serverChannel, clientChannel);
-
- ASSERT_EQ(OK, result)
- << "should have successfully opened a channel pair";
-
- serverChannel.clear(); // close server channel
-
- InputMessage msg;
- EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg))
- << "receiveMessage should have returned DEAD_OBJECT";
-}
-
-TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
-
- status_t result = InputChannel::openInputChannelPair(String8("channel name"),
- serverChannel, clientChannel);
-
- ASSERT_EQ(OK, result)
- << "should have successfully opened a channel pair";
-
- serverChannel.clear(); // close server channel
-
- InputMessage msg;
- msg.header.type = InputMessage::TYPE_KEY;
- EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg))
- << "sendMessage should have returned DEAD_OBJECT";
-}
-
-
-} // namespace android
diff --git a/libs/androidfw/tests/InputEvent_test.cpp b/libs/androidfw/tests/InputEvent_test.cpp
deleted file mode 100644
index e9164d1..0000000
--- a/libs/androidfw/tests/InputEvent_test.cpp
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * 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.
- */
-
-#include <androidfw/Input.h>
-#include <gtest/gtest.h>
-#include <binder/Parcel.h>
-
-#include <math.h>
-#include <core/SkMatrix.h>
-
-namespace android {
-
-class BaseTest : public testing::Test {
-protected:
- virtual void SetUp() { }
- virtual void TearDown() { }
-};
-
-// --- PointerCoordsTest ---
-
-class PointerCoordsTest : public BaseTest {
-};
-
-TEST_F(PointerCoordsTest, ClearSetsBitsToZero) {
- PointerCoords coords;
- coords.clear();
-
- ASSERT_EQ(0ULL, coords.bits);
-}
-
-TEST_F(PointerCoordsTest, AxisValues) {
- float* valuePtr;
- PointerCoords coords;
- coords.clear();
-
- // Check invariants when no axes are present.
- ASSERT_EQ(0, coords.getAxisValue(0))
- << "getAxisValue should return zero because axis is not present";
- ASSERT_EQ(0, coords.getAxisValue(1))
- << "getAxisValue should return zero because axis is not present";
-
- // Set first axis.
- ASSERT_EQ(OK, coords.setAxisValue(1, 5));
- ASSERT_EQ(0x00000002ULL, coords.bits);
- ASSERT_EQ(5, coords.values[0]);
-
- ASSERT_EQ(0, coords.getAxisValue(0))
- << "getAxisValue should return zero because axis is not present";
- ASSERT_EQ(5, coords.getAxisValue(1))
- << "getAxisValue should return value of axis";
-
- // Set an axis with a higher id than all others. (appending value at the end)
- ASSERT_EQ(OK, coords.setAxisValue(3, 2));
- ASSERT_EQ(0x0000000aULL, coords.bits);
- ASSERT_EQ(5, coords.values[0]);
- ASSERT_EQ(2, coords.values[1]);
-
- ASSERT_EQ(0, coords.getAxisValue(0))
- << "getAxisValue should return zero because axis is not present";
- ASSERT_EQ(5, coords.getAxisValue(1))
- << "getAxisValue should return value of axis";
- ASSERT_EQ(0, coords.getAxisValue(2))
- << "getAxisValue should return zero because axis is not present";
- ASSERT_EQ(2, coords.getAxisValue(3))
- << "getAxisValue should return value of axis";
-
- // Set an axis with an id lower than all others. (prepending value at beginning)
- ASSERT_EQ(OK, coords.setAxisValue(0, 4));
- ASSERT_EQ(0x0000000bULL, coords.bits);
- ASSERT_EQ(4, coords.values[0]);
- ASSERT_EQ(5, coords.values[1]);
- ASSERT_EQ(2, coords.values[2]);
-
- ASSERT_EQ(4, coords.getAxisValue(0))
- << "getAxisValue should return value of axis";
- ASSERT_EQ(5, coords.getAxisValue(1))
- << "getAxisValue should return value of axis";
- ASSERT_EQ(0, coords.getAxisValue(2))
- << "getAxisValue should return zero because axis is not present";
- ASSERT_EQ(2, coords.getAxisValue(3))
- << "getAxisValue should return value of axis";
-
- // Set an axis with an id between the others. (inserting value in the middle)
- ASSERT_EQ(OK, coords.setAxisValue(2, 1));
- ASSERT_EQ(0x0000000fULL, coords.bits);
- ASSERT_EQ(4, coords.values[0]);
- ASSERT_EQ(5, coords.values[1]);
- ASSERT_EQ(1, coords.values[2]);
- ASSERT_EQ(2, coords.values[3]);
-
- ASSERT_EQ(4, coords.getAxisValue(0))
- << "getAxisValue should return value of axis";
- ASSERT_EQ(5, coords.getAxisValue(1))
- << "getAxisValue should return value of axis";
- ASSERT_EQ(1, coords.getAxisValue(2))
- << "getAxisValue should return value of axis";
- ASSERT_EQ(2, coords.getAxisValue(3))
- << "getAxisValue should return value of axis";
-
- // Set an existing axis value in place.
- ASSERT_EQ(OK, coords.setAxisValue(1, 6));
- ASSERT_EQ(0x0000000fULL, coords.bits);
- ASSERT_EQ(4, coords.values[0]);
- ASSERT_EQ(6, coords.values[1]);
- ASSERT_EQ(1, coords.values[2]);
- ASSERT_EQ(2, coords.values[3]);
-
- ASSERT_EQ(4, coords.getAxisValue(0))
- << "getAxisValue should return value of axis";
- ASSERT_EQ(6, coords.getAxisValue(1))
- << "getAxisValue should return value of axis";
- ASSERT_EQ(1, coords.getAxisValue(2))
- << "getAxisValue should return value of axis";
- ASSERT_EQ(2, coords.getAxisValue(3))
- << "getAxisValue should return value of axis";
-
- // Set maximum number of axes.
- for (size_t axis = 4; axis < PointerCoords::MAX_AXES; axis++) {
- ASSERT_EQ(OK, coords.setAxisValue(axis, axis));
- }
- ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits));
-
- // Try to set one more axis beyond maximum number.
- // Ensure bits are unchanged.
- ASSERT_EQ(NO_MEMORY, coords.setAxisValue(PointerCoords::MAX_AXES, 100));
- ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits));
-}
-
-TEST_F(PointerCoordsTest, Parcel) {
- Parcel parcel;
-
- PointerCoords inCoords;
- inCoords.clear();
- PointerCoords outCoords;
-
- // Round trip with empty coords.
- inCoords.writeToParcel(&parcel);
- parcel.setDataPosition(0);
- outCoords.readFromParcel(&parcel);
-
- ASSERT_EQ(0ULL, outCoords.bits);
-
- // Round trip with some values.
- parcel.freeData();
- inCoords.setAxisValue(2, 5);
- inCoords.setAxisValue(5, 8);
-
- inCoords.writeToParcel(&parcel);
- parcel.setDataPosition(0);
- outCoords.readFromParcel(&parcel);
-
- ASSERT_EQ(outCoords.bits, inCoords.bits);
- ASSERT_EQ(outCoords.values[0], inCoords.values[0]);
- ASSERT_EQ(outCoords.values[1], inCoords.values[1]);
-}
-
-
-// --- KeyEventTest ---
-
-class KeyEventTest : public BaseTest {
-};
-
-TEST_F(KeyEventTest, Properties) {
- KeyEvent event;
-
- // Initialize and get properties.
- const nsecs_t ARBITRARY_DOWN_TIME = 1;
- const nsecs_t ARBITRARY_EVENT_TIME = 2;
- event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN,
- AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121,
- AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
-
- ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType());
- ASSERT_EQ(2, event.getDeviceId());
- ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource());
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction());
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags());
- ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode());
- ASSERT_EQ(121, event.getScanCode());
- ASSERT_EQ(AMETA_ALT_ON, event.getMetaState());
- ASSERT_EQ(1, event.getRepeatCount());
- ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime());
- ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getEventTime());
-
- // Set source.
- event.setSource(AINPUT_SOURCE_JOYSTICK);
- ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
-}
-
-
-// --- MotionEventTest ---
-
-class MotionEventTest : public BaseTest {
-protected:
- static const nsecs_t ARBITRARY_DOWN_TIME;
- static const nsecs_t ARBITRARY_EVENT_TIME;
- static const float X_OFFSET;
- static const float Y_OFFSET;
-
- void initializeEventWithHistory(MotionEvent* event);
- void assertEqualsEventWithHistory(const MotionEvent* event);
-};
-
-const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1;
-const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2;
-const float MotionEventTest::X_OFFSET = 1.0f;
-const float MotionEventTest::Y_OFFSET = 1.1f;
-
-void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
- PointerProperties pointerProperties[2];
- pointerProperties[0].clear();
- pointerProperties[0].id = 1;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- pointerProperties[1].clear();
- pointerProperties[1].id = 2;
- pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
-
- PointerCoords pointerCoords[2];
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18);
- pointerCoords[1].clear();
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
- event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
- AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
- AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
- X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
- ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME,
- 2, pointerProperties, pointerCoords);
-
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
- event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
-
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
- event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
-}
-
-void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
- // Check properties.
- ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
- ASSERT_EQ(2, event->getDeviceId());
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource());
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
- ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
- ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
- ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
- ASSERT_EQ(X_OFFSET, event->getXOffset());
- ASSERT_EQ(Y_OFFSET, event->getYOffset());
- ASSERT_EQ(2.0f, event->getXPrecision());
- ASSERT_EQ(2.1f, event->getYPrecision());
- ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime());
-
- ASSERT_EQ(2U, event->getPointerCount());
- ASSERT_EQ(1, event->getPointerId(0));
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, event->getToolType(0));
- ASSERT_EQ(2, event->getPointerId(1));
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, event->getToolType(1));
-
- ASSERT_EQ(2U, event->getHistorySize());
-
- // Check data.
- ASSERT_EQ(ARBITRARY_EVENT_TIME, event->getHistoricalEventTime(0));
- ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
- ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
-
- ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(211, event->getRawPointerCoords(0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(221, event->getRawPointerCoords(1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
-
- ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
- ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
- ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
- ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
- ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
- ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
-
- ASSERT_EQ(10, event->getHistoricalRawX(0, 0));
- ASSERT_EQ(20, event->getHistoricalRawX(1, 0));
- ASSERT_EQ(110, event->getHistoricalRawX(0, 1));
- ASSERT_EQ(120, event->getHistoricalRawX(1, 1));
- ASSERT_EQ(210, event->getRawX(0));
- ASSERT_EQ(220, event->getRawX(1));
-
- ASSERT_EQ(11, event->getHistoricalRawY(0, 0));
- ASSERT_EQ(21, event->getHistoricalRawY(1, 0));
- ASSERT_EQ(111, event->getHistoricalRawY(0, 1));
- ASSERT_EQ(121, event->getHistoricalRawY(1, 1));
- ASSERT_EQ(211, event->getRawY(0));
- ASSERT_EQ(221, event->getRawY(1));
-
- ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
- ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
- ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
- ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
- ASSERT_EQ(X_OFFSET + 210, event->getX(0));
- ASSERT_EQ(X_OFFSET + 220, event->getX(1));
-
- ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
- ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
- ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
- ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
- ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
- ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
-
- ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
- ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
- ASSERT_EQ(112, event->getHistoricalPressure(0, 1));
- ASSERT_EQ(122, event->getHistoricalPressure(1, 1));
- ASSERT_EQ(212, event->getPressure(0));
- ASSERT_EQ(222, event->getPressure(1));
-
- ASSERT_EQ(13, event->getHistoricalSize(0, 0));
- ASSERT_EQ(23, event->getHistoricalSize(1, 0));
- ASSERT_EQ(113, event->getHistoricalSize(0, 1));
- ASSERT_EQ(123, event->getHistoricalSize(1, 1));
- ASSERT_EQ(213, event->getSize(0));
- ASSERT_EQ(223, event->getSize(1));
-
- ASSERT_EQ(14, event->getHistoricalTouchMajor(0, 0));
- ASSERT_EQ(24, event->getHistoricalTouchMajor(1, 0));
- ASSERT_EQ(114, event->getHistoricalTouchMajor(0, 1));
- ASSERT_EQ(124, event->getHistoricalTouchMajor(1, 1));
- ASSERT_EQ(214, event->getTouchMajor(0));
- ASSERT_EQ(224, event->getTouchMajor(1));
-
- ASSERT_EQ(15, event->getHistoricalTouchMinor(0, 0));
- ASSERT_EQ(25, event->getHistoricalTouchMinor(1, 0));
- ASSERT_EQ(115, event->getHistoricalTouchMinor(0, 1));
- ASSERT_EQ(125, event->getHistoricalTouchMinor(1, 1));
- ASSERT_EQ(215, event->getTouchMinor(0));
- ASSERT_EQ(225, event->getTouchMinor(1));
-
- ASSERT_EQ(16, event->getHistoricalToolMajor(0, 0));
- ASSERT_EQ(26, event->getHistoricalToolMajor(1, 0));
- ASSERT_EQ(116, event->getHistoricalToolMajor(0, 1));
- ASSERT_EQ(126, event->getHistoricalToolMajor(1, 1));
- ASSERT_EQ(216, event->getToolMajor(0));
- ASSERT_EQ(226, event->getToolMajor(1));
-
- ASSERT_EQ(17, event->getHistoricalToolMinor(0, 0));
- ASSERT_EQ(27, event->getHistoricalToolMinor(1, 0));
- ASSERT_EQ(117, event->getHistoricalToolMinor(0, 1));
- ASSERT_EQ(127, event->getHistoricalToolMinor(1, 1));
- ASSERT_EQ(217, event->getToolMinor(0));
- ASSERT_EQ(227, event->getToolMinor(1));
-
- ASSERT_EQ(18, event->getHistoricalOrientation(0, 0));
- ASSERT_EQ(28, event->getHistoricalOrientation(1, 0));
- ASSERT_EQ(118, event->getHistoricalOrientation(0, 1));
- ASSERT_EQ(128, event->getHistoricalOrientation(1, 1));
- ASSERT_EQ(218, event->getOrientation(0));
- ASSERT_EQ(228, event->getOrientation(1));
-}
-
-TEST_F(MotionEventTest, Properties) {
- MotionEvent event;
-
- // Initialize, add samples and check properties.
- initializeEventWithHistory(&event);
- ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
-
- // Set source.
- event.setSource(AINPUT_SOURCE_JOYSTICK);
- ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
-
- // Set action.
- event.setAction(AMOTION_EVENT_ACTION_CANCEL);
- ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction());
-
- // Set meta state.
- event.setMetaState(AMETA_CTRL_ON);
- ASSERT_EQ(AMETA_CTRL_ON, event.getMetaState());
-}
-
-TEST_F(MotionEventTest, CopyFrom_KeepHistory) {
- MotionEvent event;
- initializeEventWithHistory(&event);
-
- MotionEvent copy;
- copy.copyFrom(&event, true /*keepHistory*/);
-
- ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
-}
-
-TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) {
- MotionEvent event;
- initializeEventWithHistory(&event);
-
- MotionEvent copy;
- copy.copyFrom(&event, false /*keepHistory*/);
-
- ASSERT_EQ(event.getPointerCount(), copy.getPointerCount());
- ASSERT_EQ(0U, copy.getHistorySize());
-
- ASSERT_EQ(event.getPointerId(0), copy.getPointerId(0));
- ASSERT_EQ(event.getPointerId(1), copy.getPointerId(1));
-
- ASSERT_EQ(event.getEventTime(), copy.getEventTime());
-
- ASSERT_EQ(event.getX(0), copy.getX(0));
-}
-
-TEST_F(MotionEventTest, OffsetLocation) {
- MotionEvent event;
- initializeEventWithHistory(&event);
-
- event.offsetLocation(5.0f, -2.0f);
-
- ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset());
- ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset());
-}
-
-TEST_F(MotionEventTest, Scale) {
- MotionEvent event;
- initializeEventWithHistory(&event);
-
- event.scale(2.0f);
-
- ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
- ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
-
- ASSERT_EQ(210 * 2, event.getRawX(0));
- ASSERT_EQ(211 * 2, event.getRawY(0));
- ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
- ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
- ASSERT_EQ(212, event.getPressure(0));
- ASSERT_EQ(213, event.getSize(0));
- ASSERT_EQ(214 * 2, event.getTouchMajor(0));
- ASSERT_EQ(215 * 2, event.getTouchMinor(0));
- ASSERT_EQ(216 * 2, event.getToolMajor(0));
- ASSERT_EQ(217 * 2, event.getToolMinor(0));
- ASSERT_EQ(218, event.getOrientation(0));
-}
-
-TEST_F(MotionEventTest, Parcel) {
- Parcel parcel;
-
- MotionEvent inEvent;
- initializeEventWithHistory(&inEvent);
- MotionEvent outEvent;
-
- // Round trip.
- inEvent.writeToParcel(&parcel);
- parcel.setDataPosition(0);
- outEvent.readFromParcel(&parcel);
-
- ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent));
-}
-
-TEST_F(MotionEventTest, Transform) {
- // Generate some points on a circle.
- // Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle
- // of ARC * i degrees clockwise relative to the Y axis.
- // The geometrical representation is irrelevant to the test, it's just easy to generate
- // and check rotation. We set the orientation to the same angle.
- // Coordinate system: down is increasing Y, right is increasing X.
- const float PI_180 = float(M_PI / 180);
- const float RADIUS = 10;
- const float ARC = 36;
- const float ROTATION = ARC * 2;
-
- const size_t pointerCount = 11;
- PointerProperties pointerProperties[pointerCount];
- PointerCoords pointerCoords[pointerCount];
- for (size_t i = 0; i < pointerCount; i++) {
- float angle = float(i * ARC * PI_180);
- pointerProperties[i].clear();
- pointerProperties[i].id = i;
- pointerCoords[i].clear();
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, sinf(angle) * RADIUS + 3);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, -cosf(angle) * RADIUS + 2);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
- }
- MotionEvent event;
- event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
- float originalRawX = 0 + 3;
- float originalRawY = -RADIUS + 2;
-
- // Check original raw X and Y assumption.
- ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
- ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
-
- // Now translate the motion event so the circle's origin is at (0,0).
- event.offsetLocation(-3, -2);
-
- // Offsetting the location should preserve the raw X and Y of the first point.
- ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
- ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
-
- // Apply a rotation about the origin by ROTATION degrees clockwise.
- SkMatrix matrix;
- matrix.setRotate(ROTATION);
- event.transform(&matrix);
-
- // Check the points.
- for (size_t i = 0; i < pointerCount; i++) {
- float angle = float((i * ARC + ROTATION) * PI_180);
- ASSERT_NEAR(sinf(angle) * RADIUS, event.getX(i), 0.001);
- ASSERT_NEAR(-cosf(angle) * RADIUS, event.getY(i), 0.001);
- ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1);
- }
-
- // Applying the transformation should preserve the raw X and Y of the first point.
- ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
- ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
-}
-
-} // namespace android
diff --git a/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp
deleted file mode 100644
index f45774b..0000000
--- a/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <androidfw/InputTransport.h>
-#include <utils/Timers.h>
-#include <utils/StopWatch.h>
-#include <gtest/gtest.h>
-#include <unistd.h>
-#include <time.h>
-#include <sys/mman.h>
-#include <cutils/ashmem.h>
-
-#include "TestHelpers.h"
-
-namespace android {
-
-class InputPublisherAndConsumerTest : public testing::Test {
-protected:
- sp<InputChannel> serverChannel, clientChannel;
- InputPublisher* mPublisher;
- InputConsumer* mConsumer;
- PreallocatedInputEventFactory mEventFactory;
-
- virtual void SetUp() {
- status_t result = InputChannel::openInputChannelPair(String8("channel name"),
- serverChannel, clientChannel);
-
- mPublisher = new InputPublisher(serverChannel);
- mConsumer = new InputConsumer(clientChannel);
- }
-
- virtual void TearDown() {
- if (mPublisher) {
- delete mPublisher;
- mPublisher = NULL;
- }
-
- if (mConsumer) {
- delete mConsumer;
- mConsumer = NULL;
- }
-
- serverChannel.clear();
- clientChannel.clear();
- }
-
- void PublishAndConsumeKeyEvent();
- void PublishAndConsumeMotionEvent();
-};
-
-TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
- EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
- EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
-}
-
-void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
- status_t status;
-
- const uint32_t seq = 15;
- const int32_t deviceId = 1;
- const int32_t source = AINPUT_SOURCE_KEYBOARD;
- const int32_t action = AKEY_EVENT_ACTION_DOWN;
- const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
- const int32_t keyCode = AKEYCODE_ENTER;
- const int32_t scanCode = 13;
- const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
- const int32_t repeatCount = 1;
- const nsecs_t downTime = 3;
- const nsecs_t eventTime = 4;
-
- status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags,
- keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
- ASSERT_EQ(OK, status)
- << "publisher publishKeyEvent should return OK";
-
- uint32_t consumeSeq;
- InputEvent* event;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
- ASSERT_EQ(OK, status)
- << "consumer consume should return OK";
-
- ASSERT_TRUE(event != NULL)
- << "consumer should have returned non-NULL event";
- ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType())
- << "consumer should have returned a key event";
-
- KeyEvent* keyEvent = static_cast<KeyEvent*>(event);
- EXPECT_EQ(seq, consumeSeq);
- EXPECT_EQ(deviceId, keyEvent->getDeviceId());
- EXPECT_EQ(source, keyEvent->getSource());
- EXPECT_EQ(action, keyEvent->getAction());
- EXPECT_EQ(flags, keyEvent->getFlags());
- EXPECT_EQ(keyCode, keyEvent->getKeyCode());
- EXPECT_EQ(scanCode, keyEvent->getScanCode());
- EXPECT_EQ(metaState, keyEvent->getMetaState());
- EXPECT_EQ(repeatCount, keyEvent->getRepeatCount());
- EXPECT_EQ(downTime, keyEvent->getDownTime());
- EXPECT_EQ(eventTime, keyEvent->getEventTime());
-
- status = mConsumer->sendFinishedSignal(seq, true);
- ASSERT_EQ(OK, status)
- << "consumer sendFinishedSignal should return OK";
-
- uint32_t finishedSeq = 0;
- bool handled = false;
- status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
- ASSERT_EQ(OK, status)
- << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-}
-
-void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
- status_t status;
-
- const uint32_t seq = 15;
- const int32_t deviceId = 1;
- const int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
- const int32_t action = AMOTION_EVENT_ACTION_MOVE;
- const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
- const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
- const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
- const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
- const float xOffset = -10;
- const float yOffset = -20;
- const float xPrecision = 0.25;
- const float yPrecision = 0.5;
- const nsecs_t downTime = 3;
- const size_t pointerCount = 3;
- const nsecs_t eventTime = 4;
- PointerProperties pointerProperties[pointerCount];
- PointerCoords pointerCoords[pointerCount];
- for (size_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].clear();
- pointerProperties[i].id = (i + 2) % pointerCount;
- pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
- pointerCoords[i].clear();
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
- }
-
- status = mPublisher->publishMotionEvent(seq, deviceId, source, action, flags, edgeFlags,
- metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision,
- downTime, eventTime, pointerCount,
- pointerProperties, pointerCoords);
- ASSERT_EQ(OK, status)
- << "publisher publishMotionEvent should return OK";
-
- uint32_t consumeSeq;
- InputEvent* event;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
- ASSERT_EQ(OK, status)
- << "consumer consume should return OK";
-
- ASSERT_TRUE(event != NULL)
- << "consumer should have returned non-NULL event";
- ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
- << "consumer should have returned a motion event";
-
- MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
- EXPECT_EQ(seq, consumeSeq);
- EXPECT_EQ(deviceId, motionEvent->getDeviceId());
- EXPECT_EQ(source, motionEvent->getSource());
- EXPECT_EQ(action, motionEvent->getAction());
- EXPECT_EQ(flags, motionEvent->getFlags());
- EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
- EXPECT_EQ(metaState, motionEvent->getMetaState());
- EXPECT_EQ(buttonState, motionEvent->getButtonState());
- EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
- EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
- EXPECT_EQ(downTime, motionEvent->getDownTime());
- EXPECT_EQ(eventTime, motionEvent->getEventTime());
- EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
- EXPECT_EQ(0U, motionEvent->getHistorySize());
-
- for (size_t i = 0; i < pointerCount; i++) {
- SCOPED_TRACE(i);
- EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i));
- EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
-
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- motionEvent->getRawX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- motionEvent->getRawY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
- motionEvent->getX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
- motionEvent->getY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- motionEvent->getPressure(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- motionEvent->getSize(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- motionEvent->getTouchMajor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- motionEvent->getTouchMinor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- motionEvent->getToolMajor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- motionEvent->getToolMinor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
- motionEvent->getOrientation(i));
- }
-
- status = mConsumer->sendFinishedSignal(seq, false);
- ASSERT_EQ(OK, status)
- << "consumer sendFinishedSignal should return OK";
-
- uint32_t finishedSeq = 0;
- bool handled = true;
- status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
- ASSERT_EQ(OK, status)
- << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_FALSE(handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) {
- status_t status;
- const size_t pointerCount = 0;
- PointerProperties pointerProperties[pointerCount];
- PointerCoords pointerCoords[pointerCount];
-
- status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
- ASSERT_EQ(BAD_VALUE, status)
- << "publisher publishMotionEvent should return BAD_VALUE";
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
- status_t status;
- const size_t pointerCount = MAX_POINTERS + 1;
- PointerProperties pointerProperties[pointerCount];
- PointerCoords pointerCoords[pointerCount];
- for (size_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].clear();
- pointerCoords[i].clear();
- }
-
- status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
- ASSERT_EQ(BAD_VALUE, status)
- << "publisher publishMotionEvent should return BAD_VALUE";
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
-}
-
-} // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
deleted file mode 100644
index d8e985e..0000000
--- a/libs/androidfw/tests/TestHelpers.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TESTHELPERS_H
-#define TESTHELPERS_H
-
-#include <utils/threads.h>
-
-namespace android {
-
-class Pipe {
-public:
- int sendFd;
- int receiveFd;
-
- Pipe() {
- int fds[2];
- ::pipe(fds);
-
- receiveFd = fds[0];
- sendFd = fds[1];
- }
-
- ~Pipe() {
- if (sendFd != -1) {
- ::close(sendFd);
- }
-
- if (receiveFd != -1) {
- ::close(receiveFd);
- }
- }
-
- status_t writeSignal() {
- ssize_t nWritten = ::write(sendFd, "*", 1);
- return nWritten == 1 ? 0 : -errno;
- }
-
- status_t readSignal() {
- char buf[1];
- ssize_t nRead = ::read(receiveFd, buf, 1);
- return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
- }
-};
-
-class DelayedTask : public Thread {
- int mDelayMillis;
-
-public:
- DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
-
-protected:
- virtual ~DelayedTask() { }
-
- virtual void doTask() = 0;
-
- virtual bool threadLoop() {
- usleep(mDelayMillis * 1000);
- doTask();
- return false;
- }
-};
-
-} // namespace android
-
-#endif // TESTHELPERS_H
diff --git a/libs/androidfw/tests/ZipFileRO_test.cpp b/libs/androidfw/tests/ZipFileRO_test.cpp
new file mode 100644
index 0000000..cb9c721
--- /dev/null
+++ b/libs/androidfw/tests/ZipFileRO_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ZipFileRO_test"
+#include <utils/Log.h>
+#include <androidfw/ZipFileRO.h>
+
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <string.h>
+
+namespace android {
+
+class ZipFileROTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(ZipFileROTest, ZipTimeConvertSuccess) {
+ struct tm t;
+
+ // 2011-06-29 14:40:40
+ long when = 0x3EDD7514;
+
+ ZipFileRO::zipTimeToTimespec(when, &t);
+
+ EXPECT_EQ(2011, t.tm_year + 1900)
+ << "Year was improperly converted.";
+
+ EXPECT_EQ(6, t.tm_mon)
+ << "Month was improperly converted.";
+
+ EXPECT_EQ(29, t.tm_mday)
+ << "Day was improperly converted.";
+
+ EXPECT_EQ(14, t.tm_hour)
+ << "Hour was improperly converted.";
+
+ EXPECT_EQ(40, t.tm_min)
+ << "Minute was improperly converted.";
+
+ EXPECT_EQ(40, t.tm_sec)
+ << "Second was improperly converted.";
+}
+
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index a630ea1..5a30472 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -10,6 +10,7 @@
thread/TaskManager.cpp \
font/CacheTexture.cpp \
font/Font.cpp \
+ AssetAtlas.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
@@ -21,6 +22,7 @@
Extensions.cpp \
FboCache.cpp \
GradientCache.cpp \
+ Image.cpp \
Layer.cpp \
LayerCache.cpp \
LayerRenderer.cpp \
@@ -39,6 +41,7 @@
SkiaShader.cpp \
Snapshot.cpp \
Stencil.cpp \
+ Texture.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
@@ -52,17 +55,23 @@
external/skia/include/images \
external/skia/src/core \
external/skia/src/ports \
- external/skia/include/utils \
- $(intermediates) \
- frameworks/rs/cpp \
- frameworks/rs
+ external/skia/include/utils
- LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES
+ LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
- LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libGLESv2 libskia libui libRS libRScpp
+ LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
+ ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+ LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
+ LOCAL_SHARED_LIBRARIES += libRS libRScpp
+ LOCAL_C_INCLUDES += \
+ $(intermediates) \
+ frameworks/rs/cpp \
+ frameworks/rs
+ endif
+
ifndef HWUI_COMPILE_SYMBOLS
LOCAL_CFLAGS += -fvisibility=hidden
endif
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
new file mode 100644
index 0000000..d98a538
--- /dev/null
+++ b/libs/hwui/AssetAtlas.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "AssetAtlas.h"
+#include "Caches.h"
+
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Lifecycle
+///////////////////////////////////////////////////////////////////////////////
+
+void AssetAtlas::init(sp<GraphicBuffer> buffer, int* map, int count) {
+ if (mImage) {
+ return;
+ }
+
+ mImage = new Image(buffer);
+
+ if (mImage->getTexture()) {
+ Caches& caches = Caches::getInstance();
+
+ mTexture = new Texture(caches);
+ mTexture->id = mImage->getTexture();
+ mTexture->width = buffer->getWidth();
+ mTexture->height = buffer->getHeight();
+
+ createEntries(caches, map, count);
+ } else {
+ ALOGW("Could not create atlas image");
+
+ delete mImage;
+ mImage = NULL;
+ mTexture = NULL;
+ }
+
+ mGenerationId++;
+}
+
+void AssetAtlas::terminate() {
+ if (mImage) {
+ delete mImage;
+ mImage = NULL;
+
+ delete mTexture;
+ mTexture = NULL;
+
+ for (size_t i = 0; i < mEntries.size(); i++) {
+ delete mEntries.valueAt(i);
+ }
+ mEntries.clear();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Entries
+///////////////////////////////////////////////////////////////////////////////
+
+AssetAtlas::Entry* AssetAtlas::getEntry(SkBitmap* const bitmap) const {
+ ssize_t index = mEntries.indexOfKey(bitmap);
+ return index >= 0 ? mEntries.valueAt(index) : NULL;
+}
+
+Texture* AssetAtlas::getEntryTexture(SkBitmap* const bitmap) const {
+ ssize_t index = mEntries.indexOfKey(bitmap);
+ return index >= 0 ? mEntries.valueAt(index)->texture : NULL;
+}
+
+/**
+ * Delegates changes to wrapping and filtering to the base atlas texture
+ * instead of applying the changes to the virtual textures.
+ */
+struct DelegateTexture: public Texture {
+ DelegateTexture(Caches& caches, Texture* delegate): Texture(caches), mDelegate(delegate) { }
+
+ virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+ mDelegate->setWrapST(wrapS, wrapT, bindTexture, force, renderTarget);
+ }
+
+ virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+ mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget);
+ }
+private:
+ Texture* const mDelegate;
+}; // struct DelegateTexture
+
+/**
+ * TODO: This method does not take the rotation flag into account
+ */
+void AssetAtlas::createEntries(Caches& caches, int* map, int count) {
+ const float width = float(mTexture->width);
+ const float height = float(mTexture->height);
+
+ for (int i = 0; i < count; ) {
+ SkBitmap* bitmap = (SkBitmap*) map[i++];
+ int x = map[i++];
+ int y = map[i++];
+ bool rotated = map[i++] > 0;
+
+ // Bitmaps should never be null, we're just extra paranoid
+ if (!bitmap) continue;
+
+ const UvMapper mapper(
+ x / width, (x + bitmap->width()) / width,
+ y / height, (y + bitmap->height()) / height);
+
+ Texture* texture = new DelegateTexture(caches, mTexture);
+ Entry* entry = new Entry(bitmap, x, y, rotated, texture, mapper, *this);
+
+ texture->id = mTexture->id;
+ texture->blend = !bitmap->isOpaque();
+ texture->width = bitmap->width();
+ texture->height = bitmap->height();
+ texture->uvMapper = &entry->uvMapper;
+
+ mEntries.add(entry->bitmap, entry);
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
new file mode 100644
index 0000000..9afc54d
--- /dev/null
+++ b/libs/hwui/AssetAtlas.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_ASSET_ATLAS_H
+#define ANDROID_HWUI_ASSET_ATLAS_H
+
+#include <GLES2/gl2.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include <utils/KeyedVector.h>
+
+#include <cutils/compiler.h>
+
+#include <SkBitmap.h>
+
+#include "Image.h"
+#include "Texture.h"
+#include "UvMapper.h"
+
+namespace android {
+namespace uirenderer {
+
+class Caches;
+
+/**
+ * An asset atlas holds a collection of framework bitmaps in a single OpenGL
+ * texture. Each bitmap is associated with a location, defined in pixels,
+ * inside the atlas. The atlas is generated by the framework and bound as
+ * an external texture using the EGLImageKHR extension.
+ */
+class AssetAtlas {
+public:
+ /**
+ * Entry representing the position and rotation of a
+ * bitmap inside the atlas.
+ */
+ struct Entry {
+ /**
+ * The bitmap that generated this atlas entry.
+ */
+ SkBitmap* bitmap;
+
+ /**
+ * Location of the bitmap inside the atlas, in pixels.
+ */
+ int x;
+ int y;
+
+ /**
+ * If set, the bitmap is rotated 90 degrees (clockwise)
+ * inside the atlas.
+ */
+ bool rotated;
+
+ /*
+ * A "virtual texture" object that represents the texture
+ * this entry belongs to. This texture should never be
+ * modified.
+ */
+ Texture* texture;
+
+ /**
+ * Maps texture coordinates in the [0..1] range into the
+ * correct range to sample this entry from the atlas.
+ */
+ const UvMapper uvMapper;
+
+ /**
+ * Atlas this entry belongs to.
+ */
+ const AssetAtlas& atlas;
+
+ private:
+ Entry(SkBitmap* bitmap, int x, int y, bool rotated,
+ Texture* texture, const UvMapper& mapper, const AssetAtlas& atlas):
+ bitmap(bitmap), x(x), y(y), rotated(rotated),
+ texture(texture), uvMapper(mapper), atlas(atlas) { }
+
+ ~Entry() {
+ delete texture;
+ }
+
+ friend class AssetAtlas;
+ };
+
+ AssetAtlas(): mTexture(NULL), mImage(NULL), mGenerationId(0) { }
+ ~AssetAtlas() { terminate(); }
+
+ /**
+ * Initializes the atlas with the specified buffer and
+ * map. The buffer is a gralloc'd texture that will be
+ * used as an EGLImage. The map is a list of SkBitmap*
+ * and their (x, y) positions as well as their rotation
+ * flags.
+ *
+ * This method returns immediately if the atlas is already
+ * initialized. To re-initialize the atlas, you must
+ * first call terminate().
+ */
+ ANDROID_API void init(sp<GraphicBuffer> buffer, int* map, int count);
+
+ /**
+ * Destroys the atlas texture. This object can be
+ * re-initialized after calling this method.
+ *
+ * After calling this method, the width, height
+ * and texture are set to 0.
+ */
+ ANDROID_API void terminate();
+
+ /**
+ * Returns the width of this atlas in pixels.
+ * Can return 0 if the atlas is not initialized.
+ */
+ uint32_t getWidth() const {
+ return mTexture ? mTexture->width : 0;
+ }
+
+ /**
+ * Returns the height of this atlas in pixels.
+ * Can return 0 if the atlas is not initialized.
+ */
+ uint32_t getHeight() const {
+ return mTexture ? mTexture->height : 0;
+ }
+
+ /**
+ * Returns the OpenGL name of the texture backing this atlas.
+ * Can return 0 if the atlas is not initialized.
+ */
+ GLuint getTexture() const {
+ return mTexture ? mTexture->id : 0;
+ }
+
+ /**
+ * Returns the entry in the atlas associated with the specified
+ * bitmap. If the bitmap is not in the atlas, return NULL.
+ */
+ Entry* getEntry(SkBitmap* const bitmap) const;
+
+ /**
+ * Returns the texture for the atlas entry associated with the
+ * specified bitmap. If the bitmap is not in the atlas, return NULL.
+ */
+ Texture* getEntryTexture(SkBitmap* const bitmap) const;
+
+ /**
+ * Returns the current generation id of the atlas.
+ */
+ uint32_t getGenerationId() const {
+ return mGenerationId;
+ }
+
+private:
+ void createEntries(Caches& caches, int* map, int count);
+
+ Texture* mTexture;
+ Image* mImage;
+
+ uint32_t mGenerationId;
+
+ KeyedVector<SkBitmap*, Entry*> mEntries;
+}; // class AssetAtlas
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_ASSET_ATLAS_H
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index a381a68..6de8c8c 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -47,19 +47,21 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-Caches::Caches(): Singleton<Caches>(), mExtensions(Extensions::getInstance()), mInitialized(false) {
+Caches::Caches(): Singleton<Caches>(),
+ mExtensions(Extensions::getInstance()), mInitialized(false) {
init();
initFont();
initConstraints();
initProperties();
+ initStaticProperties();
initExtensions();
mDebugLevel = readDebugLevel();
ALOGD("Enabling debug mode %d", mDebugLevel);
}
-void Caches::init() {
- if (mInitialized) return;
+bool Caches::init() {
+ if (mInitialized) return false;
glGenBuffers(1, &meshBuffer);
glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
@@ -82,6 +84,7 @@
mTextureUnit = 0;
mRegionMesh = NULL;
+ mMeshIndices = 0;
blend = false;
lastSrcMode = GL_ZERO;
@@ -94,7 +97,13 @@
debugOverdraw = false;
debugStencilClip = kStencilHide;
+ patchCache.init(*this);
+
mInitialized = true;
+
+ resetBoundTextures();
+
+ return true;
}
void Caches::initFont() {
@@ -132,6 +141,18 @@
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
+void Caches::initStaticProperties() {
+ gpuPixelBuffersEnabled = false;
+
+ // OpenGL ES 3.0+ specific features
+ if (mExtensions.hasPixelBufferObjects()) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "true") > 0) {
+ gpuPixelBuffersEnabled = !strcmp(property, "true");
+ }
+ }
+}
+
bool Caches::initProperties() {
bool prevDebugLayersUpdates = debugLayersUpdates;
bool prevDebugOverdraw = debugOverdraw;
@@ -147,7 +168,7 @@
if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) {
INIT_LOGD(" Overdraw debug enabled: %s", property);
- debugOverdraw = !strcmp(property, "true");
+ debugOverdraw = !strcmp(property, "show");
} else {
debugOverdraw = false;
}
@@ -191,8 +212,9 @@
glDeleteBuffers(1, &meshBuffer);
mCurrentBuffer = 0;
- glDeleteBuffers(1, &mRegionMeshIndices);
+ glDeleteBuffers(1, &mMeshIndices);
delete[] mRegionMesh;
+ mMeshIndices = 0;
mRegionMesh = NULL;
fboCache.clear();
@@ -200,6 +222,10 @@
programCache.clear();
currentProgram = NULL;
+ assetAtlas.terminate();
+
+ patchCache.clear();
+
mInitialized = false;
}
@@ -227,6 +253,8 @@
pathCache.getSize(), pathCache.getMaxSize());
log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
+ log.appendFormat(" PatchCache %8d / %8d\n",
+ patchCache.getSize(), patchCache.getMaxSize());
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
const uint32_t size = fontRenderer->getFontRendererSize(i);
log.appendFormat(" FontRenderer %d %8d / %8d\n", i, size, size);
@@ -234,8 +262,6 @@
log.appendFormat("Other:\n");
log.appendFormat(" FboCache %8d / %8d\n",
fboCache.getSize(), fboCache.getMaxSize());
- log.appendFormat(" PatchCache %8d / %8d\n",
- patchCache.getSize(), patchCache.getMaxSize());
uint32_t total = 0;
total += textureCache.getSize();
@@ -244,6 +270,7 @@
total += gradientCache.getSize();
total += pathCache.getSize();
total += dropShadowCache.getSize();
+ total += patchCache.getSize();
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
total += fontRenderer->getFontRendererSize(i);
}
@@ -259,6 +286,7 @@
void Caches::clearGarbage() {
textureCache.clearGarbage();
pathCache.clearGarbage();
+ patchCache.clearGarbage();
Vector<DisplayList*> displayLists;
Vector<Layer*> layers;
@@ -357,6 +385,32 @@
return false;
}
+bool Caches::bindIndicesBuffer() {
+ if (!mMeshIndices) {
+ uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6];
+ for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
+ uint16_t quad = i * 4;
+ int index = i * 6;
+ regionIndices[index ] = quad; // top-left
+ regionIndices[index + 1] = quad + 1; // top-right
+ regionIndices[index + 2] = quad + 2; // bottom-left
+ regionIndices[index + 3] = quad + 2; // bottom-left
+ regionIndices[index + 4] = quad + 1; // top-right
+ regionIndices[index + 5] = quad + 3; // bottom-right
+ }
+
+ glGenBuffers(1, &mMeshIndices);
+ bool force = bindIndicesBuffer(mMeshIndices);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t),
+ regionIndices, GL_STATIC_DRAW);
+
+ delete[] regionIndices;
+ return force;
+ }
+
+ return bindIndicesBuffer(mMeshIndices);
+}
+
bool Caches::unbindIndicesBuffer() {
if (mCurrentIndicesBuffer) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
@@ -441,6 +495,46 @@
}
}
+void Caches::bindTexture(GLuint texture) {
+ if (mBoundTextures[mTextureUnit] != texture) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+ mBoundTextures[mTextureUnit] = texture;
+ }
+}
+
+void Caches::bindTexture(GLenum target, GLuint texture) {
+ if (mBoundTextures[mTextureUnit] != texture) {
+ glBindTexture(target, texture);
+ mBoundTextures[mTextureUnit] = texture;
+ }
+}
+
+void Caches::deleteTexture(GLuint texture) {
+ // When glDeleteTextures() is called on a currently bound texture,
+ // OpenGL ES specifies that the texture is then considered unbound
+ // Consider the following series of calls:
+ //
+ // glGenTextures -> creates texture name 2
+ // glBindTexture(2)
+ // glDeleteTextures(2) -> 2 is now unbound
+ // glGenTextures -> can return 2 again
+ //
+ // If we don't call glBindTexture(2) after the second glGenTextures
+ // call, any texture operation will be performed on the default
+ // texture (name=0)
+
+ for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
+ if (mBoundTextures[i] == texture) {
+ mBoundTextures[i] = 0;
+ }
+ }
+ glDeleteTextures(1, &texture);
+}
+
+void Caches::resetBoundTextures() {
+ memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
+}
+
///////////////////////////////////////////////////////////////////////////////
// Scissor
///////////////////////////////////////////////////////////////////////////////
@@ -545,28 +639,7 @@
TextureVertex* Caches::getRegionMesh() {
// Create the mesh, 2 triangles and 4 vertices per rectangle in the region
if (!mRegionMesh) {
- mRegionMesh = new TextureVertex[REGION_MESH_QUAD_COUNT * 4];
-
- uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6];
- for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) {
- uint16_t quad = i * 4;
- int index = i * 6;
- regionIndices[index ] = quad; // top-left
- regionIndices[index + 1] = quad + 1; // top-right
- regionIndices[index + 2] = quad + 2; // bottom-left
- regionIndices[index + 3] = quad + 2; // bottom-left
- regionIndices[index + 4] = quad + 1; // top-right
- regionIndices[index + 5] = quad + 3; // bottom-right
- }
-
- glGenBuffers(1, &mRegionMeshIndices);
- bindIndicesBuffer(mRegionMeshIndices);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t),
- regionIndices, GL_STATIC_DRAW);
-
- delete[] regionIndices;
- } else {
- bindIndicesBuffer(mRegionMeshIndices);
+ mRegionMesh = new TextureVertex[gMaxNumberOfQuads * 4];
}
return mRegionMesh;
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 91b938b..b7a97ad 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -21,13 +21,18 @@
#define LOG_TAG "OpenGLRenderer"
#endif
+#include <GLES3/gl3.h>
+
+#include <utils/KeyedVector.h>
#include <utils/Singleton.h>
+#include <utils/Vector.h>
#include <cutils/compiler.h>
#include "thread/TaskProcessor.h"
#include "thread/TaskManager.h"
+#include "AssetAtlas.h"
#include "FontRenderer.h"
#include "GammaFontRenderer.h"
#include "TextureCache.h"
@@ -50,9 +55,11 @@
// Globals
///////////////////////////////////////////////////////////////////////////////
+// GL ES 2.0 defines that at least 16 texture units must be supported
#define REQUIRED_TEXTURE_UNITS_COUNT 3
-#define REGION_MESH_QUAD_COUNT 512
+// Maximum number of quads that pre-allocated meshes can draw
+static const uint32_t gMaxNumberOfQuads = 2048;
// Generates simple and textured vertices
#define FV(x, y, u, v) { { x, y }, { u, v } }
@@ -74,6 +81,7 @@
static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
static const GLsizei gMeshCount = 4;
+// Must define as many texture units as specified by REQUIRED_TEXTURE_UNITS_COUNT
static const GLenum gTextureUnits[] = {
GL_TEXTURE0,
GL_TEXTURE1,
@@ -113,7 +121,7 @@
/**
* Initialize caches.
*/
- void init();
+ bool init();
/**
* Initialize global system properties.
@@ -172,6 +180,11 @@
*/
bool unbindMeshBuffer();
+ /**
+ * Binds a global indices buffer that can draw up to
+ * gMaxNumberOfQuads quads.
+ */
+ bool bindIndicesBuffer();
bool bindIndicesBuffer(const GLuint buffer);
bool unbindIndicesBuffer();
@@ -213,6 +226,33 @@
void activeTexture(GLuint textureUnit);
/**
+ * Binds the specified texture as a GL_TEXTURE_2D texture.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLenum, GLuint).
+ */
+ void bindTexture(GLuint texture);
+
+ /**
+ * Binds the specified texture with the specified render target.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLuint).
+ */
+ void bindTexture(GLenum target, GLuint texture);
+
+ /**
+ * Deletes the specified texture and clears it from the cache
+ * of bound textures.
+ * All textures must be deleted using this method.
+ */
+ void deleteTexture(GLuint texture);
+
+ /**
+ * Signals that the cache of bound textures should be cleared.
+ * Other users of the context may have altered which textures are bound.
+ */
+ void resetBoundTextures();
+
+ /**
* Sets the scissor for the current surface.
*/
bool setScissor(GLint x, GLint y, GLint width, GLint height);
@@ -290,6 +330,10 @@
Dither dither;
Stencil stencil;
+ AssetAtlas assetAtlas;
+
+ bool gpuPixelBuffersEnabled;
+
// Debug methods
PFNGLINSERTEVENTMARKEREXTPROC eventMark;
PFNGLPUSHGROUPMARKEREXTPROC startMark;
@@ -302,6 +346,7 @@
void initFont();
void initExtensions();
void initConstraints();
+ void initStaticProperties();
static void eventMarkNull(GLsizei length, const GLchar* marker) { }
static void startMarkNull(GLsizei length, const GLchar* marker) { }
@@ -336,7 +381,9 @@
// Used to render layers
TextureVertex* mRegionMesh;
- GLuint mRegionMeshIndices;
+
+ // Global index buffer
+ GLuint mMeshIndices;
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
@@ -346,6 +393,8 @@
bool mInitialized;
uint32_t mFunctorsCount;
+
+ GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT];
}; // class Caches
}; // namespace uirenderer
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 790c4f4..786f12a 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -53,8 +53,6 @@
// Turn on to display debug info about 9patch objects
#define DEBUG_PATCHES 0
-// Turn on to "explode" 9patch objects
-#define DEBUG_EXPLODE_PATCHES 0
// Turn on to display vertex and tex coords data about 9patch objects
// This flag requires DEBUG_PATCHES to be turned on
#define DEBUG_PATCHES_VERTICES 0
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 9323a3a..7ce15c5 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -20,6 +20,8 @@
#include <SkCanvas.h>
#include <utils/Trace.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
#include "Caches.h"
#include "Debug.h"
@@ -51,19 +53,23 @@
public:
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0;
virtual ~Batch() {}
+ virtual bool purelyDrawBatch() { return false; }
+ virtual bool coversBounds(const Rect& bounds) { return false; }
};
class DrawBatch : public Batch {
public:
- DrawBatch(int batchId, mergeid_t mergeId) : mBatchId(batchId), mMergeId(mergeId) {
+ DrawBatch(const DeferInfo& deferInfo) : mAllOpsOpaque(true),
+ mBatchId(deferInfo.batchId), mMergeId(deferInfo.mergeId) {
mOps.clear();
}
virtual ~DrawBatch() { mOps.clear(); }
- void add(DrawOp* op) {
+ virtual void add(DrawOp* op, bool opaqueOverBounds) {
// NOTE: ignore empty bounds special case, since we don't merge across those ops
mBounds.unionWith(op->state.mBounds);
+ mAllOpsOpaque &= opaqueOverBounds;
mOps.add(op);
}
@@ -85,8 +91,8 @@
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
- DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
- index, this, mOps.size(), mOps[0]->getBatchId(), mOps[0]->getMergeId());
+ DEFER_LOGD("%d replaying DrawBatch %p, with %d ops (batch id %x, merge id %p)",
+ index, this, mOps.size(), getBatchId(), getMergeId());
status_t status = DrawGlInfo::kStatusDone;
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
@@ -114,14 +120,28 @@
return status;
}
+ virtual bool purelyDrawBatch() { return true; }
+
+ virtual bool coversBounds(const Rect& bounds) {
+ if (CC_LIKELY(!mAllOpsOpaque || !mBounds.contains(bounds) || count() == 1)) return false;
+
+ Region uncovered(android::Rect(bounds.left, bounds.top, bounds.right, bounds.bottom));
+ for (unsigned int i = 0; i < mOps.size(); i++) {
+ Rect &r = mOps[i]->state.mBounds;
+ uncovered.subtractSelf(android::Rect(r.left, r.top, r.right, r.bottom));
+ }
+ return uncovered.isEmpty();
+ }
+
inline int getBatchId() const { return mBatchId; }
inline mergeid_t getMergeId() const { return mMergeId; }
inline int count() const { return mOps.size(); }
protected:
Vector<DrawOp*> mOps;
- Rect mBounds;
+ Rect mBounds; // union of bounds of contained ops
private:
+ bool mAllOpsOpaque;
int mBatchId;
mergeid_t mMergeId;
};
@@ -132,21 +152,39 @@
class MergingDrawBatch : public DrawBatch {
public:
- MergingDrawBatch(int batchId, mergeid_t mergeId) : DrawBatch(batchId, mergeId) {}
+ MergingDrawBatch(DeferInfo& deferInfo, int width, int height) :
+ DrawBatch(deferInfo), mClipRect(width, height),
+ mClipSideFlags(kClipSide_None) {}
+
+ /*
+ * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
+ * and clip side flags. Positive bounds delta means new bounds fit in old.
+ */
+ static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
+ float boundsDelta) {
+ bool currentClipExists = currentFlags & side;
+ bool newClipExists = newFlags & side;
+
+ // if current is clipped, we must be able to fit new bounds in current
+ if (boundsDelta > 0 && currentClipExists) return false;
+
+ // if new is clipped, we must be able to fit current bounds in new
+ if (boundsDelta < 0 && newClipExists) return false;
+
+ return true;
+ }
/*
* Checks if a (mergeable) op can be merged into this batch
*
* If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
* important to consider all paint attributes used in the draw calls in deciding both a) if an
- * op tries to merge at all, and b) if the op
+ * op tries to merge at all, and b) if the op can merge with another set of ops
*
* False positives can lead to information from the paints of subsequent merged operations being
* dropped, so we make simplifying qualifications on the ops that can merge, per op type.
*/
bool canMergeWith(DrawOp* op) {
- if (!op->state.mMatrix.isPureTranslate()) return false;
-
bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
@@ -155,12 +193,32 @@
if (!isTextBatch || op->state.mDrawModifiers.mHasShadow) {
if (intersects(op->state.mBounds)) return false;
}
-
const DeferredDisplayState& lhs = op->state;
const DeferredDisplayState& rhs = mOps[0]->state;
if (NEQ_FALPHA(lhs.mAlpha, rhs.mAlpha)) return false;
+ /* Clipping compatibility check
+ *
+ * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
+ * clip for that side.
+ */
+ const int currentFlags = mClipSideFlags;
+ const int newFlags = op->state.mClipSideFlags;
+ if (currentFlags != kClipSide_None || newFlags != kClipSide_None) {
+ const Rect& opBounds = op->state.mBounds;
+ float boundsDelta = mBounds.left - opBounds.left;
+ if (!checkSide(currentFlags, newFlags, kClipSide_Left, boundsDelta)) return false;
+ boundsDelta = mBounds.top - opBounds.top;
+ if (!checkSide(currentFlags, newFlags, kClipSide_Top, boundsDelta)) return false;
+
+ // right and bottom delta calculation reversed to account for direction
+ boundsDelta = opBounds.right - mBounds.right;
+ if (!checkSide(currentFlags, newFlags, kClipSide_Right, boundsDelta)) return false;
+ boundsDelta = opBounds.bottom - mBounds.bottom;
+ if (!checkSide(currentFlags, newFlags, kClipSide_Bottom, boundsDelta)) return false;
+ }
+
// if paints are equal, then modifiers + paint attribs don't need to be compared
if (op->mPaint == mOps[0]->mPaint) return true;
@@ -178,7 +236,6 @@
*
* These ignore cases prevent us from simply memcmp'ing the drawModifiers
*/
-
const DrawModifiers& lhsMod = lhs.mDrawModifiers;
const DrawModifiers& rhsMod = rhs.mDrawModifiers;
if (lhsMod.mShader != rhsMod.mShader) return false;
@@ -192,13 +249,28 @@
return true;
}
+ virtual void add(DrawOp* op, bool opaqueOverBounds) {
+ DrawBatch::add(op, opaqueOverBounds);
+
+ const int newClipSideFlags = op->state.mClipSideFlags;
+ mClipSideFlags |= newClipSideFlags;
+ if (newClipSideFlags & kClipSide_Left) mClipRect.left = op->state.mClip.left;
+ if (newClipSideFlags & kClipSide_Top) mClipRect.top = op->state.mClip.top;
+ if (newClipSideFlags & kClipSide_Right) mClipRect.right = op->state.mClip.right;
+ if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = op->state.mClip.bottom;
+ }
+
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
- DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
- index, this, mOps.size(), getBatchId(), getMergeId());
+ DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops,"
+ " clip flags %x (batch id %x, merge id %p)",
+ index, this, mOps.size(), mClipSideFlags, getBatchId(), getMergeId());
if (mOps.size() == 1) {
- return DrawBatch::replay(renderer, dirty, false);
+ return DrawBatch::replay(renderer, dirty, -1);
}
+ // clipping in the merged case is done ahead of time since all ops share the clip (if any)
+ renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : NULL);
+
DrawOp* op = mOps[0];
DisplayListLogBuffer& buffer = DisplayListLogBuffer::getInstance();
buffer.writeCommand(0, "multiDraw");
@@ -211,6 +283,15 @@
#endif
return status;
}
+
+private:
+ /*
+ * Contains the effective clip rect shared by all merged ops. Initialized to the layer viewport,
+ * it will shrink if an op must be clipped on a certain side. The clipped sides are reflected in
+ * mClipSideFlags.
+ */
+ Rect mClipRect;
+ int mClipSideFlags;
};
class StateOpBatch : public Batch {
@@ -294,6 +375,7 @@
mBatches.clear();
mSaveStack.clear();
mEarliestBatchIndex = 0;
+ mEarliestUnclearedIndex = 0;
}
/////////////////////////////////////////////////////////////////////////////////
@@ -402,18 +484,26 @@
return; // quick rejected
}
- int batchId = kOpBatch_None;
- mergeid_t mergeId = (mergeid_t) -1;
- bool mergeable = op->onDefer(renderer, &batchId, &mergeId);
+ DeferInfo deferInfo;
+ op->onDefer(renderer, deferInfo);
// complex clip has a complex set of expectations on the renderer state - for now, avoid taking
// the merge path in those cases
- mergeable &= !recordingComplexClip();
+ deferInfo.mergeable &= !recordingComplexClip();
+ deferInfo.opaqueOverBounds &= !recordingComplexClip() && mSaveStack.isEmpty();
+
+ if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
+ op->state.mClipSideFlags != kClipSide_ConservativeFull &&
+ deferInfo.opaqueOverBounds && op->state.mBounds.contains(mBounds)) {
+ // avoid overdraw by resetting drawing state + discarding drawing ops
+ discardDrawingBatches(mBatches.size() - 1);
+ resetBatchingState();
+ }
if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
// TODO: elegant way to reuse batches?
- DrawBatch* b = new DrawBatch(batchId, mergeId);
- b->add(op);
+ DrawBatch* b = new DrawBatch(deferInfo);
+ b->add(op, deferInfo.opaqueOverBounds);
mBatches.add(b);
return;
}
@@ -427,8 +517,8 @@
if (!mBatches.isEmpty()) {
if (op->state.mBounds.isEmpty()) {
// don't know the bounds for op, so add to last batch and start from scratch on next op
- DrawBatch* b = new DrawBatch(batchId, mergeId);
- b->add(op);
+ DrawBatch* b = new DrawBatch(deferInfo);
+ b->add(op, deferInfo.opaqueOverBounds);
mBatches.add(b);
resetBatchingState();
#if DEBUG_DEFER
@@ -438,19 +528,19 @@
return;
}
- if (mergeable) {
+ if (deferInfo.mergeable) {
// Try to merge with any existing batch with same mergeId.
- if (mMergingBatches[batchId].get(mergeId, targetBatch)) {
+ if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op)) {
targetBatch = NULL;
}
}
} else {
// join with similar, non-merging batch
- targetBatch = (DrawBatch*)mBatchLookup[batchId];
+ targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId];
}
- if (targetBatch || mergeable) {
+ if (targetBatch || deferInfo.mergeable) {
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still interate to find similar batch to insert after
for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
@@ -459,7 +549,7 @@
if (overBatch == targetBatch) break;
// TODO: also consider shader shared between batch types
- if (batchId == overBatch->getBatchId()) {
+ if (deferInfo.batchId == overBatch->getBatchId()) {
insertBatchIndex = i + 1;
if (!targetBatch) break; // found insert position, quit
}
@@ -481,20 +571,22 @@
}
if (!targetBatch) {
- if (mergeable) {
- targetBatch = new MergingDrawBatch(batchId, mergeId);
- mMergingBatches[batchId].put(mergeId, targetBatch);
+ if (deferInfo.mergeable) {
+ targetBatch = new MergingDrawBatch(deferInfo,
+ renderer.getViewportWidth(), renderer.getViewportHeight());
+ mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
} else {
- targetBatch = new DrawBatch(batchId, mergeId);
- mBatchLookup[batchId] = targetBatch;
- DEFER_LOGD("creating Batch %p, bid %x, at %d",
- targetBatch, batchId, insertBatchIndex);
+ targetBatch = new DrawBatch(deferInfo);
+ mBatchLookup[deferInfo.batchId] = targetBatch;
}
+ DEFER_LOGD("creating %singBatch %p, bid %x, at %d",
+ deferInfo.mergeable ? "Merg" : "Draw",
+ targetBatch, deferInfo.batchId, insertBatchIndex);
mBatches.insertAt(targetBatch, insertBatchIndex);
}
- targetBatch->add(op);
+ targetBatch->add(op, deferInfo.opaqueOverBounds);
}
void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) {
@@ -526,7 +618,9 @@
status_t status = DrawGlInfo::kStatusDone;
for (unsigned int i = 0; i < batchList.size(); i++) {
- status |= batchList[i]->replay(renderer, dirty, i);
+ if (batchList[i]) {
+ status |= batchList[i]->replay(renderer, dirty, i);
+ }
}
DEFER_LOGD("--flushed, drew %d batches", batchList.size());
return status;
@@ -548,6 +642,13 @@
DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ if (CC_LIKELY(mAvoidOverdraw)) {
+ for (unsigned int i = 1; i < mBatches.size(); i++) {
+ if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
+ discardDrawingBatches(i - 1);
+ }
+ }
+ }
// NOTE: depth of the save stack at this point, before playback, should be reflected in
// FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly
status |= replayBatchList(mBatches, renderer, dirty);
@@ -560,5 +661,17 @@
return status;
}
+void DeferredDisplayList::discardDrawingBatches(const unsigned int maxIndex) {
+ for (unsigned int i = mEarliestUnclearedIndex; i <= maxIndex; i++) {
+ // leave deferred state ops alone for simplicity (empty save restore pairs may now exist)
+ if (mBatches[i] && mBatches[i]->purelyDrawBatch()) {
+ DrawBatch* b = (DrawBatch*) mBatches[i];
+ delete mBatches[i];
+ mBatches.replaceAt(NULL, i);
+ }
+ }
+ mEarliestUnclearedIndex = maxIndex + 1;
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 9782c1c..33feb7e 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -44,8 +44,12 @@
class DeferredDisplayList {
public:
- DeferredDisplayList() { clear(); }
+ DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
+ mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
+ clear();
+ }
~DeferredDisplayList() { clear(); }
+ void reset(const Rect& bounds) { mBounds.set(bounds); }
enum OpBatchId {
kOpBatch_None = 0, // Don't batch
@@ -96,6 +100,12 @@
int getStateOpDeferFlags() const;
int getDrawOpDeferFlags() const;
+ void discardDrawingBatches(const unsigned int maxIndex);
+
+ // layer space bounds of rendering
+ Rect mBounds;
+ const bool mAvoidOverdraw;
+
/**
* At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
* that when an associated restoreToCount is deferred, it can be recorded as a
@@ -112,6 +122,9 @@
// Points to the index after the most recent barrier
int mEarliestBatchIndex;
+ // Points to the first index that may contain a pure drawing batch
+ int mEarliestUnclearedIndex;
+
/**
* Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
* MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
@@ -120,6 +133,24 @@
TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
};
+/**
+ * Struct containing information that instructs the defer
+ */
+struct DeferInfo {
+public:
+ DeferInfo() :
+ batchId(DeferredDisplayList::kOpBatch_None),
+ mergeId((mergeid_t) -1),
+ mergeable(false),
+ opaqueOverBounds(false) {
+ };
+
+ int batchId;
+ mergeid_t mergeId;
+ bool mergeable;
+ bool opaqueOverBounds; // opaque over bounds in DeferredDisplayState - can skip ops below
+};
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 1cbd531..cebfd26 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -44,13 +44,14 @@
}
DisplayList::DisplayList(const DisplayListRenderer& recorder) :
- mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
+ mDestroyed(false), mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
mStaticMatrix(NULL), mAnimationMatrix(NULL) {
initFromDisplayListRenderer(recorder);
}
DisplayList::~DisplayList() {
+ mDestroyed = true;
clearResources();
}
@@ -63,7 +64,6 @@
void DisplayList::clearResources() {
mDisplayListData = NULL;
- mSize = 0; // TODO: shouldn't be needed, WAR possible use after delete
mClipRectOp = NULL;
mSaveLayerOp = NULL;
@@ -100,6 +100,10 @@
caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
}
+ for (size_t i = 0; i < mPatchResources.size(); i++) {
+ caches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i));
+ }
+
for (size_t i = 0; i < mShaders.size(); i++) {
caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
caches.resourceCache.destructorLocked(mShaders.itemAt(i));
@@ -134,6 +138,7 @@
mBitmapResources.clear();
mOwnedBitmapResources.clear();
mFilterResources.clear();
+ mPatchResources.clear();
mShaders.clear();
mSourcePaths.clear();
mPaints.clear();
@@ -169,6 +174,10 @@
mSaveLayerOp = new (alloc) SaveLayerOp();
mSaveOp = new (alloc) SaveOp();
mRestoreToCountOp = new (alloc) RestoreToCountOp();
+ if (CC_UNLIKELY(!mSaveOp)) { // temporary debug logging
+ ALOGW("Error: %s's SaveOp not allocated, size %d", getName(), mSize);
+ CRASH();
+ }
mFunctorCount = recorder.getFunctorCount();
@@ -197,6 +206,13 @@
caches.resourceCache.incrementRefcountLocked(resource);
}
+ const Vector<Res_png_9patch*>& patchResources = recorder.getPatchResources();
+ for (size_t i = 0; i < patchResources.size(); i++) {
+ Res_png_9patch* resource = patchResources.itemAt(i);
+ mPatchResources.add(resource);
+ caches.resourceCache.incrementRefcountLocked(resource);
+ }
+
const Vector<SkiaShader*>& shaders = recorder.getShaders();
for (size_t i = 0; i < shaders.size(); i++) {
SkiaShader* resource = shaders.itemAt(i);
@@ -352,22 +368,26 @@
level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
}
}
+
+ bool clipToBoundsNeeded = mClipToBounds;
if (mAlpha < 1) {
if (mCaching) {
ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mAlpha);
+ clipToBoundsNeeded = false; // clipping done by layer
} else if (!mHasOverlappingRendering) {
ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
} else {
int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (mClipToBounds) {
+ if (clipToBoundsNeeded) {
flags |= SkCanvas::kClipToLayer_SaveFlag;
+ clipToBoundsNeeded = false; // clipping done by save layer
}
ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
(float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
(int)(mAlpha * 255), flags);
}
}
- if (mClipToBounds && !mCaching) {
+ if (clipToBoundsNeeded) {
ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
(float) mRight - mLeft, (float) mBottom - mTop);
}
@@ -402,9 +422,11 @@
renderer.concatMatrix(mTransformMatrix);
}
}
+ bool clipToBoundsNeeded = mClipToBounds;
if (mAlpha < 1) {
if (mCaching) {
renderer.setOverrideLayerAlpha(mAlpha);
+ clipToBoundsNeeded = false; // clipping done by layer
} else if (!mHasOverlappingRendering) {
renderer.scaleAlpha(mAlpha);
} else {
@@ -412,15 +434,16 @@
// have to pass it into this call. In fact, this information might be in the
// location/size info that we store with the new native transform data.
int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (mClipToBounds) {
+ if (clipToBoundsNeeded) {
saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
+ clipToBoundsNeeded = false; // clipping done by saveLayer
}
handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop,
mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT,
mClipToBounds);
}
}
- if (mClipToBounds && !mCaching) {
+ if (clipToBoundsNeeded) {
handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op),
PROPERTY_SAVECOUNT, mClipToBounds);
}
@@ -480,7 +503,11 @@
*/
template <class T>
void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
- if (mSize == 0 || mAlpha <= 0 || CC_UNLIKELY(!mSaveOp)) { // TODO: shouldn't need mSaveOp check
+ if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
+ ALOGW("Error: %s is drawing after destruction, size %d", getName(), mSize);
+ CRASH();
+ }
+ if (mSize == 0 || mAlpha <= 0) {
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
return;
}
@@ -514,6 +541,10 @@
for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
DisplayListOp *op = mDisplayListData->displayListOps[i];
+#if DEBUG_DISPLAY_LIST
+ op->output(level + 1);
+#endif
+
logBuffer.writeCommand(level, op->name());
handler(op, saveCount, mClipToBounds);
}
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 5f84329..194be9e 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -30,8 +30,11 @@
#include <utils/SortedVector.h>
#include <utils/String8.h>
#include <utils/Vector.h>
+
#include <cutils/compiler.h>
+#include <androidfw/ResourceTypes.h>
+
#include "utils/LinearAllocator.h"
#include "Debug.h"
@@ -129,7 +132,12 @@
void setName(const char* name) {
if (name) {
- mName.setTo(name);
+ char* lastPeriod = strrchr(name, '.');
+ if (lastPeriod) {
+ mName.setTo(lastPeriod + 1);
+ } else {
+ mName.setTo(name);
+ }
}
}
@@ -479,6 +487,7 @@
Vector<SkBitmap*> mBitmapResources;
Vector<SkBitmap*> mOwnedBitmapResources;
Vector<SkiaColorFilter*> mFilterResources;
+ Vector<Res_png_9patch*> mPatchResources;
Vector<SkPaint*> mPaints;
Vector<SkPath*> mPaths;
@@ -496,6 +505,7 @@
uint32_t mFunctorCount;
String8 mName;
+ bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
// View properties
bool mClipToBounds;
@@ -525,11 +535,11 @@
* an alpha causes a SaveLayerAlpha to occur). These operations point into mDisplayListData's
* allocation, or null if uninitialized.
*
- * These are initialized (via friend constructors) when a displayList is issued in either replay
- * or deferred mode. If replaying, the ops are not used until the next frame. If deferring, the
- * ops may be stored in the DeferredDisplayList to be played back a second time.
+ * These are initialized (via friend re-constructors) when a displayList is issued in either
+ * replay or deferred mode. If replaying, the ops are not used until the next frame. If
+ * deferring, the ops may be stored in the DeferredDisplayList to be played back a second time.
*
- * They should be used at most once per frame (one call to iterate)
+ * They should be used at most once per frame (one call to 'iterate') to avoid overwriting data
*/
ClipRectOp* mClipRectOp;
SaveLayerOp* mSaveLayerOp;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index a0290e3..2b6f4cd 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -26,26 +26,19 @@
#include <private/hwui/DrawGlInfo.h>
#include "OpenGLRenderer.h"
+#include "AssetAtlas.h"
#include "DeferredDisplayList.h"
#include "DisplayListRenderer.h"
+#include "UvMapper.h"
#include "utils/LinearAllocator.h"
#define CRASH() do { \
- *(int *)(uintptr_t)0xbbadbeef = 0; \
+ *(int *)(uintptr_t) 0xbbadbeef = 0; \
((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \
} while(false)
-#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
-#define MATRIX_ARGS(m) \
- m->get(0), m->get(1), m->get(2), \
- m->get(3), m->get(4), m->get(5), \
- m->get(6), m->get(7), m->get(8)
-#define RECT_STRING "%.2f %.2f %.2f %.2f"
-#define RECT_ARGS(r) \
- r.left, r.top, r.right, r.bottom
-
// Use OP_LOG for logging with arglist, OP_LOGS if just printing char*
-#define OP_LOGS(s) OP_LOG("%s", s)
+#define OP_LOGS(s) OP_LOG("%s", (s))
#define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ )
namespace android {
@@ -84,7 +77,7 @@
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
bool useQuickReject) = 0;
- virtual void output(int level, uint32_t logFlags = 0) = 0;
+ virtual void output(int level, uint32_t logFlags = 0) const = 0;
// NOTE: it would be nice to declare constants and overriding the implementation in each op to
// point at the constants, but that seems to require a .cpp file
@@ -136,7 +129,10 @@
return;
}
- if (!getLocalBounds(state.mBounds)) {
+ if (getLocalBounds(state.mBounds)) {
+ // valid empty bounds, don't bother deferring
+ if (state.mBounds.isEmpty()) return;
+ } else {
// empty bounds signify bounds can't be calculated
state.mBounds.setEmpty();
}
@@ -166,13 +162,13 @@
const Vector<DrawOp*>& ops, const Rect& bounds) {
status_t status = DrawGlInfo::kStatusDone;
for (unsigned int i = 0; i < ops.size(); i++) {
- renderer.restoreDisplayState(ops[i]->state);
+ renderer.restoreDisplayState(ops[i]->state, true);
status |= ops[i]->applyDraw(renderer, dirty);
}
return status;
}
- /*
+ /**
* When this method is invoked the state field is initialized to have the
* final rendering state. We can thus use it to process data as it will be
* used at draw time.
@@ -180,12 +176,9 @@
* Additionally, this method allows subclasses to provide defer-time preferences for batching
* and merging.
*
- * Return true if the op can merge with others of its kind (such subclasses should implement
- * multiDraw)
+ * if a subclass can set deferInfo.mergeable to true, it should implement multiDraw()
*/
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- return false;
- }
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {}
// returns true if bounds exist
virtual bool getLocalBounds(Rect& localBounds) { return false; }
@@ -209,6 +202,23 @@
return renderer.filterPaint(mPaint);
}
+ // Helper method for determining op opaqueness. Assumes op fills its bounds in local
+ // coordinates, and that paint's alpha is used
+ inline bool isOpaqueOverBounds() {
+ // ensure that local bounds cover mapped bounds
+ 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;
+
+ SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
+ return (mode == SkXfermode::kSrcOver_Mode ||
+ mode == SkXfermode::kSrc_Mode);
+
+ }
+
SkPaint* mPaint; // should be accessed via getPaint() when applying
bool mQuickRejected;
};
@@ -218,6 +228,9 @@
DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
+ DrawBoundedOp(const Rect& localBounds, SkPaint* paint)
+ : DrawOp(paint), mLocalBounds(localBounds) {}
+
// Calculates bounds as smallest rect encompassing all points
// NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in
// subclass' constructor)
@@ -240,16 +253,6 @@
return true;
}
- bool mergeAllowed() {
- if (!state.mMatrix.isPureTranslate()) return false;
-
- // checks that we're unclipped, and srcover
- const Rect& opBounds = state.mBounds;
- return fabs(opBounds.getWidth() - mLocalBounds.getWidth()) < 0.1 &&
- fabs(opBounds.getHeight() - mLocalBounds.getHeight()) < 0.1 &&
- (OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode);
- }
-
protected:
Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint
};
@@ -275,7 +278,7 @@
renderer.save(mFlags);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Save flags %x", mFlags);
}
@@ -309,7 +312,7 @@
renderer.restoreToCount(saveCount + mCount);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Restore to count %d", mCount);
}
@@ -348,7 +351,7 @@
renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mMode, mFlags);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SaveLayer%s of area " RECT_STRING,
(isSaveLayerAlpha() ? "Alpha" : ""),RECT_ARGS(mArea));
}
@@ -369,7 +372,7 @@
return this;
}
- bool isSaveLayerAlpha() { return mAlpha < 255 && mMode == SkXfermode::kSrcOver_Mode; }
+ bool isSaveLayerAlpha() const { return mAlpha < 255 && mMode == SkXfermode::kSrcOver_Mode; }
Rect mArea;
int mAlpha;
SkXfermode::Mode mMode;
@@ -385,7 +388,7 @@
renderer.translate(mDx, mDy);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Translate by %f %f", mDx, mDy);
}
@@ -405,7 +408,7 @@
renderer.rotate(mDegrees);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Rotate by %f degrees", mDegrees);
}
@@ -424,7 +427,7 @@
renderer.scale(mSx, mSy);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Scale by %f %f", mSx, mSy);
}
@@ -444,7 +447,7 @@
renderer.skew(mSx, mSy);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Skew by %f %f", mSx, mSy);
}
@@ -464,7 +467,7 @@
renderer.setMatrix(mMatrix);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
}
@@ -483,7 +486,7 @@
renderer.concatMatrix(mMatrix);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("ConcatMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
}
@@ -527,7 +530,7 @@
renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea));
}
@@ -556,7 +559,7 @@
renderer.clipPath(mPath, mOp);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
SkRect bounds = mPath->getBounds();
OP_LOG("ClipPath bounds " RECT_STRING,
bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
@@ -577,7 +580,7 @@
renderer.clipRegion(mRegion, mOp);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
SkIRect bounds = mRegion->getBounds();
OP_LOG("ClipRegion bounds %d %d %d %d",
bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
@@ -596,7 +599,7 @@
renderer.resetShader();
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOGS("ResetShader");
}
@@ -611,7 +614,7 @@
renderer.setupShader(mShader);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SetupShader, shader %p", mShader);
}
@@ -627,7 +630,7 @@
renderer.resetColorFilter();
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOGS("ResetColorFilter");
}
@@ -643,7 +646,7 @@
renderer.setupColorFilter(mColorFilter);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SetupColorFilter, filter %p", mColorFilter);
}
@@ -659,7 +662,7 @@
renderer.resetShadow();
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOGS("ResetShadow");
}
@@ -675,7 +678,7 @@
renderer.setupShadow(mRadius, mDx, mDy, mColor);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SetupShadow, radius %f, %f, %f, color %#x", mRadius, mDx, mDy, mColor);
}
@@ -694,7 +697,7 @@
renderer.resetPaintFilter();
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOGS("ResetPaintFilter");
}
@@ -710,7 +713,7 @@
renderer.setupPaintFilter(mClearBits, mSetBits);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SetupPaintFilter, clear %#x, set %#x", mClearBits, mSetBits);
}
@@ -721,7 +724,6 @@
int mSetBits;
};
-
///////////////////////////////////////////////////////////////////////////////
// DRAW OPERATIONS - these are operations that can draw to the canvas's device
///////////////////////////////////////////////////////////////////////////////
@@ -729,34 +731,60 @@
class DrawBitmapOp : public DrawBoundedOp {
public:
DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
- : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(),
- paint),
- mBitmap(bitmap) {}
+ : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), paint),
+ mBitmap(bitmap), mAtlas(Caches::getInstance().assetAtlas) {
+ mEntry = mAtlas.getEntry(bitmap);
+ if (mEntry) {
+ mEntryGenerationId = mAtlas.getGenerationId();
+ mUvMapper = mEntry->uvMapper;
+ }
+ }
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top,
getPaint(renderer));
}
+ AssetAtlas::Entry* getAtlasEntry() {
+ // The atlas entry is stale, let's get a new one
+ if (mEntry && mEntryGenerationId != mAtlas.getGenerationId()) {
+ mEntryGenerationId = mAtlas.getGenerationId();
+ mEntry = mAtlas.getEntry(mBitmap);
+ mUvMapper = mEntry->uvMapper;
+ }
+ return mEntry;
+ }
+
#define SET_TEXTURE(ptr, posRect, offsetRect, texCoordsRect, xDim, yDim) \
TextureVertex::set(ptr++, posRect.xDim - offsetRect.left, posRect.yDim - offsetRect.top, \
texCoordsRect.xDim, texCoordsRect.yDim)
+ /**
+ * This multi-draw operation builds a mesh on the stack by generating a quad
+ * for each bitmap in the batch. This method is also responsible for dirtying
+ * the current layer, if any.
+ */
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
const Vector<DrawOp*>& ops, const Rect& bounds) {
renderer.restoreDisplayState(state, true); // restore all but the clip
- renderer.setFullScreenClip(); // ensure merged ops aren't clipped
TextureVertex vertices[6 * ops.size()];
TextureVertex* vertex = &vertices[0];
- // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, and allowing
- // them to be merged in getBatchId()
- const Rect texCoords(0, 0, 1, 1);
+ const bool hasLayer = renderer.hasLayer();
+ bool transformed = false;
- const float width = mBitmap->width();
- const float height = mBitmap->height();
+ // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op,
+ // and allowing them to be merged in getBatchId()
for (unsigned int i = 0; i < ops.size(); i++) {
const Rect& opBounds = ops[i]->state.mBounds;
+ // When we reach multiDraw(), the matrix can be either
+ // pureTranslate or simple (translate and/or scale).
+ // If the matrix is not pureTranslate, then we have a scale
+ if (!ops[i]->state.mMatrix.isPureTranslate()) transformed = true;
+
+ Rect texCoords(0, 0, 1, 1);
+ ((DrawBitmapOp*) ops[i])->mUvMapper.map(texCoords);
+
SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
@@ -764,29 +792,42 @@
SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
+
+ if (hasLayer) {
+ const Rect& dirty = ops[i]->state.mBounds;
+ renderer.dirtyLayer(dirty.left, dirty.top, dirty.right, dirty.bottom);
+ }
}
- return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0], bounds, mPaint);
+ return renderer.drawBitmaps(mBitmap, mEntry, ops.size(), &vertices[0],
+ transformed, bounds, mPaint);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top);
}
virtual const char* name() { return "DrawBitmap"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- *mergeId = (mergeid_t)mBitmap;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ deferInfo.mergeId = getAtlasEntry() ? (mergeid_t) &mEntry->atlas : (mergeid_t) mBitmap;
- // don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
- // MergingDrawBatch::canMergeWith
- return mergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
+ // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
+ // MergingDrawBatch::canMergeWith()
+ // TODO: support clipped bitmaps by handling them in SET_TEXTURE
+ deferInfo.mergeable = state.mMatrix.isSimple() && !state.mClipSideFlags &&
+ OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
+ (mBitmap->getConfig() != SkBitmap::kA8_Config);
}
const SkBitmap* bitmap() { return mBitmap; }
protected:
SkBitmap* mBitmap;
+ const AssetAtlas& mAtlas;
+ uint32_t mEntryGenerationId;
+ AssetAtlas::Entry* mEntry;
+ UvMapper mUvMapper;
};
class DrawBitmapMatrixOp : public DrawBoundedOp {
@@ -802,15 +843,14 @@
return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
}
virtual const char* name() { return "DrawBitmapMatrix"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
private:
@@ -831,16 +871,15 @@
getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawBitmapRect"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
private:
@@ -858,15 +897,14 @@
mLocalBounds.top, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p", mBitmap);
}
virtual const char* name() { return "DrawBitmapData"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
};
@@ -883,15 +921,14 @@
mVertices, mColors, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p mesh %d x %d", mBitmap, mMeshWidth, mMeshHeight);
}
virtual const char* name() { return "DrawBitmapMesh"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
private:
@@ -904,45 +941,145 @@
class DrawPatchOp : public DrawBoundedOp {
public:
- DrawPatchOp(SkBitmap* bitmap, const int32_t* xDivs,
- const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height,
- int8_t numColors, float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode)
- : DrawBoundedOp(left, top, right, bottom, 0),
- mBitmap(bitmap), mxDivs(xDivs), myDivs(yDivs),
- mColors(colors), mxDivsCount(width), myDivsCount(height),
- mNumColors(numColors), mAlpha(alpha), mMode(mode) {};
+ DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch,
+ float left, float top, float right, float bottom, SkPaint* paint)
+ : DrawBoundedOp(left, top, right, bottom, paint),
+ mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(NULL),
+ mAtlas(Caches::getInstance().assetAtlas) {
+ mEntry = mAtlas.getEntry(bitmap);
+ if (mEntry) {
+ mEntryGenerationId = mAtlas.getGenerationId();
+ }
+ };
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- // NOTE: not calling the virtual method, which takes a paint
- return renderer.drawPatch(mBitmap, mxDivs, myDivs, mColors,
- mxDivsCount, myDivsCount, mNumColors,
- mLocalBounds.left, mLocalBounds.top,
- mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
+ AssetAtlas::Entry* getAtlasEntry() {
+ // The atlas entry is stale, let's get a new one
+ if (mEntry && mEntryGenerationId != mAtlas.getGenerationId()) {
+ mEntryGenerationId = mAtlas.getGenerationId();
+ mEntry = mAtlas.getEntry(mBitmap);
+ }
+ return mEntry;
}
- virtual void output(int level, uint32_t logFlags) {
+ const Patch* getMesh(OpenGLRenderer& renderer) {
+ if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) {
+ PatchCache& cache = renderer.getCaches().patchCache;
+ mMesh = cache.get(getAtlasEntry(), mBitmap->width(), mBitmap->height(),
+ mLocalBounds.getWidth(), mLocalBounds.getHeight(), mPatch);
+ mGenerationId = cache.getGenerationId();
+ }
+ return mMesh;
+ }
+
+ /**
+ * This multi-draw operation builds an indexed mesh on the stack by copying
+ * and transforming the vertices of each 9-patch in the batch. This method
+ * is also responsible for dirtying the current layer, if any.
+ */
+ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<DrawOp*>& ops, const Rect& bounds) {
+ renderer.restoreDisplayState(state, true);
+
+ // Batches will usually contain a small number of items so it's
+ // worth performing a first iteration to count the exact number
+ // of vertices we need in the new mesh
+ uint32_t totalVertices = 0;
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ totalVertices += ((DrawPatchOp*) ops[i])->getMesh(renderer)->verticesCount;
+ }
+
+ const bool hasLayer = renderer.hasLayer();
+
+ uint32_t indexCount = 0;
+
+ TextureVertex vertices[totalVertices];
+ TextureVertex* vertex = &vertices[0];
+
+ // Create a mesh that contains the transformed vertices for all the
+ // 9-patch objects that are part of the batch. Note that onDefer()
+ // enforces ops drawn by this function to have a pure translate or
+ // identity matrix
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ DrawPatchOp* patchOp = (DrawPatchOp*) ops[i];
+ const Patch* opMesh = patchOp->getMesh(renderer);
+ uint32_t vertexCount = opMesh->verticesCount;
+ if (vertexCount == 0) continue;
+
+ // We use the bounds to know where to translate our vertices
+ // Using patchOp->state.mBounds wouldn't work because these
+ // bounds are clipped
+ const float tx = (int) floorf(patchOp->state.mMatrix.getTranslateX() +
+ patchOp->mLocalBounds.left + 0.5f);
+ const float ty = (int) floorf(patchOp->state.mMatrix.getTranslateY() +
+ patchOp->mLocalBounds.top + 0.5f);
+
+ // Copy & transform all the vertices for the current operation
+ TextureVertex* opVertices = opMesh->vertices;
+ for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
+ TextureVertex::set(vertex++,
+ opVertices->position[0] + tx, opVertices->position[1] + ty,
+ opVertices->texture[0], opVertices->texture[1]);
+ }
+
+ // Dirty the current layer if possible. When the 9-patch does not
+ // contain empty quads we can take a shortcut and simply set the
+ // dirty rect to the object's bounds.
+ if (hasLayer) {
+ if (!opMesh->hasEmptyQuads) {
+ renderer.dirtyLayer(tx, ty,
+ tx + patchOp->mLocalBounds.getWidth(),
+ ty + patchOp->mLocalBounds.getHeight());
+ } else {
+ const size_t count = opMesh->quads.size();
+ for (size_t i = 0; i < count; i++) {
+ const Rect& quadBounds = opMesh->quads[i];
+ const float x = tx + quadBounds.left;
+ const float y = ty + quadBounds.top;
+ renderer.dirtyLayer(x, y,
+ x + quadBounds.getWidth(), y + quadBounds.getHeight());
+ }
+ }
+ }
+
+ indexCount += opMesh->indexCount;
+ }
+
+ return renderer.drawPatches(mBitmap, getAtlasEntry(),
+ &vertices[0], indexCount, getPaint(renderer));
+ }
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+ // We're not calling the public variant of drawPatch() here
+ // This method won't perform the quickReject() since we've already done it at this point
+ return renderer.drawPatch(mBitmap, getMesh(renderer), getAtlasEntry(),
+ mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
+ getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawPatch"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Patch;
- *mergeId = (mergeid_t)mBitmap;
- return true;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
+ deferInfo.mergeId = getAtlasEntry() ? (mergeid_t) &mEntry->atlas : (mergeid_t) mBitmap;
+ deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
+ OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ deferInfo.opaqueOverBounds = isOpaqueOverBounds() && mBitmap->isOpaque();
}
private:
SkBitmap* mBitmap;
- const int32_t* mxDivs;
- const int32_t* myDivs;
- const uint32_t* mColors;
- uint32_t mxDivsCount;
- uint32_t myDivsCount;
- int8_t mNumColors;
- int mAlpha;
- SkXfermode::Mode mMode;
+ Res_png_9patch* mPatch;
+
+ uint32_t mGenerationId;
+ const Patch* mMesh;
+
+ const AssetAtlas& mAtlas;
+ uint32_t mEntryGenerationId;
+ AssetAtlas::Entry* mEntry;
};
class DrawColorOp : public DrawOp {
@@ -954,7 +1091,7 @@
return renderer.drawColor(mColor, mMode);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw color %#x, mode %d", mColor, mMode);
}
@@ -978,15 +1115,14 @@
return true;
}
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
if (mPaint->getPathEffect()) {
- *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
} else {
- *batchId = mPaint->isAntiAlias() ?
+ deferInfo.batchId = mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
}
- return false;
}
};
@@ -1000,10 +1136,16 @@
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
}
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
+ DrawStrokableOp::onDefer(renderer, deferInfo);
+ deferInfo.opaqueOverBounds = isOpaqueOverBounds() &&
+ mPaint->getStyle() == SkPaint::kFill_Style;
+ }
+
virtual const char* name() { return "DrawRect"; }
};
@@ -1017,15 +1159,14 @@
return renderer.drawRects(mRects, mCount, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Rects count %d", mCount);
}
virtual const char* name() { return "DrawRects"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Vertices;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Vertices;
}
private:
@@ -1044,7 +1185,7 @@
mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
}
@@ -1065,7 +1206,7 @@
return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Circle x %f, y %f, r %f", mX, mY, mRadius);
}
@@ -1087,7 +1228,7 @@
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
}
@@ -1107,7 +1248,7 @@
mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
}
@@ -1136,15 +1277,14 @@
return renderer.drawPath(mPath, getPaint(renderer));
}
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
SkPaint* paint = getPaint(renderer);
renderer.getCaches().pathCache.precache(mPath, paint);
- *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
- return false;
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
}
@@ -1166,17 +1306,16 @@
return renderer.drawLines(mPoints, mCount, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Lines count %d", mCount);
}
virtual const char* name() { return "DrawLines"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = mPaint->isAntiAlias() ?
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
+ deferInfo.batchId = mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
- return false;
}
protected:
@@ -1193,7 +1332,7 @@
return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Points count %d", mCount);
}
@@ -1205,20 +1344,18 @@
DrawSomeTextOp(const char* text, int bytesCount, int count, SkPaint* paint)
: DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw some text, %d bytes", mBytesCount);
}
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
fontRenderer.precache(paint, mText, mCount, mat4::identity());
- *batchId = mPaint->getColor() == 0xff000000 ?
+ deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
-
- return false;
}
protected:
@@ -1270,27 +1407,13 @@
class DrawTextOp : public DrawBoundedOp {
public:
DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length)
- : DrawBoundedOp(paint), mText(text), mBytesCount(bytesCount), mCount(count),
- mX(x), mY(y), mPositions(positions), mLength(length) {
- // duplicates bounds calculation from OpenGLRenderer::drawText, but doesn't alter mX
- SkPaint::FontMetrics metrics;
- paint->getFontMetrics(&metrics, 0.0f);
- switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
- x -= length / 2.0f;
- break;
- case SkPaint::kRight_Align:
- x -= length;
- break;
- default:
- break;
- }
- mLocalBounds.set(x, mY + metrics.fTop, x + length, mY + metrics.fBottom);
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds)
+ : DrawBoundedOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count),
+ mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) {
memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
}
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
const mat4& transform = renderer.findBestFontTransform(state.mMatrix);
@@ -1298,39 +1421,40 @@
fontRenderer.precache(paint, mText, mCount, transform);
mPrecacheTransform = transform;
}
- *batchId = mPaint->getColor() == 0xff000000 ?
+ deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
- *mergeId = (mergeid_t)mPaint->getColor();
+ deferInfo.mergeId = (mergeid_t)mPaint->getColor();
// don't merge decorated text - the decorations won't draw in order
bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag |
SkPaint::kStrikeThruText_Flag));
- return mergeAllowed() && noDecorations;
+ deferInfo.mergeable = state.mMatrix.isPureTranslate() && noDecorations &&
+ OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
- mPositions, getPaint(renderer), mLength);
+ mPositions, getPaint(renderer), mTotalAdvance, mLocalBounds);
}
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
const Vector<DrawOp*>& ops, const Rect& bounds) {
status_t status = DrawGlInfo::kStatusDone;
- renderer.setFullScreenClip(); // ensure merged ops aren't clipped
for (unsigned int i = 0; i < ops.size(); i++) {
DrawOpMode drawOpMode = (i == ops.size() - 1) ? kDrawOpMode_Flush : kDrawOpMode_Defer;
renderer.restoreDisplayState(ops[i]->state, true); // restore all but the clip
DrawTextOp& op = *((DrawTextOp*)ops[i]);
status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
- op.mPositions, op.getPaint(renderer), op.mLength, drawOpMode);
+ op.mPositions, op.getPaint(renderer), op.mTotalAdvance, op.mLocalBounds,
+ drawOpMode);
}
return status;
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
}
@@ -1343,7 +1467,7 @@
float mX;
float mY;
const float* mPositions;
- float mLength;
+ float mTotalAdvance;
mat4 mPrecacheTransform;
};
@@ -1363,7 +1487,7 @@
return ret;
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Functor %p", mFunctor);
}
@@ -1397,7 +1521,7 @@
return DrawGlInfo::kStatusDone;
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
if (mDisplayList && (logFlags & kOpLogFlag_Recurse)) {
mDisplayList->output(level + 1);
@@ -1420,7 +1544,7 @@
return renderer.drawLayer(mLayer, mX, mY);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY);
}
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 876c38a..ba4c2a0 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -57,6 +57,10 @@
mCaches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
}
+ for (size_t i = 0; i < mPatchResources.size(); i++) {
+ mCaches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i));
+ }
+
for (size_t i = 0; i < mShaders.size(); i++) {
mCaches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
}
@@ -74,6 +78,7 @@
mBitmapResources.clear();
mOwnedBitmapResources.clear();
mFilterResources.clear();
+ mPatchResources.clear();
mSourcePaths.clear();
mShaders.clear();
@@ -313,20 +318,13 @@
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs,
- const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height,
- int8_t numColors, float left, float top, float right, float bottom, SkPaint* paint) {
- int alpha;
- SkXfermode::Mode mode;
- OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
-
+status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
+ float left, float top, float right, float bottom, SkPaint* paint) {
bitmap = refBitmap(bitmap);
- xDivs = refBuffer<int>(xDivs, width);
- yDivs = refBuffer<int>(yDivs, height);
- colors = refBuffer<uint32_t>(colors, numColors);
+ patch = refPatch(patch);
+ paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawPatchOp(bitmap, xDivs, yDivs, colors, width, height, numColors,
- left, top, right, bottom, alpha, mode));
+ addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, paint));
return DrawGlInfo::kStatusDone;
}
@@ -423,17 +421,16 @@
status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, const float* positions, SkPaint* paint,
- float length, DrawOpMode drawOpMode) {
+ float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
- if (length < 0.0f) length = paint->measureText(text, bytesCount);
-
text = refText(text, bytesCount);
positions = refBuffer<float>(positions, count * 2);
paint = refPaint(paint);
- DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, x, y, positions, paint, length);
+ DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, paint, totalAdvance, bounds);
addDrawOp(op);
return DrawGlInfo::kStatusDone;
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 75abad6..03f50c8 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -103,8 +103,7 @@
virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint);
virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
float* vertices, int* colors, SkPaint* paint);
- virtual status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
float left, float top, float right, float bottom, SkPaint* paint);
virtual status_t drawColor(int color, SkXfermode::Mode mode);
virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
@@ -122,7 +121,8 @@
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length, DrawOpMode drawOpMode);
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
+ DrawOpMode drawOpMode);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
@@ -156,6 +156,10 @@
return mFilterResources;
}
+ const Vector<Res_png_9patch*>& getPatchResources() const {
+ return mPatchResources;
+ }
+
const Vector<SkiaShader*>& getShaders() const {
return mShaders;
}
@@ -315,9 +319,16 @@
return colorFilter;
}
+ inline Res_png_9patch* refPatch(Res_png_9patch* patch) {
+ mPatchResources.add(patch);
+ mCaches.resourceCache.incrementRefcount(patch);
+ return patch;
+ }
+
Vector<SkBitmap*> mBitmapResources;
Vector<SkBitmap*> mOwnedBitmapResources;
Vector<SkiaColorFilter*> mFilterResources;
+ Vector<Res_png_9patch*> mPatchResources;
Vector<SkPaint*> mPaints;
DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap;
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 19b3849..77006a4 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -24,12 +24,15 @@
// Lifecycle
///////////////////////////////////////////////////////////////////////////////
+Dither::Dither(): mCaches(NULL), mInitialized(false), mDitherTexture(0) {
+}
+
void Dither::bindDitherTexture() {
if (!mInitialized) {
- bool useFloatTexture = Extensions::getInstance().getMajorGlVersion() >= 3;
+ bool useFloatTexture = Extensions::getInstance().hasFloatTextures();
glGenTextures(1, &mDitherTexture);
- glBindTexture(GL_TEXTURE_2D, mDitherTexture);
+ mCaches->bindTexture(mDitherTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -68,13 +71,13 @@
mInitialized = true;
} else {
- glBindTexture(GL_TEXTURE_2D, mDitherTexture);
+ mCaches->bindTexture(mDitherTexture);
}
}
void Dither::clear() {
if (mInitialized) {
- glDeleteTextures(1, &mDitherTexture);
+ mCaches->deleteTexture(mDitherTexture);
mInitialized = false;
}
}
@@ -84,8 +87,10 @@
///////////////////////////////////////////////////////////////////////////////
void Dither::setupProgram(Program* program, GLuint* textureUnit) {
+ if (!mCaches) mCaches = &Caches::getInstance();
+
GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+ mCaches->activeTexture(textureSlot);
bindDitherTexture();
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 4d1f921..546236be 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -24,9 +24,7 @@
namespace android {
namespace uirenderer {
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
+class Caches;
// Must be a power of two
#define DITHER_KERNEL_SIZE 4
@@ -39,7 +37,7 @@
*/
class Dither {
public:
- Dither(): mInitialized(false), mDitherTexture(0) { }
+ Dither();
void clear();
void setupProgram(Program* program, GLuint* textureUnit);
@@ -47,6 +45,7 @@
private:
void bindDitherTexture();
+ Caches* mCaches;
bool mInitialized;
GLuint mDitherTexture;
};
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 51aec8d..218c18e 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -14,8 +14,19 @@
* limitations under the License.
*/
+#define LOG_TAG "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <utils/Log.h>
+
#include "Debug.h"
#include "Extensions.h"
+#include "Properties.h"
namespace android {
@@ -40,33 +51,28 @@
///////////////////////////////////////////////////////////////////////////////
Extensions::Extensions(): Singleton<Extensions>() {
- const char* buffer = (const char*) glGetString(GL_EXTENSIONS);
- const char* current = buffer;
- const char* head = current;
- EXT_LOGD("Available GL extensions:");
- do {
- head = strchr(current, ' ');
- String8 s(current, head ? head - current : strlen(current));
- if (s.length()) {
- mExtensionList.add(s);
- EXT_LOGD(" %s", s.string());
- }
- current = head + 1;
- } while (head);
+ // Query GL extensions
+ findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList);
+ mHasNPot = hasGlExtension("GL_OES_texture_npot");
+ mHasFramebufferFetch = hasGlExtension("GL_NV_shader_framebuffer_fetch");
+ mHasDiscardFramebuffer = hasGlExtension("GL_EXT_discard_framebuffer");
+ mHasDebugMarker = hasGlExtension("GL_EXT_debug_marker");
+ mHasDebugLabel = hasGlExtension("GL_EXT_debug_label");
+ mHasTiledRendering = hasGlExtension("GL_QCOM_tiled_rendering");
+ mHas1BitStencil = hasGlExtension("GL_OES_stencil1");
+ mHas4BitStencil = hasGlExtension("GL_OES_stencil4");
- mHasNPot = hasExtension("GL_OES_texture_npot");
- mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
- mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer");
- mHasDebugMarker = hasExtension("GL_EXT_debug_marker");
- mHasDebugLabel = hasExtension("GL_EXT_debug_label");
- mHasTiledRendering = hasExtension("GL_QCOM_tiled_rendering");
- mHas1BitStencil = hasExtension("GL_OES_stencil1");
- mHas4BitStencil = hasExtension("GL_OES_stencil4");
+ // Query EGL extensions
+ findExtensions(eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS), mEglExtensionList);
- mExtensions = strdup(buffer);
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_DEBUG_NV_PROFILING, property, NULL) > 0) {
+ mHasNvSystemTime = !strcmp(property, "true") && hasEglExtension("EGL_NV_system_time");
+ } else {
+ mHasNvSystemTime = false;
+ }
const char* version = (const char*) glGetString(GL_VERSION);
- mVersion = strdup(version);
// Section 6.1.5 of the OpenGL ES specification indicates the GL version
// string strictly follows this format:
@@ -88,22 +94,41 @@
}
Extensions::~Extensions() {
- free(mExtensions);
- free(mVersion);
}
///////////////////////////////////////////////////////////////////////////////
// Methods
///////////////////////////////////////////////////////////////////////////////
-bool Extensions::hasExtension(const char* extension) const {
+bool Extensions::hasGlExtension(const char* extension) const {
const String8 s(extension);
- return mExtensionList.indexOf(s) >= 0;
+ return mGlExtensionList.indexOf(s) >= 0;
+}
+
+bool Extensions::hasEglExtension(const char* extension) const {
+ const String8 s(extension);
+ return mEglExtensionList.indexOf(s) >= 0;
+}
+
+void Extensions::findExtensions(const char* extensions, SortedVector<String8>& list) const {
+ const char* current = extensions;
+ const char* head = current;
+ EXT_LOGD("Available extensions:");
+ do {
+ head = strchr(current, ' ');
+ String8 s(current, head ? head - current : strlen(current));
+ if (s.length()) {
+ list.add(s);
+ EXT_LOGD(" %s", s.string());
+ }
+ current = head + 1;
+ } while (head);
}
void Extensions::dump() const {
- ALOGD("%s", mVersion);
- ALOGD("Supported extensions:\n%s", mExtensions);
+ ALOGD("%s", (const char*) glGetString(GL_VERSION));
+ ALOGD("Supported GL extensions:\n%s", (const char*) glGetString(GL_EXTENSIONS));
+ ALOGD("Supported EGL extensions:\n%s", eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS));
}
}; // namespace uirenderer
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 54a3987..5e574a6 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -17,13 +17,12 @@
#ifndef ANDROID_HWUI_EXTENSIONS_H
#define ANDROID_HWUI_EXTENSIONS_H
+#include <cutils/compiler.h>
+
#include <utils/Singleton.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
namespace android {
namespace uirenderer {
@@ -31,11 +30,8 @@
// Classes
///////////////////////////////////////////////////////////////////////////////
-class Extensions: public Singleton<Extensions> {
+class ANDROID_API Extensions: public Singleton<Extensions> {
public:
- Extensions();
- ~Extensions();
-
inline bool hasNPot() const { return mHasNPot; }
inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; }
@@ -44,21 +40,30 @@
inline bool hasTiledRendering() const { return mHasTiledRendering; }
inline bool has1BitStencil() const { return mHas1BitStencil; }
inline bool has4BitStencil() const { return mHas4BitStencil; }
+ inline bool hasNvSystemTime() const { return mHasNvSystemTime; }
+
+ inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
+ inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
+ inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
inline int getMajorGlVersion() const { return mVersionMajor; }
inline int getMinorGlVersion() const { return mVersionMinor; }
- bool hasExtension(const char* extension) const;
+ bool hasGlExtension(const char* extension) const;
+ bool hasEglExtension(const char* extension) const;
void dump() const;
private:
+ Extensions();
+ ~Extensions();
+
+ void findExtensions(const char* extensions, SortedVector<String8>& list) const;
+
friend class Singleton<Extensions>;
- SortedVector<String8> mExtensionList;
-
- char* mExtensions;
- char* mVersion;
+ SortedVector<String8> mGlExtensionList;
+ SortedVector<String8> mEglExtensionList;
bool mHasNPot;
bool mHasFramebufferFetch;
@@ -68,6 +73,7 @@
bool mHasTiledRendering;
bool mHas1BitStencil;
bool mHas4BitStencil;
+ bool mHasNvSystemTime;
int mVersionMajor;
int mVersionMinor;
diff --git a/libs/hwui/Fence.h b/libs/hwui/Fence.h
new file mode 100644
index 0000000..f175e98
--- /dev/null
+++ b/libs/hwui/Fence.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_FENCE_H
+#define ANDROID_HWUI_FENCE_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Creating a Fence instance inserts a new sync fence in the OpenGL
+ * commands stream. The caller can then wait for the fence to be signaled
+ * by calling the wait method.
+ */
+class Fence {
+public:
+ enum {
+ /**
+ * Default timeout in nano-seconds for wait()
+ */
+ kDefaultTimeout = 1000000000
+ };
+
+ /**
+ * Inserts a new sync fence in the OpenGL commands stream.
+ */
+ Fence() {
+ mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (mDisplay != EGL_NO_DISPLAY) {
+ mFence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
+ } else {
+ mFence = EGL_NO_SYNC_KHR;
+ }
+ }
+
+ /**
+ * Destroys the fence. Any caller waiting on the fence will be
+ * signaled immediately.
+ */
+ ~Fence() {
+ if (mFence != EGL_NO_SYNC_KHR) {
+ eglDestroySyncKHR(mDisplay, mFence);
+ }
+ }
+
+ /**
+ * Blocks the calling thread until this fence is signaled, or until
+ * <timeout> nanoseconds have passed.
+ *
+ * Returns true if waiting for the fence was successful, false if
+ * a timeout or an error occurred.
+ */
+ bool wait(EGLTimeKHR timeout = kDefaultTimeout) {
+ EGLint waitStatus = eglClientWaitSyncKHR(mDisplay, mFence,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, timeout);
+ if (waitStatus == EGL_FALSE) {
+ ALOGW("Failed to wait for the fence %#x", eglGetError());
+ }
+ return waitStatus == EGL_CONDITION_SATISFIED_KHR;
+ }
+
+private:
+ EGLDisplay mDisplay;
+ EGLSyncKHR mFence;
+
+}; // class Fence
+
+/**
+ * An AutoFence creates a Fence instance and waits for the fence
+ * to be signaled when the AutoFence is destroyed. This is useful
+ * to automatically wait for a series of OpenGL commands to be
+ * executed. For example:
+ *
+ * void drawAndWait() {
+ * glDrawElements();
+ * AutoFence fence;
+ * }
+ */
+class AutoFence {
+public:
+ AutoFence(EGLTimeKHR timeout = Fence::kDefaultTimeout): mTimeout(timeout) {
+ }
+
+ ~AutoFence() {
+ mFence.wait(mTimeout);
+ }
+
+private:
+ EGLTimeKHR mTimeout;
+ Fence mFence;
+
+}; // class AutoFence
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FENCE_H
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 543cfa2..79a7a93 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -24,7 +24,9 @@
#include <utils/Functor.h>
#include <utils/Log.h>
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
#include <RenderScript.h>
+#endif
#include "utils/Blur.h"
#include "utils/Timing.h"
@@ -62,8 +64,6 @@
mLinearFiltering = false;
- mIndexBufferID = 0;
-
mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
@@ -109,12 +109,6 @@
}
mCacheTextures.clear();
- if (mInitialized) {
- // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
- Caches::getInstance().unbindIndicesBuffer();
- glDeleteBuffers(1, &mIndexBufferID);
- }
-
LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
while (it.next()) {
delete it.value();
@@ -317,33 +311,6 @@
mCurrentCacheTexture = mCacheTextures[0];
}
-// Avoid having to reallocate memory and render quad by quad
-void FontRenderer::initVertexArrayBuffers() {
- uint32_t numIndices = gMaxNumberOfQuads * 6;
- uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
- uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
-
- // Four verts, two triangles , six indices per quad
- for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
- int i6 = i * 6;
- int i4 = i * 4;
-
- indexBufferData[i6 + 0] = i4 + 0;
- indexBufferData[i6 + 1] = i4 + 1;
- indexBufferData[i6 + 2] = i4 + 2;
-
- indexBufferData[i6 + 3] = i4 + 0;
- indexBufferData[i6 + 4] = i4 + 2;
- indexBufferData[i6 + 5] = i4 + 3;
- }
-
- glGenBuffers(1, &mIndexBufferID);
- Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
-
- free(indexBufferData);
-}
-
// We don't want to allocate anything unless we actually draw text
void FontRenderer::checkInit() {
if (mInitialized) {
@@ -351,7 +318,6 @@
}
initTextTexture();
- initVertexArrayBuffers();
mInitialized = true;
}
@@ -374,7 +340,7 @@
if (cacheTexture->getTextureId() != lastTextureId) {
lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, lastTextureId);
+ caches.bindTexture(lastTextureId);
}
if (cacheTexture->upload()) {
@@ -414,7 +380,7 @@
if (mFunctor) (*mFunctor)(0, NULL);
checkTextureUpdate();
- caches.bindIndicesBuffer(mIndexBufferID);
+ caches.bindIndicesBuffer();
if (!mDrawn) {
// If returns true, a VBO was bound and we must
@@ -427,7 +393,7 @@
first = false;
}
- glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
+ caches.bindTexture(texture->getTextureId());
texture->setLinearFiltering(mLinearFiltering, false);
TextureVertex* mesh = texture->mesh();
@@ -532,13 +498,18 @@
return image;
}
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
// Align buffers for renderscript usage
if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
}
-
int size = paddedWidth * paddedHeight;
uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
+#else
+ int size = paddedWidth * paddedHeight;
+ uint8_t* dataBuffer = (uint8_t*) malloc(size);
+#endif
+
memset(dataBuffer, 0, size);
int penX = radius - bounds.left;
@@ -633,43 +604,46 @@
}
void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
- if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
- float *gaussian = new float[2 * radius + 1];
- Blur::generateGaussianWeights(gaussian, radius);
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
+ if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
+ uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
- uint8_t* scratch = new uint8_t[width * height];
- Blur::horizontal(gaussian, radius, *image, scratch, width, height);
- Blur::vertical(gaussian, radius, scratch, *image, width, height);
+ if (mRs.get() == 0) {
+ mRs = new RSC::RS();
+ if (!mRs->init(true, true)) {
+ ALOGE("blur RS failed to init");
+ }
- delete[] gaussian;
- delete[] scratch;
- return;
- }
-
- uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
-
- if (mRs.get() == 0) {
- mRs = new RSC::RS();
- if (!mRs->init(true, true)) {
- ALOGE("blur RS failed to init");
+ mRsElement = RSC::Element::A_8(mRs);
+ mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
}
- mRsElement = RSC::Element::A_8(mRs);
- mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
+ sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
+ sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
+ RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
+ sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
+ RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
+
+ mRsScript->setRadius(radius);
+ mRsScript->blur(ain, aout);
+
+ // replace the original image's pointer, avoiding a copy back to the original buffer
+ free(*image);
+ *image = outImage;
+
+ return;
}
+#endif
- sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
- sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
- RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
- sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
- RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
+ float *gaussian = new float[2 * radius + 1];
+ Blur::generateGaussianWeights(gaussian, radius);
- mRsScript->setRadius(radius);
- mRsScript->blur(ain, aout);
+ uint8_t* scratch = new uint8_t[width * height];
+ Blur::horizontal(gaussian, radius, *image, scratch, width, height);
+ Blur::vertical(gaussian, radius, scratch, *image, width, height);
- // replace the original image's pointer, avoiding a copy back to the original buffer
- free(*image);
- *image = outImage;
+ delete[] gaussian;
+ delete[] scratch;
}
uint32_t FontRenderer::getCacheSize() const {
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 307a1d9..c1072ed 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -19,6 +19,7 @@
#include <utils/LruCache.h>
#include <utils/Vector.h>
+#include <utils/StrongPointer.h>
#include <SkPaint.h>
@@ -32,11 +33,13 @@
#include "Matrix.h"
#include "Properties.h"
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
namespace RSC {
class Element;
class RS;
class ScriptIntrinsicBlur;
}
+#endif
class Functor;
@@ -102,8 +105,6 @@
private:
friend class Font;
- static const uint32_t gMaxNumberOfQuads = 2048;
-
const uint8_t* mGammaTable;
void allocateTextureMemory(CacheTexture* cacheTexture);
@@ -115,7 +116,6 @@
CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
void flushAllAndInvalidate();
- void initVertexArrayBuffers();
void checkInit();
void initRender(const Rect* clip, Rect* bounds, Functor* functor);
@@ -157,8 +157,6 @@
bool mUploadTexture;
- uint32_t mIndexBufferID;
-
Functor* mFunctor;
const Rect* mClip;
Rect* mBounds;
@@ -168,10 +166,12 @@
bool mLinearFiltering;
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
// RS constructs
sp<RSC::RS> mRs;
sp<const RSC::Element> mRsElement;
sp<RSC::ScriptIntrinsicBlur> mRsScript;
+#endif
static void computeGaussianWeights(float* weights, int32_t radius);
static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 507ed95..0916942 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -78,7 +78,7 @@
mCache.setOnEntryRemovedListener(this);
const Extensions& extensions = Extensions::getInstance();
- mUseFloatTexture = extensions.getMajorGlVersion() >= 3;
+ mUseFloatTexture = extensions.hasFloatTextures();
mHasNpot = extensions.hasNPot();
}
@@ -120,7 +120,7 @@
const uint32_t size = texture->width * texture->height * bytesPerPixel();
mSize -= size;
- glDeleteTextures(1, &texture->id);
+ texture->deleteTexture();
delete texture;
}
}
@@ -173,7 +173,7 @@
GradientInfo info;
getGradientInfo(colors, count, info);
- Texture* texture = new Texture;
+ Texture* texture = new Texture();
texture->width = info.width;
texture->height = 2;
texture->blend = info.hasAlpha;
@@ -286,7 +286,7 @@
memcpy(pixels + rowBytes, pixels, rowBytes);
glGenTextures(1, &texture->id);
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ Caches::getInstance().bindTexture(texture->id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
if (mUseFloatTexture) {
diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp
new file mode 100644
index 0000000..edf3930
--- /dev/null
+++ b/libs/hwui/Image.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include "Caches.h"
+#include "Image.h"
+
+namespace android {
+namespace uirenderer {
+
+Image::Image(sp<GraphicBuffer> buffer) {
+ // Create the EGLImage object that maps the GraphicBuffer
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer->getNativeBuffer();
+ EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
+
+ mImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
+
+ if (mImage == EGL_NO_IMAGE_KHR) {
+ ALOGW("Error creating image (%#x)", eglGetError());
+ mTexture = 0;
+ } else {
+ // Create a 2D texture to sample from the EGLImage
+ glGenTextures(1, &mTexture);
+ Caches::getInstance().bindTexture(mTexture);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage);
+
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ ALOGW("Error creating image (%#x)", status);
+ }
+ }
+}
+
+Image::~Image() {
+ if (mImage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), mImage);
+ mImage = EGL_NO_IMAGE_KHR;
+
+ Caches::getInstance().deleteTexture(mTexture);
+ mTexture = 0;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Image.h b/libs/hwui/Image.h
new file mode 100644
index 0000000..2514535
--- /dev/null
+++ b/libs/hwui/Image.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_IMAGE_H
+#define ANDROID_HWUI_IMAGE_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A simple wrapper that creates an EGLImage and a texture for a GraphicBuffer.
+ */
+class Image {
+public:
+ /**
+ * Creates a new image from the specified graphic buffer. If the image
+ * cannot be created, getTexture() will return 0 and getImage() will
+ * return EGL_NO_IMAGE_KHR.
+ */
+ Image(sp<GraphicBuffer> buffer);
+ ~Image();
+
+ /**
+ * Returns the name of the GL texture that can be used to sample
+ * from this image.
+ */
+ GLuint getTexture() const {
+ return mTexture;
+ }
+
+ /**
+ * Returns the name of the EGL image represented by this object.
+ */
+ EGLImageKHR getImage() const {
+ return mImage;
+ }
+
+private:
+ GLuint mTexture;
+ EGLImageKHR mImage;
+}; // class Image
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_IMAGE_H
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 4adad05..73082c1 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -28,7 +28,8 @@
namespace android {
namespace uirenderer {
-Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
+Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
+ caches(Caches::getInstance()), texture(caches) {
mesh = NULL;
meshIndices = NULL;
meshElementCount = 0;
@@ -47,11 +48,11 @@
debugDrawUpdate = false;
hasDrawnSinceUpdate = false;
deferredList = NULL;
- Caches::getInstance().resourceCache.incrementRefcount(this);
+ caches.resourceCache.incrementRefcount(this);
}
Layer::~Layer() {
- if (colorFilter) Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
+ if (colorFilter) caches.resourceCache.decrementRefcount(colorFilter);
removeFbo();
deleteTexture();
@@ -76,7 +77,7 @@
return true;
}
- const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize;
+ const uint32_t maxTextureSize = caches.maxTextureSize;
if (desiredWidth > maxTextureSize || desiredHeight > maxTextureSize) {
ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
desiredWidth, desiredHeight, maxTextureSize, maxTextureSize);
@@ -89,7 +90,7 @@
setSize(desiredWidth, desiredHeight);
if (fbo) {
- Caches::getInstance().activeTexture(0);
+ caches.activeTexture(0);
bindTexture();
allocateTexture();
@@ -120,14 +121,14 @@
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- Caches::getInstance().renderBufferCache.put(stencil);
+ caches.renderBufferCache.put(stencil);
stencil = NULL;
}
if (fbo) {
if (flush) LayerRenderer::flushLayer(this);
// If put fails the cache will delete the FBO
- Caches::getInstance().fboCache.put(fbo);
+ caches.fboCache.put(fbo);
fbo = 0;
}
}
@@ -138,21 +139,55 @@
void Layer::setColorFilter(SkiaColorFilter* filter) {
if (colorFilter) {
- Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
+ caches.resourceCache.decrementRefcount(colorFilter);
}
colorFilter = filter;
if (colorFilter) {
- Caches::getInstance().resourceCache.incrementRefcount(colorFilter);
+ caches.resourceCache.incrementRefcount(colorFilter);
+ }
+}
+
+void Layer::bindTexture() const {
+ if (texture.id) {
+ caches.bindTexture(renderTarget, texture.id);
+ }
+}
+
+void Layer::bindStencilRenderBuffer() const {
+ if (stencil) {
+ stencil->bind();
+ }
+}
+
+void Layer::generateTexture() {
+ if (!texture.id) {
+ glGenTextures(1, &texture.id);
+ }
+}
+
+void Layer::deleteTexture() {
+ if (texture.id) {
+ texture.deleteTexture();
+ texture.id = 0;
+ }
+}
+
+void Layer::clearTexture() {
+ texture.id = 0;
+}
+
+void Layer::allocateTexture() {
+#if DEBUG_LAYERS
+ ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
+#endif
+ if (texture.id) {
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
}
void Layer::defer() {
- if (!deferredList) {
- deferredList = new DeferredDisplayList;
- }
- DeferStateStruct deferredState(*deferredList, *renderer,
- DisplayList::kReplayFlag_ClipChildren);
-
const float width = layer.getWidth();
const float height = layer.getHeight();
@@ -161,6 +196,14 @@
dirtyRect.set(0, 0, width, height);
}
+ if (deferredList) {
+ deferredList->reset(dirtyRect);
+ } else {
+ deferredList = new DeferredDisplayList(dirtyRect);
+ }
+ DeferStateStruct deferredState(*deferredList, *renderer,
+ DisplayList::kReplayFlag_ClipChildren);
+
renderer->initViewport(width, height);
renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, !isBlend());
@@ -170,8 +213,19 @@
deferredUpdateScheduled = false;
}
-void Layer::flush() {
+void Layer::cancelDefer() {
+ renderer = NULL;
+ displayList = NULL;
+ deferredUpdateScheduled = false;
if (deferredList) {
+ delete deferredList;
+ deferredList = NULL;
+ }
+}
+
+void Layer::flush() {
+ // renderer is checked as layer may be destroyed/put in layer cache with flush scheduled
+ if (deferredList && renderer) {
renderer->setViewport(layer.getWidth(), layer.getHeight());
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 7186603..ebd5543 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -40,6 +40,7 @@
///////////////////////////////////////////////////////////////////////////////
// Forward declarations
+class Caches;
class OpenGLRenderer;
class DisplayList;
class DeferredDisplayList;
@@ -221,50 +222,19 @@
ANDROID_API void setColorFilter(SkiaColorFilter* filter);
- inline void bindTexture() const {
- if (texture.id) {
- glBindTexture(renderTarget, texture.id);
- }
- }
+ void bindStencilRenderBuffer() const;
- inline void bindStencilRenderBuffer() const {
- if (stencil) {
- stencil->bind();
- }
- }
-
- inline void generateTexture() {
- if (!texture.id) {
- glGenTextures(1, &texture.id);
- }
- }
-
- inline void deleteTexture() {
- if (texture.id) {
- glDeleteTextures(1, &texture.id);
- texture.id = 0;
- }
- }
+ void bindTexture() const;
+ void generateTexture();
+ void allocateTexture();
+ void deleteTexture();
/**
* When the caller frees the texture itself, the caller
* must call this method to tell this layer that it lost
* the texture.
*/
- void clearTexture() {
- texture.id = 0;
- }
-
- inline void allocateTexture() {
-#if DEBUG_LAYERS
- ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
-#endif
- if (texture.id) {
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- }
- }
+ ANDROID_API void clearTexture();
inline mat4& getTexTransform() {
return texTransform;
@@ -275,6 +245,7 @@
}
void defer();
+ void cancelDefer();
void flush();
void render();
@@ -320,6 +291,8 @@
bool hasDrawnSinceUpdate;
private:
+ Caches& caches;
+
/**
* Name of the FBO used to render the layer. If the name is 0
* this layer is not backed by an FBO, but a simple texture.
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index a0709af..6be0146 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -155,9 +155,7 @@
victim->layer.getHeight());
}
- layer->deferredUpdateScheduled = false;
- layer->renderer = NULL;
- layer->displayList = NULL;
+ layer->cancelDefer();
LayerEntry entry(layer);
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 3e55fff..cfb1e97 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -436,7 +436,7 @@
if ((error = glGetError()) != GL_NO_ERROR) goto error;
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, texture);
+ caches.bindTexture(texture);
glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
@@ -467,7 +467,7 @@
mat4 texTransform(layer->getTexTransform());
mat4 invert;
- invert.translate(0.0f, 1.0f, 0.0f);
+ invert.translate(0.0f, 1.0f);
invert.scale(1.0f, -1.0f, 1.0f);
layer->getTexTransform().multiply(invert);
@@ -498,7 +498,7 @@
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
- glDeleteTextures(1, &texture);
+ caches.deleteTexture(texture);
caches.fboCache.put(fbo);
glViewport(previousViewport[0], previousViewport[1],
previousViewport[2], previousViewport[3]);
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 6a5ea51..65e7eae 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -72,7 +72,7 @@
return fabs(f) <= EPSILON;
}
-uint32_t Matrix4::getType() const {
+uint8_t Matrix4::getType() const {
if (mType & kTypeUnknown) {
mType = kTypeIdentity;
@@ -114,7 +114,7 @@
return mType;
}
-uint32_t Matrix4::getGeometryType() const {
+uint8_t Matrix4::getGeometryType() const {
return getType() & sGeometryMask;
}
@@ -127,7 +127,7 @@
}
bool Matrix4::isPureTranslate() const {
- return getGeometryType() == kTypeTranslate;
+ return getGeometryType() <= kTypeTranslate;
}
bool Matrix4::isSimple() const {
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 75e280c..5116203 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -26,6 +26,12 @@
namespace android {
namespace uirenderer {
+#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define MATRIX_ARGS(m) \
+ (m)->get(0), (m)->get(1), (m)->get(2), \
+ (m)->get(3), (m)->get(4), (m)->get(5), \
+ (m)->get(6), (m)->get(7), (m)->get(8)
+
///////////////////////////////////////////////////////////////////////////////
// Classes
///////////////////////////////////////////////////////////////////////////////
@@ -118,7 +124,7 @@
void loadOrtho(float left, float right, float bottom, float top, float near, float far);
- uint32_t getType() const;
+ uint8_t getType() const;
void multiply(const Matrix4& v) {
Matrix4 u;
@@ -128,10 +134,27 @@
void multiply(float v);
- void translate(float x, float y, float z) {
- Matrix4 u;
- u.loadTranslate(x, y, z);
- multiply(u);
+ void translate(float x, float y) {
+ if ((getType() & sGeometryMask) <= kTypeTranslate) {
+ data[kTranslateX] += x;
+ data[kTranslateY] += y;
+ } else {
+ // Doing a translation will only affect the translate bit of the type
+ // Save the type
+ uint8_t type = mType;
+
+ Matrix4 u;
+ u.loadTranslate(x, y, 0.0f);
+ multiply(u);
+
+ // Restore the type and fix the translate bit
+ mType = type;
+ if (data[kTranslateX] != 0.0f || data[kTranslateY] != 0.0f) {
+ mType |= kTypeTranslate;
+ } else {
+ mType &= ~kTypeTranslate;
+ }
+ }
}
void scale(float sx, float sy, float sz) {
@@ -179,7 +202,7 @@
static const Matrix4& identity();
private:
- mutable uint32_t mType;
+ mutable uint8_t mType;
inline float get(int i, int j) const {
return data[i * 4 + j];
@@ -189,7 +212,7 @@
data[i * 4 + j] = v;
}
- uint32_t getGeometryType() const;
+ uint8_t getGeometryType() const;
}; // class Matrix4
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7735819..7c0f3ad 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <SkCanvas.h>
-#include <SkPathMeasure.h>
#include <SkTypeface.h>
#include <utils/Log.h>
@@ -34,6 +33,7 @@
#include "OpenGLRenderer.h"
#include "DeferredDisplayList.h"
#include "DisplayListRenderer.h"
+#include "Fence.h"
#include "PathTessellator.h"
#include "Properties.h"
#include "Vector.h"
@@ -120,6 +120,7 @@
mFirstSnapshot = new Snapshot;
mFrameStarted = false;
+ mCountOverdraw = false;
mScissorOptimizationDisabled = false;
}
@@ -222,6 +223,7 @@
status_t OpenGLRenderer::prepareDirty(float left, float top,
float right, float bottom, bool opaque) {
+
setupFrameState(left, top, right, bottom, opaque);
// Layer renderers will start the frame immediately
@@ -253,7 +255,7 @@
}
status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
- if (!opaque) {
+ if (!opaque || mCountOverdraw) {
mCaches.enableScissor();
mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
glClear(GL_COLOR_BUFFER_BIT);
@@ -335,6 +337,10 @@
#endif
}
+ if (mCountOverdraw) {
+ countOverdraw();
+ }
+
mFrameStarted = false;
}
@@ -366,6 +372,7 @@
dirtyClip();
mCaches.activeTexture(0);
+ mCaches.resetBoundTextures();
mCaches.blend = true;
glEnable(GL_BLEND);
@@ -459,7 +466,7 @@
info.height = getSnapshot()->height;
getSnapshot()->transform->copyTo(&info.transform[0]);
- status_t result = (*functor)(DrawGlInfo::kModeDraw, &info) | DrawGlInfo::kStatusDrew;
+ status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);
if (result != DrawGlInfo::kStatusDone) {
Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
@@ -471,7 +478,7 @@
}
resume();
- return result;
+ return result | DrawGlInfo::kStatusDrew;
}
///////////////////////////////////////////////////////////////////////////////
@@ -524,6 +531,21 @@
}
}
+void OpenGLRenderer::countOverdraw() {
+ size_t count = mWidth * mHeight;
+ uint32_t* buffer = new uint32_t[count];
+ glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]);
+
+ size_t total = 0;
+ for (size_t i = 0; i < count; i++) {
+ total += buffer[i] & 0xff;
+ }
+
+ mOverdraw = total / float(count);
+
+ delete[] buffer;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Layers
///////////////////////////////////////////////////////////////////////////////
@@ -531,6 +553,8 @@
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
if (layer->deferredUpdateScheduled && layer->renderer &&
layer->displayList && layer->displayList->isRenderable()) {
+ ATRACE_CALL();
+
Rect& dirty = layer->dirtyRect;
if (inFrame) {
@@ -598,8 +622,11 @@
sprintf(layerName, "Layer #%d", i);
startMark(layerName);
+ ATRACE_BEGIN("flushLayer");
Layer* layer = mLayerUpdates.itemAt(i);
layer->flush();
+ ATRACE_END();
+
mCaches.resourceCache.decrementRefcount(layer);
endMark();
@@ -628,6 +655,18 @@
}
}
+void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
+ if (layer) {
+ for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
+ if (mLayerUpdates.itemAt(i) == layer) {
+ mLayerUpdates.removeAt(i);
+ mCaches.resourceCache.decrementRefcount(layer);
+ break;
+ }
+ }
+ }
+}
+
void OpenGLRenderer::clearLayerUpdates() {
size_t count = mLayerUpdates.size();
if (count > 0) {
@@ -640,6 +679,14 @@
}
}
+void OpenGLRenderer::flushLayerUpdates() {
+ syncState();
+ updateLayers();
+ flushLayers();
+ // Wait for all the layer updates to be executed
+ AutoFence fence;
+}
+
///////////////////////////////////////////////////////////////////////////////
// State management
///////////////////////////////////////////////////////////////////////////////
@@ -780,6 +827,7 @@
if (!mSnapshot->isIgnored()) {
mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
}
}
@@ -961,6 +1009,10 @@
const Rect& rect = layer->layer;
const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
+ bool clipRequired = false;
+ quickRejectNoScissor(rect, &clipRequired); // safely ignore return, should never be rejected
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+
if (fboLayer) {
endTiling();
@@ -1013,7 +1065,7 @@
}
void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
- float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha;
+ float alpha = getLayerAlpha(layer);
setupDraw();
if (layer->getRenderTarget() == GL_TEXTURE_2D) {
@@ -1119,8 +1171,6 @@
return;
}
- // TODO: See LayerRenderer.cpp::generateMesh() for important
- // information about this implementation
if (CC_LIKELY(!layer->region.isEmpty())) {
size_t count;
const android::Rect* rects;
@@ -1143,7 +1193,7 @@
// after we setup drawing in case we need to mess with the
// stencil buffer in setupDraw()
TextureVertex* mesh = mCaches.getRegionMesh();
- GLsizei numQuads = 0;
+ uint32_t numQuads = 0;
setupDrawWithTexture();
setupDrawColor(alpha, alpha, alpha, alpha);
@@ -1182,7 +1232,7 @@
numQuads++;
- if (numQuads >= REGION_MESH_QUAD_COUNT) {
+ if (numQuads >= gMaxNumberOfQuads) {
DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
GL_UNSIGNED_SHORT, NULL));
numQuads = 0;
@@ -1342,11 +1392,26 @@
// state has bounds initialized in local coordinates
if (!state.mBounds.isEmpty()) {
currentMatrix.mapRect(state.mBounds);
- if (!state.mBounds.intersect(currentClip)) {
+ Rect clippedBounds(state.mBounds);
+ if(!clippedBounds.intersect(currentClip)) {
// quick rejected
return true;
}
+
+ state.mClipSideFlags = kClipSide_None;
+ if (!currentClip.contains(state.mBounds)) {
+ int& flags = state.mClipSideFlags;
+ // op partially clipped, so record which sides are clipped for clip-aware merging
+ if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
+ if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
+ if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
+ if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
+ }
+ state.mBounds.set(clippedBounds);
} else {
+ // Empty bounds implies size unknown. Label op as conservatively clipped to disable
+ // overdraw avoidance (since we don't know what it overlaps)
+ state.mClipSideFlags = kClipSide_ConservativeFull;
state.mBounds.set(currentClip);
}
}
@@ -1370,14 +1435,27 @@
mSnapshot->alpha = state.mAlpha;
if (state.mClipValid && !skipClipRestore) {
- mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
+ mSnapshot->setClip(state.mClip.left, state.mClip.top,
+ state.mClip.right, state.mClip.bottom);
dirtyClip();
}
}
-void OpenGLRenderer::setFullScreenClip() {
- mSnapshot->setClip(0, 0, mWidth, mHeight);
+/**
+ * Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done
+ * in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at
+ * least one op is clipped), or disabled entirely (because no merged op is clipped)
+ *
+ * This method should be called when restoreDisplayState() won't be restoring the clip
+ */
+void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
+ if (clipRect != NULL) {
+ mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+ } else {
+ mSnapshot->setClip(0, 0, mWidth, mHeight);
+ }
dirtyClip();
+ mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled);
}
///////////////////////////////////////////////////////////////////////////////
@@ -1385,7 +1463,7 @@
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::translate(float dx, float dy) {
- currentTransform().translate(dx, dy, 0.0f);
+ currentTransform().translate(dx, dy);
}
void OpenGLRenderer::rotate(float degrees) {
@@ -1508,8 +1586,9 @@
return mSnapshot->getLocalClip();
}
-bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom) {
- if (mSnapshot->isIgnored()) {
+bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom,
+ bool* clipRequired) {
+ if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
return true;
}
@@ -1520,23 +1599,10 @@
Rect clipRect(*mSnapshot->clipRect);
clipRect.snapToPixelBoundaries();
- return !clipRect.intersects(r);
-}
+ if (!clipRect.intersects(r)) return true;
-bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom,
- Rect& transformed, Rect& clip) {
- if (mSnapshot->isIgnored()) {
- return true;
- }
-
- transformed.set(left, top, right, bottom);
- currentTransform().mapRect(transformed);
- transformed.snapToPixelBoundaries();
-
- clip.set(*mSnapshot->clipRect);
- clip.snapToPixelBoundaries();
-
- return !clip.intersects(transformed);
+ if (clipRequired) *clipRequired = !clipRect.contains(r);
+ return false;
}
bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom,
@@ -1550,23 +1616,15 @@
}
bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
- if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
+ bool clipRequired = false;
+ if (quickRejectNoScissor(left, top, right, bottom, &clipRequired)) {
return true;
}
- Rect r(left, top, right, bottom);
- currentTransform().mapRect(r);
- r.snapToPixelBoundaries();
-
- Rect clipRect(*mSnapshot->clipRect);
- clipRect.snapToPixelBoundaries();
-
- bool rejected = !clipRect.intersects(r);
- if (!isDeferred() && !rejected) {
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || !clipRect.contains(r));
+ if (!isDeferred()) {
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
}
-
- return rejected;
+ return false;
}
void OpenGLRenderer::debugClip() {
@@ -1659,6 +1717,8 @@
mDescription.hasDebugHighlight = !mCaches.debugOverdraw &&
mCaches.debugStencilClip == Caches::kStencilShowHighlight &&
mCaches.stencil.isTestEnabled();
+
+ mDescription.emulateStencil = mCountOverdraw;
}
void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
@@ -1684,11 +1744,6 @@
mDescription.isAA = true;
}
-void OpenGLRenderer::setupDrawPoint(float pointSize) {
- mDescription.isPoint = true;
- mDescription.pointSize = pointSize;
-}
-
void OpenGLRenderer::setupDrawColor(int color, int alpha) {
mColorA = alpha / 255.0f;
mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
@@ -1803,11 +1858,6 @@
}
}
-void OpenGLRenderer::setupDrawPointUniforms() {
- int slot = mCaches.currentProgram->getUniform("pointSize");
- glUniform1f(slot, mDescription.pointSize);
-}
-
void OpenGLRenderer::setupDrawColorUniforms() {
if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
@@ -1876,7 +1926,7 @@
void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
bool force = false;
- if (!vertices) {
+ if (!vertices || vbo) {
force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
} else {
force = mCaches.unbindMeshBuffer();
@@ -1907,8 +1957,18 @@
mCaches.unbindIndicesBuffer();
}
-void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords) {
- bool force = mCaches.unbindMeshBuffer();
+void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
+ bool force = false;
+ // If vbo is != 0 we want to treat the vertices parameter as an offset inside
+ // a VBO. However, if vertices is set to NULL and vbo == 0 then we want to
+ // use the default VBO found in Caches
+ if (!vertices || vbo) {
+ force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
+ } else {
+ force = mCaches.unbindMeshBuffer();
+ }
+ mCaches.bindIndicesBuffer();
+
mCaches.bindPositionVertexPointer(force, vertices);
if (mCaches.currentProgram->texCoords >= 0) {
mCaches.bindTexCoordsVertexPointer(force, texCoords);
@@ -1941,7 +2001,8 @@
return status | replayStruct.mDrawGlStatus;
}
- DeferredDisplayList deferredList;
+ bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
+ DeferredDisplayList deferredList(*(mSnapshot->clipRect), avoidOverdraw);
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
displayList->defer(deferStruct, 0);
@@ -1983,20 +2044,24 @@
texture->setFilter(FILTER(paint), true);
}
+ // No need to check for a UV mapper on the texture object, only ARGB_8888
+ // bitmaps get packed in the atlas
drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
- paint != NULL, color, alpha, mode, (GLvoid*) NULL,
- (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
+ paint != NULL, color, alpha, mode, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
+ GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
}
-status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
- const Rect& bounds, SkPaint* paint) {
-
- // merged draw operations don't need scissor, but clip should still be valid
- mCaches.setScissorEnabled(mScissorOptimizationDisabled);
-
+/**
+ * Important note: this method is intended to draw batches of bitmaps and
+ * will not set the scissor enable or dirty the current layer, if any.
+ * The caller is responsible for properly dirtying the current layer.
+ */
+status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
+ TextureVertex* vertices, bool transformed, const Rect& bounds, SkPaint* paint) {
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
+
const AutoTexture autoCleanup(texture);
int alpha;
@@ -2004,7 +2069,7 @@
getAlphaAndMode(paint, &alpha, &mode);
texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(GL_NEAREST, true); // merged ops are always pure-translation for now
+ texture->setFilter(transformed ? FILTER(paint) : GL_NEAREST, true);
const float x = (int) floorf(bounds.left + 0.5f);
const float y = (int) floorf(bounds.top + 0.5f);
@@ -2013,12 +2078,12 @@
drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
texture->id, paint != NULL, color, alpha, mode,
&vertices[0].position[0], &vertices[0].texture[0],
- GL_TRIANGLES, bitmapCount * 6, true, true);
+ GL_TRIANGLES, bitmapCount * 6, true, true, false);
} else {
drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
texture->id, alpha / 255.0f, mode, texture->blend,
&vertices[0].position[0], &vertices[0].texture[0],
- GL_TRIANGLES, bitmapCount * 6, false, true, 0, true);
+ GL_TRIANGLES, bitmapCount * 6, false, true, 0, true, false);
}
return DrawGlInfo::kStatusDrew;
@@ -2033,7 +2098,7 @@
}
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
@@ -2056,7 +2121,7 @@
}
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
@@ -2101,6 +2166,9 @@
return DrawGlInfo::kStatusDone;
}
+ // TODO: use quickReject on bounds from vertices
+ mCaches.enableScissor();
+
float left = FLT_MAX;
float top = FLT_MAX;
float right = FLT_MIN;
@@ -2119,6 +2187,10 @@
cleanupColors = true;
}
+ mCaches.activeTexture(0);
+ Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap);
+ const UvMapper& mapper(getMapper(texture));
+
for (int32_t y = 0; y < meshHeight; y++) {
for (int32_t x = 0; x < meshWidth; x++) {
uint32_t i = (y * (meshWidth + 1) + x) * 2;
@@ -2128,6 +2200,8 @@
float v1 = float(y) / meshHeight;
float v2 = float(y + 1) / meshHeight;
+ mapper.map(u1, v1, u2, v2);
+
int ax = i + (meshWidth + 1) * 2;
int ay = ax + 1;
int bx = i;
@@ -2157,11 +2231,12 @@
return DrawGlInfo::kStatusDone;
}
- mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
if (!texture) {
- if (cleanupColors) delete[] colors;
- return DrawGlInfo::kStatusDone;
+ texture = mCaches.textureCache.get(bitmap);
+ if (!texture) {
+ if (cleanupColors) delete[] colors;
+ return DrawGlInfo::kStatusDone;
+ }
}
const AutoTexture autoCleanup(texture);
@@ -2214,17 +2289,19 @@
}
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
const float width = texture->width;
const float height = texture->height;
- const float u1 = fmax(0.0f, srcLeft / width);
- const float v1 = fmax(0.0f, srcTop / height);
- const float u2 = fmin(1.0f, srcRight / width);
- const float v2 = fmin(1.0f, srcBottom / height);
+ float u1 = fmax(0.0f, srcLeft / width);
+ float v1 = fmax(0.0f, srcTop / height);
+ float u2 = fmin(1.0f, srcRight / width);
+ float v2 = fmin(1.0f, srcBottom / height);
+
+ getMapper(texture).map(u1, v1, u2, v2);
mCaches.unbindMeshBuffer();
resetDrawTextureTexCoords(u1, v1, u2, v2);
@@ -2295,37 +2372,38 @@
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
float left, float top, float right, float bottom, SkPaint* paint) {
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- return drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
- left, top, right, bottom, alpha, mode);
-}
-
-status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
- float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode) {
if (quickReject(left, top, right, bottom)) {
return DrawGlInfo::kStatusDone;
}
- alpha *= mSnapshot->alpha;
+ AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
+ const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
+ right - left, bottom - top, patch);
- const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
- right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
+ return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint);
+}
+
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
+ float left, float top, float right, float bottom, SkPaint* paint) {
+ if (quickReject(left, top, right, bottom)) {
+ return DrawGlInfo::kStatusDone;
+ }
if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
+
texture->setWrap(GL_CLAMP_TO_EDGE, true);
texture->setFilter(GL_LINEAR, true);
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
const bool pureTranslate = currentTransform().isPureTranslate();
// Mark the current layer dirty where we are going to draw the patch
if (hasLayer() && mesh->hasEmptyQuads) {
@@ -2349,24 +2427,52 @@
const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
- drawTextureMesh(x, y, x + right - left, y + bottom - top, texture->id, alpha / 255.0f,
- mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLES, mesh->verticesCount, false, true, mesh->meshBuffer,
- true, !mesh->hasEmptyQuads);
+ right = x + right - left;
+ bottom = y + bottom - top;
+ drawIndexedTextureMesh(x, y, right, bottom, texture->id, alpha / 255.0f,
+ mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
+ GL_TRIANGLES, mesh->indexCount, false, true,
+ mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads);
} else {
- drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
- mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer,
- true, !mesh->hasEmptyQuads);
+ drawIndexedTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
+ mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
+ GL_TRIANGLES, mesh->indexCount, false, false,
+ mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads);
}
}
return DrawGlInfo::kStatusDrew;
}
+/**
+ * Important note: this method is intended to draw batches of 9-patch objects and
+ * will not set the scissor enable or dirty the current layer, if any.
+ * The caller is responsible for properly dirtying the current layer.
+ */
+status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
+ TextureVertex* vertices, uint32_t indexCount, SkPaint* paint) {
+ mCaches.activeTexture(0);
+ Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
+ if (!texture) return DrawGlInfo::kStatusDone;
+ const AutoTexture autoCleanup(texture);
+
+ texture->setWrap(GL_CLAMP_TO_EDGE, true);
+ texture->setFilter(GL_LINEAR, true);
+
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f,
+ mode, texture->blend, &vertices[0].position[0], &vertices[0].texture[0],
+ GL_TRIANGLES, indexCount, false, true, 0, true, false);
+
+ return DrawGlInfo::kStatusDrew;
+}
+
status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
bool useOffset) {
- if (!vertexBuffer.getSize()) {
+ if (!vertexBuffer.getVertexCount()) {
// no vertices to draw
return DrawGlInfo::kStatusDone;
}
@@ -2404,7 +2510,7 @@
glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
}
- glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getSize());
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
if (isAA) {
glDisableVertexAttribArray(alphaSlot);
@@ -2467,65 +2573,22 @@
}
status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+ if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
- // TODO: The paint's cap style defines whether the points are square or circular
- // TODO: Handle AA for round points
+ count &= ~0x1; // round down to nearest two
- // A stroke width of 0 has a special meaning in Skia:
- // it draws an unscaled 1px point
- float strokeWidth = paint->getStrokeWidth();
- const bool isHairLine = paint->getStrokeWidth() == 0.0f;
- if (isHairLine) {
- // Now that we know it's hairline, we can set the effective width, to be used later
- strokeWidth = 1.0f;
- }
- const float halfWidth = strokeWidth / 2;
+ VertexBuffer buffer;
+ SkRect bounds;
+ PathTessellator::tessellatePoints(points, count, paint, mSnapshot->transform, bounds, buffer);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- int verticesCount = count >> 1;
- int generatedVerticesCount = 0;
-
- TextureVertex pointsData[verticesCount];
- TextureVertex* vertex = &pointsData[0];
-
- // TODO: We should optimize this method to not generate vertices for points
- // that lie outside of the clip.
- mCaches.enableScissor();
-
- setupDraw();
- setupDrawNoTexture();
- setupDrawPoint(strokeWidth);
- setupDrawColor(paint->getColor(), alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(mode);
- setupDrawProgram();
- setupDrawModelViewIdentity(true);
- setupDrawColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawPointUniforms();
- setupDrawShaderIdentityUniforms();
- setupDrawMesh(vertex);
-
- for (int i = 0; i < count; i += 2) {
- TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
- generatedVerticesCount++;
-
- float left = points[i] - halfWidth;
- float right = points[i] + halfWidth;
- float top = points[i + 1] - halfWidth;
- float bottom = points [i + 1] + halfWidth;
-
- dirtyLayer(left, top, right, bottom, currentTransform());
+ if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+ return DrawGlInfo::kStatusDone;
}
- glDrawArrays(GL_POINTS, 0, generatedVerticesCount);
+ dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
- return DrawGlInfo::kStatusDrew;
+ bool useOffset = !paint->isAntiAlias();
+ return drawVertexBuffer(buffer, paint, useOffset);
}
status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -2794,6 +2857,8 @@
return DrawGlInfo::kStatusDone;
}
+ mCaches.enableScissor();
+
float x = 0.0f;
float y = 0.0f;
const bool pureTranslate = currentTransform().isPureTranslate();
@@ -2856,36 +2921,17 @@
return fontTransform;
}
-status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length,
+status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
- if (drawOpMode == kDrawOpMode_Immediate &&
- (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint))) {
- return DrawGlInfo::kStatusDone;
- }
-
- if (length < 0.0f) length = paint->measureText(text, bytesCount);
- switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
- x -= length / 2.0f;
- break;
- case SkPaint::kRight_Align:
- x -= length;
- break;
- default:
- break;
- }
-
- SkPaint::FontMetrics metrics;
- paint->getFontMetrics(&metrics, 0.0f);
if (drawOpMode == kDrawOpMode_Immediate) {
- if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
+ // The checks for corner-case ignorable text and quick rejection is only done for immediate
+ // drawing as ops from DeferredDisplayList are already filtered for these
+ if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint) ||
+ quickReject(bounds)) {
return DrawGlInfo::kStatusDone;
}
- } else {
- // merged draw operations don't need scissor, but clip should still be valid
- mCaches.setScissorEnabled(mScissorOptimizationDisabled);
}
const float oldX = x;
@@ -2933,7 +2979,7 @@
// TODO: Implement better clipping for scaled/rotated text
const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect;
- Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
+ Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
bool status;
TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
@@ -2944,20 +2990,20 @@
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
} else {
status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
}
if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
if (!pureTranslate) {
- transform.mapRect(bounds);
+ transform.mapRect(layerBounds);
}
- dirtyLayerUnchecked(bounds, getRegion());
+ dirtyLayerUnchecked(layerBounds, getRegion());
}
- drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
+ drawTextDecorations(text, bytesCount, totalAdvance, oldX, oldY, paint);
return DrawGlInfo::kStatusDrew;
}
@@ -2968,6 +3014,9 @@
return DrawGlInfo::kStatusDone;
}
+ // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
+ mCaches.enableScissor();
+
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, mat4::identity());
fontRenderer.setTextureFiltering(true);
@@ -3043,10 +3092,9 @@
}
}
- Rect transformed;
- Rect clip;
+ bool clipRequired = false;
const bool rejected = quickRejectNoScissor(x, y,
- x + layer->layer.getWidth(), y + layer->layer.getHeight(), transformed, clip);
+ x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired);
if (rejected) {
if (transform && !transform->isIdentity()) {
@@ -3057,7 +3105,7 @@
updateLayer(layer, true);
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || !clip.contains(transformed));
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
mCaches.activeTexture(0);
if (CC_LIKELY(!layer->region.isEmpty())) {
@@ -3131,7 +3179,7 @@
void OpenGLRenderer::setupShader(SkiaShader* shader) {
mDrawModifiers.mShader = shader;
if (mDrawModifiers.mShader) {
- mDrawModifiers.mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
+ mDrawModifiers.mShader->setCaches(mCaches);
}
}
@@ -3199,6 +3247,14 @@
// Drawing implementation
///////////////////////////////////////////////////////////////////////////////
+Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) {
+ Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap);
+ if (!texture) {
+ return mCaches.textureCache.get(bitmap);
+ }
+ return texture;
+}
+
void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
float x, float y, SkPaint* paint) {
if (quickReject(x, y, x + texture->width, y + texture->height)) {
@@ -3233,17 +3289,12 @@
#define kStdUnderline_Offset (1.0f / 9.0f)
#define kStdUnderline_Thickness (1.0f / 18.0f)
-void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length,
+void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float underlineWidth,
float x, float y, SkPaint* paint) {
// Handle underline and strike-through
uint32_t flags = paint->getFlags();
if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
SkPaint paintCopy(*paint);
- float underlineWidth = length;
- // If length is > 0.0f, we already measured the text for the text alignment
- if (length <= 0.0f) {
- underlineWidth = paintCopy.measureText(text, bytesCount);
- }
if (CC_LIKELY(underlineWidth > 0.0f)) {
const float textSize = paintCopy.getTextSize();
@@ -3392,19 +3443,35 @@
texture->setWrap(GL_CLAMP_TO_EDGE, true);
+ GLvoid* vertices = (GLvoid*) NULL;
+ GLvoid* texCoords = (GLvoid*) gMeshTextureOffset;
+
+ if (texture->uvMapper) {
+ vertices = &mMeshVertices[0].position[0];
+ texCoords = &mMeshVertices[0].texture[0];
+
+ Rect uvs(0.0f, 0.0f, 1.0f, 1.0f);
+ texture->uvMapper->map(uvs);
+
+ resetDrawTextureTexCoords(uvs.left, uvs.top, uvs.right, uvs.bottom);
+ }
+
if (CC_LIKELY(currentTransform().isPureTranslate())) {
const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
texture->setFilter(GL_NEAREST, true);
drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
- alpha / 255.0f, mode, texture->blend, (GLvoid*) NULL,
- (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, false, true);
+ alpha / 255.0f, mode, texture->blend, vertices, texCoords,
+ GL_TRIANGLE_STRIP, gMeshCount, false, true);
} else {
texture->setFilter(FILTER(paint), true);
drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
- texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLE_STRIP, gMeshCount);
+ texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount);
+ }
+
+ if (texture->uvMapper) {
+ resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
}
@@ -3441,6 +3508,33 @@
finishDrawTexture();
}
+void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, float bottom,
+ GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
+ GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
+ bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) {
+
+ setupDraw();
+ setupDrawWithTexture();
+ setupDrawColor(alpha, alpha, alpha, alpha);
+ setupDrawColorFilter();
+ setupDrawBlending(blend, mode, swapSrcDst);
+ setupDrawProgram();
+ if (!dirty) setupDrawDirtyRegionsDisabled();
+ if (!ignoreScale) {
+ setupDrawModelView(left, top, right, bottom, ignoreTransform);
+ } else {
+ setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
+ }
+ setupDrawTexture(texture);
+ setupDrawPureColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawMeshIndices(vertices, texCoords, vbo);
+
+ glDrawElements(drawMode, elementsCount, GL_UNSIGNED_SHORT, NULL);
+
+ finishDrawTexture();
+}
+
void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
@@ -3474,6 +3568,19 @@
void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
ProgramDescription& description, bool swapSrcDst) {
+ if (mCountOverdraw) {
+ if (!mCaches.blend) glEnable(GL_BLEND);
+ if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) {
+ glBlendFunc(GL_ONE, GL_ONE);
+ }
+
+ mCaches.blend = true;
+ mCaches.lastSrcMode = GL_ONE;
+ mCaches.lastDstMode = GL_ONE;
+
+ return;
+ }
+
blend = blend || mode != SkXfermode::kSrcOver_Mode;
if (blend) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index a0ad888..5e731b4 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -34,6 +34,8 @@
#include <cutils/compiler.h>
+#include <androidfw/ResourceTypes.h>
+
#include "Debug.h"
#include "Extensions.h"
#include "Matrix.h"
@@ -43,6 +45,7 @@
#include "Vertex.h"
#include "SkiaShader.h"
#include "SkiaColorFilter.h"
+#include "UvMapper.h"
#include "Caches.h"
namespace android {
@@ -77,12 +80,25 @@
kDrawOpMode_Flush
};
+enum ClipSideFlags {
+ kClipSide_None = 0x0,
+ kClipSide_Left = 0x1,
+ kClipSide_Top = 0x2,
+ kClipSide_Right = 0x4,
+ kClipSide_Bottom = 0x8,
+ kClipSide_Full = 0xF,
+ kClipSide_ConservativeFull = 0x1F
+};
+
struct DeferredDisplayState {
- Rect mBounds; // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped.
+ // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped
+ Rect mBounds;
// the below are set and used by the OpenGLRenderer at record and deferred playback
bool mClipValid;
Rect mClip;
+ int mClipSideFlags; // specifies which sides of the bounds are clipped, unclipped if cleared
+ bool mClipped;
mat4 mMatrix;
DrawModifiers mDrawModifiers;
float mAlpha;
@@ -188,13 +204,23 @@
*/
virtual void resume();
+ ANDROID_API void setCountOverdrawEnabled(bool enabled) {
+ mCountOverdraw = enabled;
+ }
+
+ ANDROID_API float getOverdraw() {
+ return mCountOverdraw ? mOverdraw : 0.0f;
+ }
+
ANDROID_API status_t invokeFunctors(Rect& dirty);
ANDROID_API void detachFunctor(Functor* functor);
ANDROID_API void attachFunctor(Functor* functor);
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
ANDROID_API void pushLayerUpdate(Layer* layer);
+ ANDROID_API void cancelLayerUpdate(Layer* layer);
ANDROID_API void clearLayerUpdates();
+ ANDROID_API void flushLayerUpdates();
ANDROID_API int getSaveCount() const;
virtual int save(int flags);
@@ -228,8 +254,31 @@
virtual void concatMatrix(SkMatrix* matrix);
ANDROID_API const Rect& getClipBounds();
- ANDROID_API bool quickReject(float left, float top, float right, float bottom);
- bool quickRejectNoScissor(float left, float top, float right, float bottom);
+
+ /**
+ * Performs a quick reject but adjust the bounds to account for stroke width if necessary
+ */
+ bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint);
+
+ /**
+ * Returns false and sets scissor based upon bounds if drawing won't be clipped out
+ */
+ bool quickReject(float left, float top, float right, float bottom);
+ bool quickReject(const Rect& bounds) {
+ return quickReject(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
+
+ /**
+ * Same as quickReject, without the scissor, instead returning clipRequired through pointer.
+ * clipRequired will be only set if not rejected
+ */
+ ANDROID_API bool quickRejectNoScissor(float left, float top, float right, float bottom,
+ bool* clipRequired = NULL);
+ bool quickRejectNoScissor(const Rect& bounds, bool* clipRequired = NULL) {
+ return quickRejectNoScissor(bounds.left, bounds.top, bounds.right, bounds.bottom,
+ clipRequired);
+ }
+
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
virtual bool clipPath(SkPath* path, SkRegion::Op op);
virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
@@ -239,8 +288,8 @@
virtual void outputDisplayList(DisplayList* displayList);
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
- status_t drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
- const Rect& bounds, SkPaint* paint);
+ status_t drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
+ TextureVertex* vertices, bool transformed, const Rect& bounds, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -248,12 +297,12 @@
virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint);
virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
float* vertices, int* colors, SkPaint* paint);
- virtual status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ status_t drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
+ TextureVertex* vertices, uint32_t indexCount, SkPaint* paint);
+ virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
float left, float top, float right, float bottom, SkPaint* paint);
- status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
- float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode);
+ status_t drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
+ float left, float top, float right, float bottom, SkPaint* paint);
virtual status_t drawColor(int color, SkXfermode::Mode mode);
virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
virtual status_t drawRoundRect(float left, float top, float right, float bottom,
@@ -270,7 +319,7 @@
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length = -1.0f,
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
@@ -293,7 +342,7 @@
bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags);
void restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore = false);
- void setFullScreenClip();
+ void setupMergedMultiDraw(const Rect* clipRect);
const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
@@ -311,6 +360,9 @@
return mSnapshot->clipRegion->isEmpty();
}
+ int getViewportWidth() { return getSnapshot()->viewport.getWidth(); }
+ int getViewportHeight() { return getSnapshot()->viewport.getHeight(); }
+
/**
* Scales the alpha on the current snapshot. This alpha value will be modulated
* with other alpha values when drawing primitives.
@@ -579,18 +631,6 @@
void setStencilFromClip();
/**
- * Performs a quick reject but does not affect the scissor. Returns
- * the transformed rect to test and the current clip.
- */
- bool quickRejectNoScissor(float left, float top, float right, float bottom,
- Rect& transformed, Rect& clip);
-
- /**
- * Performs a quick reject but adjust the bounds to account for stroke width if necessary
- */
- bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint);
-
- /**
* Given the local bounds of the layer, calculates ...
*/
void calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer);
@@ -798,6 +838,12 @@
bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
bool ignoreScale = false, bool dirty = true);
+ void drawIndexedTextureMesh(float left, float top, float right, float bottom, GLuint texture,
+ float alpha, SkXfermode::Mode mode, bool blend,
+ GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
+ bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
+ bool ignoreScale = false, bool dirty = true);
+
void drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
@@ -808,12 +854,12 @@
*
* @param text The text to decor
* @param bytesCount The number of bytes in the text
- * @param length The length in pixels of the text, can be <= 0.0f to force a measurement
+ * @param totalAdvance The total advance in pixels, defines underline/strikethrough length
* @param x The x coordinate where the text will be drawn
* @param y The y coordinate where the text will be drawn
* @param paint The paint to draw the text with
*/
- void drawTextDecorations(const char* text, int bytesCount, float length,
+ void drawTextDecorations(const char* text, int bytesCount, float totalAdvance,
float x, float y, SkPaint* paint);
/**
@@ -868,7 +914,7 @@
* prior to calling this method.
*/
inline void bindTexture(GLuint texture) {
- glBindTexture(GL_TEXTURE_2D, texture);
+ mCaches.bindTexture(texture);
}
/**
@@ -876,7 +922,7 @@
* prior to calling this method.
*/
inline void bindExternalTexture(GLuint texture) {
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
+ mCaches.bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
}
/**
@@ -911,7 +957,6 @@
void setupDrawWithExternalTexture();
void setupDrawNoTexture();
void setupDrawAA();
- void setupDrawPoint(float pointSize);
void setupDrawColor(int color, int alpha);
void setupDrawColor(float r, float g, float b, float a);
void setupDrawAlpha8Color(int color, int alpha);
@@ -929,7 +974,6 @@
bool ignoreTransform = false, bool ignoreModelView = false);
void setupDrawModelViewTranslate(float left, float top, float right, float bottom,
bool ignoreTransform = false);
- void setupDrawPointUniforms();
void setupDrawColorUniforms();
void setupDrawPureColorUniforms();
void setupDrawShaderIdentityUniforms();
@@ -943,7 +987,7 @@
void setupDrawTextGammaUniforms();
void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors);
- void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords);
+ void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0);
void setupDrawVertices(GLvoid* vertices);
void finishDrawTexture();
void accountForClear(SkXfermode::Mode mode);
@@ -973,6 +1017,7 @@
void debugOverdraw(bool enable, bool clear);
void renderOverdraw();
+ void countOverdraw();
/**
* Should be invoked every time the glScissor is modified.
@@ -985,6 +1030,17 @@
return *mSnapshot->transform;
}
+ inline const UvMapper& getMapper(const Texture* texture) {
+ return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper;
+ }
+
+ /**
+ * Returns a texture object for the specified bitmap. The texture can
+ * come from the texture cache or an atlas. If this method returns
+ * NULL, the texture could not be found and/or allocated.
+ */
+ Texture* getTexture(SkBitmap* bitmap);
+
// Dimensions of the drawing surface
int mWidth, mHeight;
@@ -1010,6 +1066,9 @@
// Used to draw textured quads
TextureVertex mMeshVertices[4];
+ // Default UV mapper
+ const UvMapper mUvMapper;
+
// shader, filters, and shadow
DrawModifiers mDrawModifiers;
SkPaint mFilteredPaint;
@@ -1050,12 +1109,19 @@
// No-ops start/endTiling when set
bool mSuppressTiling;
+ // If true, this renderer will setup drawing to emulate
+ // an increment stencil buffer in the color buffer
+ bool mCountOverdraw;
+ float mOverdraw;
+
// Optional name of the renderer
String8 mName;
friend class DisplayListRenderer;
friend class Layer;
friend class TextSetupFunctor;
+ friend class DrawBitmapOp;
+ friend class DrawPatchOp;
}; // class OpenGLRenderer
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 45c619e..7656f85 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -20,9 +20,10 @@
#include <utils/Log.h>
-#include "Patch.h"
#include "Caches.h"
+#include "Patch.h"
#include "Properties.h"
+#include "UvMapper.h"
namespace android {
namespace uirenderer {
@@ -31,90 +32,61 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads):
- mXCount(xCount), mYCount(yCount), mEmptyQuads(emptyQuads) {
- // Initialized with the maximum number of vertices we will need
- // 2 triangles per patch, 3 vertices per triangle
- uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3;
- mVertices = new TextureVertex[maxVertices];
- mAllocatedVerticesCount = 0;
-
- verticesCount = 0;
- hasEmptyQuads = emptyQuads > 0;
-
- mColorKey = 0;
- mXDivs = new int32_t[mXCount];
- mYDivs = new int32_t[mYCount];
-
- PATCH_LOGD(" patch: xCount = %d, yCount = %d, emptyQuads = %d, max vertices = %d",
- xCount, yCount, emptyQuads, maxVertices);
-
- glGenBuffers(1, &meshBuffer);
+Patch::Patch(): vertices(NULL), verticesCount(0), indexCount(0), hasEmptyQuads(false) {
}
Patch::~Patch() {
- delete[] mVertices;
- delete[] mXDivs;
- delete[] mYDivs;
- glDeleteBuffers(1, &meshBuffer);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Patch management
-///////////////////////////////////////////////////////////////////////////////
-
-void Patch::copy(const int32_t* xDivs, const int32_t* yDivs) {
- memcpy(mXDivs, xDivs, mXCount * sizeof(int32_t));
- memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t));
-}
-
-void Patch::updateColorKey(const uint32_t colorKey) {
- mColorKey = colorKey;
-}
-
-bool Patch::matches(const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t colorKey, const int8_t emptyQuads) {
-
- bool matches = true;
-
- if (mEmptyQuads != emptyQuads) {
- mEmptyQuads = emptyQuads;
- hasEmptyQuads = emptyQuads > 0;
- matches = false;
- }
-
- if (mColorKey != colorKey) {
- updateColorKey(colorKey);
- matches = false;
- }
-
- if (memcmp(mXDivs, xDivs, mXCount * sizeof(int32_t))) {
- memcpy(mXDivs, xDivs, mXCount * sizeof(int32_t));
- matches = false;
- }
-
- if (memcmp(mYDivs, yDivs, mYCount * sizeof(int32_t))) {
- memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t));
- matches = false;
- }
-
- return matches;
}
///////////////////////////////////////////////////////////////////////////////
// Vertices management
///////////////////////////////////////////////////////////////////////////////
-void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
- float left, float top, float right, float bottom) {
- if (hasEmptyQuads) quads.clear();
+uint32_t Patch::getSize() const {
+ return verticesCount * sizeof(TextureVertex);
+}
- // Reset the vertices count here, we will count exactly how many
- // vertices we actually need when generating the quads
- verticesCount = 0;
+TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
+ float width, float height, const Res_png_9patch* patch) {
+ UvMapper mapper;
+ return createMesh(bitmapWidth, bitmapHeight, width, height, mapper, patch);
+}
- const uint32_t xStretchCount = (mXCount + 1) >> 1;
- const uint32_t yStretchCount = (mYCount + 1) >> 1;
+TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
+ float width, float height, const UvMapper& mapper, const Res_png_9patch* patch) {
+ if (vertices) return vertices;
+
+ const uint32_t* colors = &patch->colors[0];
+ const int8_t numColors = patch->numColors;
+
+ mColorKey = 0;
+ int8_t emptyQuads = 0;
+
+ if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
+ for (int8_t i = 0; i < numColors; i++) {
+ if (colors[i] == 0x0) {
+ emptyQuads++;
+ mColorKey |= 0x1 << i;
+ }
+ }
+ }
+
+ hasEmptyQuads = emptyQuads > 0;
+
+ uint32_t xCount = patch->numXDivs;
+ uint32_t yCount = patch->numYDivs;
+
+ uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 4;
+ if (maxVertices == 0) return NULL;
+
+ TextureVertex* tempVertices = new TextureVertex[maxVertices];
+ TextureVertex* vertex = tempVertices;
+
+ const int32_t* xDivs = patch->xDivs;
+ const int32_t* yDivs = patch->yDivs;
+
+ const uint32_t xStretchCount = (xCount + 1) >> 1;
+ const uint32_t yStretchCount = (yCount + 1) >> 1;
float stretchX = 0.0f;
float stretchY = 0.0f;
@@ -124,29 +96,28 @@
if (xStretchCount > 0) {
uint32_t stretchSize = 0;
- for (uint32_t i = 1; i < mXCount; i += 2) {
- stretchSize += mXDivs[i] - mXDivs[i - 1];
+ for (uint32_t i = 1; i < xCount; i += 2) {
+ stretchSize += xDivs[i] - xDivs[i - 1];
}
const float xStretchTex = stretchSize;
const float fixed = bitmapWidth - stretchSize;
- const float xStretch = fmaxf(right - left - fixed, 0.0f);
+ const float xStretch = fmaxf(width - fixed, 0.0f);
stretchX = xStretch / xStretchTex;
- rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(right - left, 0.0f) / fixed, 1.0f);
+ rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(width, 0.0f) / fixed, 1.0f);
}
if (yStretchCount > 0) {
uint32_t stretchSize = 0;
- for (uint32_t i = 1; i < mYCount; i += 2) {
- stretchSize += mYDivs[i] - mYDivs[i - 1];
+ for (uint32_t i = 1; i < yCount; i += 2) {
+ stretchSize += yDivs[i] - yDivs[i - 1];
}
const float yStretchTex = stretchSize;
const float fixed = bitmapHeight - stretchSize;
- const float yStretch = fmaxf(bottom - top - fixed, 0.0f);
+ const float yStretch = fmaxf(height - fixed, 0.0f);
stretchY = yStretch / yStretchTex;
- rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(bottom - top, 0.0f) / fixed, 1.0f);
+ rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(height, 0.0f) / fixed, 1.0f);
}
- TextureVertex* vertex = mVertices;
uint32_t quadCount = 0;
float previousStepY = 0.0f;
@@ -155,8 +126,10 @@
float y2 = 0.0f;
float v1 = 0.0f;
- for (uint32_t i = 0; i < mYCount; i++) {
- float stepY = mYDivs[i];
+ mUvMapper = mapper;
+
+ for (uint32_t i = 0; i < yCount; i++) {
+ float stepY = yDivs[i];
const float segment = stepY - previousStepY;
if (i & 1) {
@@ -170,15 +143,8 @@
v1 += vOffset / bitmapHeight;
if (stepY > 0.0f) {
-#if DEBUG_EXPLODE_PATCHES
- y1 += i * EXPLODE_GAP;
- y2 += i * EXPLODE_GAP;
-#endif
- generateRow(vertex, y1, y2, v1, v2, stretchX, rescaleX, right - left,
- bitmapWidth, quadCount);
-#if DEBUG_EXPLODE_PATCHES
- y2 -= i * EXPLODE_GAP;
-#endif
+ generateRow(xDivs, xCount, vertex, y1, y2, v1, v2, stretchX, rescaleX,
+ width, bitmapWidth, quadCount);
}
y1 = y2;
@@ -188,34 +154,25 @@
}
if (previousStepY != bitmapHeight) {
- y2 = bottom - top;
-#if DEBUG_EXPLODE_PATCHES
- y1 += mYCount * EXPLODE_GAP;
- y2 += mYCount * EXPLODE_GAP;
-#endif
- generateRow(vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, right - left,
- bitmapWidth, quadCount);
+ y2 = height;
+ generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX,
+ width, bitmapWidth, quadCount);
}
- if (verticesCount > 0) {
- Caches& caches = Caches::getInstance();
- caches.bindMeshBuffer(meshBuffer);
- if (mAllocatedVerticesCount < verticesCount) {
- glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount,
- mVertices, GL_DYNAMIC_DRAW);
- mAllocatedVerticesCount = verticesCount;
- } else {
- glBufferSubData(GL_ARRAY_BUFFER, 0,
- sizeof(TextureVertex) * verticesCount, mVertices);
- }
- caches.resetVertexPointers();
+ if (verticesCount == maxVertices) {
+ vertices = tempVertices;
+ } else {
+ vertices = new TextureVertex[verticesCount];
+ memcpy(vertices, tempVertices, verticesCount * sizeof(TextureVertex));
+ delete[] tempVertices;
}
- PATCH_LOGD(" patch: new vertices count = %d", verticesCount);
+ return vertices;
}
-void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2,
- float stretchX, float rescaleX, float width, float bitmapWidth, uint32_t& quadCount) {
+void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex,
+ float y1, float y2, float v1, float v2, float stretchX, float rescaleX,
+ float width, float bitmapWidth, uint32_t& quadCount) {
float previousStepX = 0.0f;
float x1 = 0.0f;
@@ -223,8 +180,8 @@
float u1 = 0.0f;
// Generate the row quad by quad
- for (uint32_t i = 0; i < mXCount; i++) {
- float stepX = mXDivs[i];
+ for (uint32_t i = 0; i < xCount; i++) {
+ float stepX = xDivs[i];
const float segment = stepX - previousStepX;
if (i & 1) {
@@ -238,14 +195,7 @@
u1 += uOffset / bitmapWidth;
if (stepX > 0.0f) {
-#if DEBUG_EXPLODE_PATCHES
- x1 += i * EXPLODE_GAP;
- x2 += i * EXPLODE_GAP;
-#endif
generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount);
-#if DEBUG_EXPLODE_PATCHES
- x2 -= i * EXPLODE_GAP;
-#endif
}
x1 = x2;
@@ -256,10 +206,6 @@
if (previousStepX != bitmapWidth) {
x2 = width;
-#if DEBUG_EXPLODE_PATCHES
- x1 += mXCount * EXPLODE_GAP;
- x2 += mXCount * EXPLODE_GAP;
-#endif
generateQuad(vertex, x1, y1, x2, y2, u1, v1, 1.0f, v2, quadCount);
}
}
@@ -290,18 +236,15 @@
quads.add(bounds);
}
- // Left triangle
+ mUvMapper.map(u1, v1, u2, v2);
+
TextureVertex::set(vertex++, x1, y1, u1, v1);
TextureVertex::set(vertex++, x2, y1, u2, v1);
TextureVertex::set(vertex++, x1, y2, u1, v2);
-
- // Right triangle
- TextureVertex::set(vertex++, x1, y2, u1, v2);
- TextureVertex::set(vertex++, x2, y1, u2, v1);
TextureVertex::set(vertex++, x2, y2, u2, v2);
- // A quad is made of 2 triangles, 6 vertices
- verticesCount += 6;
+ verticesCount += 4;
+ indexCount += 6;
#if DEBUG_PATCHES_VERTICES
PATCH_LOGD(" quad %d", oldQuadCount);
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index ee7bf70..246ba66 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -23,62 +23,51 @@
#include <utils/Vector.h>
+#include <androidfw/ResourceTypes.h>
+
#include "Rect.h"
+#include "UvMapper.h"
#include "Vertex.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define EXPLODE_GAP 4
-
-///////////////////////////////////////////////////////////////////////////////
// 9-patch structures
///////////////////////////////////////////////////////////////////////////////
-/**
- * An OpenGL patch. This contains an array of vertices and an array of
- * indices to render the vertices.
- */
struct Patch {
- Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads);
+ Patch();
~Patch();
- void updateVertices(const float bitmapWidth, const float bitmapHeight,
- float left, float top, float right, float bottom);
+ /**
+ * Returns the size of this patch's mesh in bytes.
+ */
+ uint32_t getSize() const;
- void updateColorKey(const uint32_t colorKey);
- void copy(const int32_t* xDivs, const int32_t* yDivs);
- bool matches(const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t colorKey, const int8_t emptyQuads);
-
- GLuint meshBuffer;
+ TextureVertex* vertices;
uint32_t verticesCount;
+ uint32_t indexCount;
bool hasEmptyQuads;
Vector<Rect> quads;
+ GLintptr offset;
+ GLintptr textureOffset;
+
+ TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
+ float width, float height, const Res_png_9patch* patch);
+ TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
+ float width, float height, const UvMapper& mapper, const Res_png_9patch* patch);
+
private:
- TextureVertex* mVertices;
- uint32_t mAllocatedVerticesCount;
-
- int32_t* mXDivs;
- int32_t* mYDivs;
- uint32_t mColorKey;
-
- uint32_t mXCount;
- uint32_t mYCount;
- int8_t mEmptyQuads;
-
- void generateRow(TextureVertex*& vertex, float y1, float y2,
- float v1, float v2, float stretchX, float rescaleX,
+ void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex,
+ float y1, float y2, float v1, float v2, float stretchX, float rescaleX,
float width, float bitmapWidth, uint32_t& quadCount);
- void generateQuad(TextureVertex*& vertex,
- float x1, float y1, float x2, float y2,
- float u1, float v1, float u2, float v2,
- uint32_t& quadCount);
+ void generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
+ float u1, float v1, float u2, float v2, uint32_t& quadCount);
+
+ uint32_t mColorKey;
+ UvMapper mUvMapper;
}; // struct Patch
}; // namespace uirenderer
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 62e38d3..dc0d98c 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -16,8 +16,10 @@
#define LOG_TAG "OpenGLRenderer"
+#include <utils/JenkinsHash.h>
#include <utils/Log.h>
+#include "Caches.h"
#include "PatchCache.h"
#include "Properties.h"
@@ -28,111 +30,243 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-PatchCache::PatchCache(): mMaxEntries(DEFAULT_PATCH_CACHE_SIZE) {
-}
-
-PatchCache::PatchCache(uint32_t maxEntries): mMaxEntries(maxEntries) {
+PatchCache::PatchCache():
+ mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
+ mMeshBuffer(0), mFreeBlocks(NULL), mGenerationId(0) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) {
+ INIT_LOGD(" Setting patch cache size to %skB", property);
+ mMaxSize = KB(atoi(property));
+ } else {
+ INIT_LOGD(" Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE);
+ mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE);
+ }
}
PatchCache::~PatchCache() {
clear();
}
+void PatchCache::init(Caches& caches) {
+ bool created = false;
+ if (!mMeshBuffer) {
+ glGenBuffers(1, &mMeshBuffer);
+ created = true;
+ }
+
+ caches.bindMeshBuffer(mMeshBuffer);
+ caches.resetVertexPointers();
+
+ if (created) {
+ createVertexBuffer();
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Caching
///////////////////////////////////////////////////////////////////////////////
-int PatchCache::PatchDescription::compare(
- const PatchCache::PatchDescription& lhs, const PatchCache::PatchDescription& rhs) {
- int deltaInt = lhs.bitmapWidth - rhs.bitmapWidth;
- if (deltaInt != 0) return deltaInt;
+hash_t PatchCache::PatchDescription::hash() const {
+ uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch));
+ hash = JenkinsHashMix(hash, mBitmapWidth);
+ hash = JenkinsHashMix(hash, mBitmapHeight);
+ hash = JenkinsHashMix(hash, mPixelWidth);
+ hash = JenkinsHashMix(hash, mPixelHeight);
+ return JenkinsHashWhiten(hash);
+}
- deltaInt = lhs.bitmapHeight - rhs.bitmapHeight;
- if (deltaInt != 0) return deltaInt;
-
- if (lhs.pixelWidth < rhs.pixelWidth) return -1;
- if (lhs.pixelWidth > rhs.pixelWidth) return +1;
-
- if (lhs.pixelHeight < rhs.pixelHeight) return -1;
- if (lhs.pixelHeight > rhs.pixelHeight) return +1;
-
- deltaInt = lhs.xCount - rhs.xCount;
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = lhs.yCount - rhs.yCount;
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = lhs.emptyCount - rhs.emptyCount;
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = lhs.colorKey - rhs.colorKey;
- if (deltaInt != 0) return deltaInt;
-
- return 0;
+int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
+ const PatchCache::PatchDescription& rhs) {
+ return memcmp(&lhs, &rhs, sizeof(PatchDescription));
}
void PatchCache::clear() {
- size_t count = mCache.size();
- for (size_t i = 0; i < count; i++) {
- delete mCache.valueAt(i);
+ clearCache();
+
+ if (mMeshBuffer) {
+ Caches::getInstance().unbindMeshBuffer();
+ glDeleteBuffers(1, &mMeshBuffer);
+ mMeshBuffer = 0;
+ mSize = 0;
}
- mCache.clear();
}
-Patch* PatchCache::get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
- const float pixelWidth, const float pixelHeight,
- const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
- const uint32_t width, const uint32_t height, const int8_t numColors) {
+void PatchCache::clearCache() {
+ LruCache<PatchDescription, Patch*>::Iterator i(mCache);
+ while (i.next()) {
+ delete i.value();
+ }
+ mCache.clear();
- int8_t transparentQuads = 0;
- uint32_t colorKey = 0;
+ BufferBlock* block = mFreeBlocks;
+ while (block) {
+ BufferBlock* next = block->next;
+ delete block;
+ block = next;
+ }
+ mFreeBlocks = NULL;
+}
- if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
- for (int8_t i = 0; i < numColors; i++) {
- if (colors[i] == 0x0) {
- transparentQuads++;
- colorKey |= 0x1 << i;
- }
+void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
+ LruCache<PatchDescription, Patch*>::Iterator i(mCache);
+ while (i.next()) {
+ const PatchDescription& key = i.key();
+ if (key.getPatch() == patch) {
+ patchesToRemove.push(patch_pair_t(&key, i.value()));
}
}
+}
- // If the 9patch is made of only transparent quads
- if (transparentQuads == int8_t((width + 1) * (height + 1))) {
- return NULL;
+void PatchCache::removeDeferred(Res_png_9patch* patch) {
+ Mutex::Autolock _l(mLock);
+ mGarbage.push(patch);
+}
+
+void PatchCache::clearGarbage() {
+ Vector<patch_pair_t> patchesToRemove;
+
+ { // scope for the mutex
+ Mutex::Autolock _l(mLock);
+ size_t count = mGarbage.size();
+ for (size_t i = 0; i < count; i++) {
+ remove(patchesToRemove, mGarbage[i]);
+ }
+ mGarbage.clear();
}
- const PatchDescription description(bitmapWidth, bitmapHeight,
- pixelWidth, pixelHeight, width, height, transparentQuads, colorKey);
+ // TODO: We could sort patchesToRemove by offset to merge
+ // adjacent free blocks
+ for (size_t i = 0; i < patchesToRemove.size(); i++) {
+ const patch_pair_t& pair = patchesToRemove[i];
- ssize_t index = mCache.indexOfKey(description);
- Patch* mesh = NULL;
- if (index >= 0) {
- mesh = mCache.valueAt(index);
+ // Add a new free block to the list
+ const Patch* patch = pair.getSecond();
+ BufferBlock* block = new BufferBlock(patch->offset, patch->getSize());
+ block->next = mFreeBlocks;
+ mFreeBlocks = block;
+
+ mSize -= patch->getSize();
+
+ mCache.remove(*pair.getFirst());
}
+#if DEBUG_PATCHES
+ if (patchesToRemove.size() > 0) {
+ dumpFreeBlocks("Removed garbage");
+ }
+#endif
+}
+
+void PatchCache::createVertexBuffer() {
+ glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
+ mSize = 0;
+ mFreeBlocks = new BufferBlock(0, mMaxSize);
+ mGenerationId++;
+}
+
+/**
+ * Sets the mesh's offsets and copies its associated vertices into
+ * the mesh buffer (VBO).
+ */
+void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
+ // This call ensures the VBO exists and that it is bound
+ init(Caches::getInstance());
+
+ // If we're running out of space, let's clear the entire cache
+ uint32_t size = newMesh->getSize();
+ if (mSize + size > mMaxSize) {
+ clearCache();
+ createVertexBuffer();
+ }
+
+ // Find a block where we can fit the mesh
+ BufferBlock* previous = NULL;
+ BufferBlock* block = mFreeBlocks;
+ while (block) {
+ // The mesh fits
+ if (block->size >= size) {
+ break;
+ }
+ previous = block;
+ block = block->next;
+ }
+
+ // We have enough space left in the buffer, but it's
+ // too fragmented, let's clear the cache
+ if (!block) {
+ clearCache();
+ createVertexBuffer();
+ previous = NULL;
+ block = mFreeBlocks;
+ }
+
+ // Copy the 9patch mesh in the VBO
+ newMesh->offset = (GLintptr) (block->offset);
+ newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
+ glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
+
+ // Remove the block since we've used it entirely
+ if (block->size == size) {
+ if (previous) {
+ previous->next = block->next;
+ } else {
+ mFreeBlocks = block->next;
+ }
+ } else {
+ // Resize the block now that it's occupied
+ block->offset += size;
+ block->size -= size;
+ }
+
+ mSize += size;
+}
+
+const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
+ const uint32_t bitmapWidth, const uint32_t bitmapHeight,
+ const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
+
+ const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
+ const Patch* mesh = mCache.get(description);
+
if (!mesh) {
- PATCH_LOGD("New patch mesh "
- "xCount=%d yCount=%d, w=%.2f h=%.2f, bw=%.2f bh=%.2f",
- width, height, pixelWidth, pixelHeight, bitmapWidth, bitmapHeight);
+ Patch* newMesh = new Patch();
+ TextureVertex* vertices;
- mesh = new Patch(width, height, transparentQuads);
- mesh->updateColorKey(colorKey);
- mesh->copy(xDivs, yDivs);
- mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight);
-
- if (mCache.size() >= mMaxEntries) {
- delete mCache.valueAt(mCache.size() - 1);
- mCache.removeItemsAt(mCache.size() - 1, 1);
+ if (entry) {
+ // An atlas entry has a UV mapper
+ vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
+ pixelWidth, pixelHeight, entry->uvMapper, patch);
+ } else {
+ vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
+ pixelWidth, pixelHeight, patch);
}
- mCache.add(description, mesh);
- } else if (!mesh->matches(xDivs, yDivs, colorKey, transparentQuads)) {
- PATCH_LOGD("Patch mesh does not match, refreshing vertices");
- mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight);
+ if (vertices) {
+ setupMesh(newMesh, vertices);
+ }
+
+#if DEBUG_PATCHES
+ dumpFreeBlocks("Adding patch");
+#endif
+
+ mCache.put(description, newMesh);
+ return newMesh;
}
return mesh;
}
+#if DEBUG_PATCHES
+void PatchCache::dumpFreeBlocks(const char* prefix) {
+ String8 dump;
+ BufferBlock* block = mFreeBlocks;
+ while (block) {
+ dump.appendFormat("->(%d, %d)", block->offset, block->size);
+ block = block->next;
+ }
+ ALOGD("%s: Free blocks%s", prefix, dump.string());
+}
+#endif
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 0822cba..9f2c9a5 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -17,10 +17,16 @@
#ifndef ANDROID_HWUI_PATCH_CACHE_H
#define ANDROID_HWUI_PATCH_CACHE_H
-#include <utils/KeyedVector.h>
+#include <GLES2/gl2.h>
+#include <utils/LruCache.h>
+
+#include <androidfw/ResourceTypes.h>
+
+#include "AssetAtlas.h"
#include "Debug.h"
#include "Patch.h"
+#include "utils/Pair.h"
namespace android {
namespace uirenderer {
@@ -40,45 +46,64 @@
// Cache
///////////////////////////////////////////////////////////////////////////////
+class Caches;
+
class PatchCache {
public:
PatchCache();
- PatchCache(uint32_t maxCapacity);
~PatchCache();
+ void init(Caches& caches);
- Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
- const float pixelWidth, const float pixelHeight,
- const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
- const uint32_t width, const uint32_t height, const int8_t numColors);
+ const Patch* get(const AssetAtlas::Entry* entry,
+ const uint32_t bitmapWidth, const uint32_t bitmapHeight,
+ const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch);
void clear();
uint32_t getSize() const {
- return mCache.size();
+ return mSize;
}
uint32_t getMaxSize() const {
- return mMaxEntries;
+ return mMaxSize;
}
-private:
+ GLuint getMeshBuffer() const {
+ return mMeshBuffer;
+ }
+
+ uint32_t getGenerationId() const {
+ return mGenerationId;
+ }
+
/**
- * Description of a patch.
+ * Removes the entries associated with the specified 9-patch. This is meant
+ * to be called from threads that are not the EGL context thread (GC thread
+ * on the VM side for instance.)
*/
+ void removeDeferred(Res_png_9patch* patch);
+
+ /**
+ * Process deferred removals.
+ */
+ void clearGarbage();
+
+
+private:
struct PatchDescription {
- PatchDescription(): bitmapWidth(0), bitmapHeight(0), pixelWidth(0), pixelHeight(0),
- xCount(0), yCount(0), emptyCount(0), colorKey(0) {
+ PatchDescription(): mPatch(NULL), mBitmapWidth(0), mBitmapHeight(0),
+ mPixelWidth(0), mPixelHeight(0) {
}
PatchDescription(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
- const float pixelWidth, const float pixelHeight,
- const uint32_t xCount, const uint32_t yCount,
- const int8_t emptyCount, const uint32_t colorKey):
- bitmapWidth(bitmapWidth), bitmapHeight(bitmapHeight),
- pixelWidth(pixelWidth), pixelHeight(pixelHeight),
- xCount(xCount), yCount(yCount),
- emptyCount(emptyCount), colorKey(colorKey) {
+ const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch):
+ mPatch(patch), mBitmapWidth(bitmapWidth), mBitmapHeight(bitmapHeight),
+ mPixelWidth(pixelWidth), mPixelHeight(pixelHeight) {
}
+ hash_t hash() const;
+
+ const Res_png_9patch* getPatch() const { return mPatch; }
+
static int compare(const PatchDescription& lhs, const PatchDescription& rhs);
bool operator==(const PatchDescription& other) const {
@@ -99,21 +124,63 @@
return PatchDescription::compare(lhs, rhs);
}
+ friend inline hash_t hash_type(const PatchDescription& entry) {
+ return entry.hash();
+ }
+
private:
- uint32_t bitmapWidth;
- uint32_t bitmapHeight;
- float pixelWidth;
- float pixelHeight;
- uint32_t xCount;
- uint32_t yCount;
- int8_t emptyCount;
- uint32_t colorKey;
+ const Res_png_9patch* mPatch;
+ uint32_t mBitmapWidth;
+ uint32_t mBitmapHeight;
+ float mPixelWidth;
+ float mPixelHeight;
}; // struct PatchDescription
- uint32_t mMaxEntries;
- KeyedVector<PatchDescription, Patch*> mCache;
+ /**
+ * A buffer block represents an empty range in the mesh buffer
+ * that can be used to store vertices.
+ *
+ * The patch cache maintains a linked-list of buffer blocks
+ * to track available regions of memory in the VBO.
+ */
+ struct BufferBlock {
+ BufferBlock(uint32_t offset, uint32_t size): offset(offset), size(size), next(NULL) {
+ }
+ uint32_t offset;
+ uint32_t size;
+
+ BufferBlock* next;
+ }; // struct BufferBlock
+
+ typedef Pair<const PatchDescription*, Patch*> patch_pair_t;
+
+ void clearCache();
+ void createVertexBuffer();
+
+ void setupMesh(Patch* newMesh, TextureVertex* vertices);
+
+ void remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch);
+
+#if DEBUG_PATCHES
+ void dumpFreeBlocks(const char* prefix);
+#endif
+
+ uint32_t mMaxSize;
+ uint32_t mSize;
+
+ LruCache<PatchDescription, Patch*> mCache;
+
+ GLuint mMeshBuffer;
+ // First available free block inside the mesh buffer
+ BufferBlock* mFreeBlocks;
+
+ uint32_t mGenerationId;
+
+ // Garbage tracking, required to handle GC events on the VM side
+ Vector<Res_png_9patch*> mGarbage;
+ mutable Mutex mLock;
}; // class PatchCache
}; // namespace uirenderer
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index fdb10e2..70ab6e7 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -139,7 +139,7 @@
static PathTexture* createTexture(float left, float top, float offset,
uint32_t width, uint32_t height, uint32_t id) {
- PathTexture* texture = new PathTexture();
+ PathTexture* texture = new PathTexture(Caches::getInstance());
texture->left = left;
texture->top = top;
texture->offset = offset;
@@ -223,7 +223,7 @@
}
if (texture->id) {
- glDeleteTextures(1, &texture->id);
+ Caches::getInstance().deleteTexture(texture->id);
}
delete texture;
}
@@ -300,7 +300,7 @@
glGenTextures(1, &texture->id);
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ Caches::getInstance().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
@@ -350,8 +350,7 @@
// Paths
///////////////////////////////////////////////////////////////////////////////
-void PathCache::remove(const path_pair_t& pair) {
- Vector<PathDescription> pathsToRemove;
+void PathCache::remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair) {
LruCache<PathDescription, PathTexture*>::Iterator i(mCache);
while (i.next()) {
@@ -362,10 +361,6 @@
pathsToRemove.push(key);
}
}
-
- for (size_t i = 0; i < pathsToRemove.size(); i++) {
- mCache.remove(pathsToRemove.itemAt(i));
- }
}
void PathCache::removeDeferred(SkPath* path) {
@@ -374,12 +369,20 @@
}
void PathCache::clearGarbage() {
- Mutex::Autolock l(mLock);
- size_t count = mGarbage.size();
- for (size_t i = 0; i < count; i++) {
- remove(mGarbage.itemAt(i));
+ Vector<PathDescription> pathsToRemove;
+
+ { // scope for the mutex
+ Mutex::Autolock l(mLock);
+ size_t count = mGarbage.size();
+ for (size_t i = 0; i < count; i++) {
+ remove(pathsToRemove, mGarbage.itemAt(i));
+ }
+ mGarbage.clear();
}
- mGarbage.clear();
+
+ for (size_t i = 0; i < pathsToRemove.size(); i++) {
+ mCache.remove(pathsToRemove.itemAt(i));
+ }
}
/**
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index dd1f996..16d20a8 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -58,7 +58,7 @@
* Alpha texture used to represent a path.
*/
struct PathTexture: public Texture {
- PathTexture(): Texture() {
+ PathTexture(Caches& caches): Texture(caches) {
}
~PathTexture() {
@@ -269,7 +269,7 @@
* Removes an entry.
* The pair must define first=path, second=sourcePath
*/
- void remove(const path_pair_t& pair);
+ void remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair);
/**
* Ensures there is enough space in the cache for a texture of the specified
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 0879b1b..3970913 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -66,11 +66,11 @@
}
}
-inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
+inline static void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
}
-inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
+inline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
}
@@ -84,7 +84,7 @@
*
* NOTE: assumes angles between normals 90 degrees or less
*/
-inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
+inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
}
@@ -224,6 +224,20 @@
DEBUG_DUMP_BUFFER();
}
+static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
+ const vec2& normal, Vertex* buffer, int& currentIndex, bool begin) {
+ vec2 strokeOffset = normal;
+ paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
+
+ vec2 referencePoint(center.position[0], center.position[1]);
+ if (paintInfo.cap == SkPaint::kSquare_Cap) {
+ referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1);
+ }
+
+ Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
+ Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
+}
+
/**
* Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
*
@@ -235,19 +249,17 @@
const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
const int extra = paintInfo.capExtraDivisions();
const int allocSize = (vertices.size() + extra) * 2;
-
Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
+ const int lastIndex = vertices.size() - 1;
if (extra > 0) {
// tessellate both round caps
- const int last = vertices.size() - 1;
float beginTheta = atan2(
- - (vertices[0].position[0] - vertices[1].position[0]),
- vertices[0].position[1] - vertices[1].position[1]);
+ - (vertices[0].position[0] - vertices[1].position[0]),
+ vertices[0].position[1] - vertices[1].position[1]);
float endTheta = atan2(
- - (vertices[last].position[0] - vertices[last - 1].position[0]),
- vertices[last].position[1] - vertices[last - 1].position[1]);
-
+ - (vertices[lastIndex].position[0] - vertices[lastIndex - 1].position[0]),
+ vertices[lastIndex].position[1] - vertices[lastIndex - 1].position[1]);
const float dTheta = PI / (extra + 1);
const float radialScale = 2.0f / (1 + cos(dTheta));
@@ -270,56 +282,45 @@
vec2 endRadialOffset(cos(endTheta), sin(endTheta));
paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
Vertex::set(&buffer[allocSize - 1 - capOffset],
- vertices[last].position[0] + endRadialOffset.x,
- vertices[last].position[1] + endRadialOffset.y);
+ vertices[lastIndex].position[0] + endRadialOffset.x,
+ vertices[lastIndex].position[1] + endRadialOffset.y);
}
}
int currentIndex = extra;
- const Vertex* current = &(vertices[0]);
- vec2 lastNormal;
- for (unsigned int i = 0; i < vertices.size() - 1; i++) {
+ const Vertex* last = &(vertices[0]);
+ const Vertex* current = &(vertices[1]);
+ vec2 lastNormal(current->position[1] - last->position[1],
+ last->position[0] - current->position[0]);
+ lastNormal.normalize();
+
+ storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
+
+ for (unsigned int i = 1; i < vertices.size() - 1; i++) {
const Vertex* next = &(vertices[i + 1]);
vec2 nextNormal(next->position[1] - current->position[1],
current->position[0] - next->position[0]);
nextNormal.normalize();
- vec2 totalOffset;
- if (i == 0) {
- totalOffset = nextNormal;
- } else {
- totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- }
- paintInfo.scaleOffsetForStrokeWidth(totalOffset);
+ vec2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- Vertex::set(&buffer[currentIndex++],
- current->position[0] + totalOffset.x,
- current->position[1] + totalOffset.y);
-
- Vertex::set(&buffer[currentIndex++],
- current->position[0] - totalOffset.x,
- current->position[1] - totalOffset.y);
+ vec2 center(current->position[0], current->position[1]);
+ Vertex::set(&buffer[currentIndex++], center + strokeOffset);
+ Vertex::set(&buffer[currentIndex++], center - strokeOffset);
current = next;
lastNormal = nextNormal;
}
- vec2 totalOffset = lastNormal;
- paintInfo.scaleOffsetForStrokeWidth(totalOffset);
-
- Vertex::set(&buffer[currentIndex++],
- current->position[0] + totalOffset.x,
- current->position[1] + totalOffset.y);
- Vertex::set(&buffer[currentIndex++],
- current->position[0] - totalOffset.x,
- current->position[1] - totalOffset.y);
+ storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
DEBUG_DUMP_BUFFER();
}
/**
* Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
- *
+ *
* 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
* the shape (using 2 * perimeter.size() vertices)
*
@@ -389,7 +390,7 @@
* For explanation of constants and general methodoloyg, see comments for
* getStrokeVerticesFromUnclosedVerticesAA() below.
*/
-inline void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
+inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
AlphaVertex* buffer, bool isFirst, vec2 normal, int offset) {
const int extra = paintInfo.capExtraDivisions();
const int extraOffset = (extra + 1) / 2;
@@ -772,11 +773,67 @@
}
}
+static void expandRectToCoverVertex(SkRect& rect, float x, float y) {
+ rect.fLeft = fminf(rect.fLeft, x);
+ rect.fTop = fminf(rect.fTop, y);
+ rect.fRight = fmaxf(rect.fRight, x);
+ rect.fBottom = fmaxf(rect.fBottom, y);
+}
static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) {
- rect.fLeft = fminf(rect.fLeft, vertex.position[0]);
- rect.fTop = fminf(rect.fTop, vertex.position[1]);
- rect.fRight = fmaxf(rect.fRight, vertex.position[0]);
- rect.fBottom = fmaxf(rect.fBottom, vertex.position[1]);
+ expandRectToCoverVertex(rect, vertex.position[0], vertex.position[1]);
+}
+
+template <class TYPE>
+static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
+ const float* points, int count, SkRect& bounds) {
+ bounds.set(points[0], points[1], points[0], points[1]);
+
+ int numPoints = count / 2;
+ int verticesPerPoint = srcBuffer.getVertexCount();
+ dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
+
+ for (int i = 0; i < count; i += 2) {
+ expandRectToCoverVertex(bounds, points[i + 0], points[i + 1]);
+ dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
+ }
+ dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
+}
+
+void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* paint,
+ const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+ const PaintInfo paintInfo(paint, transform);
+
+ // determine point shape
+ SkPath path;
+ float radius = paintInfo.halfStrokeWidth;
+ if (radius == 0.0f) radius = 0.25f;
+
+ if (paintInfo.cap == SkPaint::kRound_Cap) {
+ path.addCircle(0, 0, radius);
+ } else {
+ path.addRect(-radius, -radius, radius, radius);
+ }
+
+ // calculate outline
+ Vector<Vertex> outlineVertices;
+ approximatePathOutlineVertices(path, true,
+ paintInfo.inverseScaleX * paintInfo.inverseScaleX,
+ paintInfo.inverseScaleY * paintInfo.inverseScaleY, outlineVertices);
+
+ if (!outlineVertices.size()) return;
+
+ // tessellate, then duplicate outline across points
+ int numPoints = count / 2;
+ VertexBuffer tempBuffer;
+ if (!paintInfo.isAA) {
+ getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
+ instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
+ } else {
+ getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer);
+ instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
+ }
+
+ expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke
}
void PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint,
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index 596d49d..85797fc 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -30,7 +30,7 @@
public:
VertexBuffer():
mBuffer(0),
- mSize(0),
+ mVertexCount(0),
mCleanupMethod(NULL)
{}
@@ -44,30 +44,42 @@
multiple regions within a single VertexBuffer, such as with PathTessellator::tesselateLines()
*/
template <class TYPE>
- TYPE* alloc(int size) {
- if (mSize) {
+ TYPE* alloc(int vertexCount) {
+ if (mVertexCount) {
TYPE* reallocBuffer = (TYPE*)mReallocBuffer;
// already have allocated the buffer, re-allocate space within
if (mReallocBuffer != mBuffer) {
// not first re-allocation, leave space for degenerate triangles to separate strips
reallocBuffer += 2;
}
- mReallocBuffer = reallocBuffer + size;
+ mReallocBuffer = reallocBuffer + vertexCount;
return reallocBuffer;
}
- mSize = size;
- mReallocBuffer = mBuffer = (void*)new TYPE[size];
+ mVertexCount = vertexCount;
+ mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
mCleanupMethod = &(cleanup<TYPE>);
return (TYPE*)mBuffer;
}
- void* getBuffer() const { return mBuffer; }
- unsigned int getSize() const { return mSize; }
+ template <class TYPE>
+ void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
+ int verticesToCopy = srcBuffer.getVertexCount();
+
+ TYPE* dst = alloc<TYPE>(verticesToCopy);
+ TYPE* src = (TYPE*)srcBuffer.getBuffer();
+
+ for (int i = 0; i < verticesToCopy; i++) {
+ TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset);
+ }
+ }
+
+ void* getBuffer() const { return mBuffer; } // shouldn't be const, since not a const ptr?
+ unsigned int getVertexCount() const { return mVertexCount; }
template <class TYPE>
void createDegenerateSeparators(int allocSize) {
- TYPE* end = (TYPE*)mBuffer + mSize;
+ TYPE* end = (TYPE*)mBuffer + mVertexCount;
for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) {
memcpy(degen, degen - 1, sizeof(TYPE));
memcpy(degen + 1, degen + 2, sizeof(TYPE));
@@ -81,7 +93,7 @@
}
void* mBuffer;
- unsigned int mSize;
+ unsigned int mVertexCount;
void* mReallocBuffer; // used for multi-allocation
@@ -95,6 +107,9 @@
static void tessellatePath(const SkPath& path, const SkPaint* paint,
const mat4 *transform, VertexBuffer& vertexBuffer);
+ static void tessellatePoints(const float* points, int count, SkPaint* paint,
+ const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+
static void tessellateLines(const float* points, int count, SkPaint* paint,
const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index 8280370..36e89c6 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include "Caches.h"
+#include "Debug.h"
#include "Extensions.h"
#include "PixelBuffer.h"
#include "Properties.h"
@@ -113,6 +114,14 @@
if (mAccessMode == kAccessMode_None) {
mCaches.bindPixelBuffer(mBuffer);
mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
+#if DEBUG_OPENGL
+ if (!mMappedPointer) {
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ ALOGE("Could not map GPU pixel buffer: 0x%x", status);
+ }
+ }
+#endif
mAccessMode = mode;
}
@@ -123,7 +132,10 @@
if (mAccessMode != kAccessMode_None) {
if (mMappedPointer) {
mCaches.bindPixelBuffer(mBuffer);
- glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+ GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+ if (status == GL_FALSE) {
+ ALOGE("Corrupted GPU pixel buffer");
+ }
}
mAccessMode = kAccessMode_None;
mMappedPointer = NULL;
@@ -147,14 +159,8 @@
///////////////////////////////////////////////////////////////////////////////
PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
- bool gpuBuffer = type == kBufferType_Auto && Extensions::getInstance().getMajorGlVersion() >= 3;
- if (gpuBuffer) {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "false") > 0) {
- if (!strcmp(property, "true")) {
- return new GpuPixelBuffer(format, width, height);
- }
- }
+ if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) {
+ return new GpuPixelBuffer(format, width, height);
}
return new CpuPixelBuffer(format, width, height);
}
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 14a2376..9e4670e 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -15,6 +15,9 @@
*/
#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <utils/Trace.h>
#include "Program.h"
@@ -25,7 +28,6 @@
// Base program
///////////////////////////////////////////////////////////////////////////////
-// TODO: Program instance should be created from a factory method
Program::Program(const ProgramDescription& description, const char* vertex, const char* fragment) {
mInitialized = false;
mHasColorUniform = false;
@@ -50,7 +52,9 @@
texCoords = -1;
}
+ ATRACE_BEGIN("linkProgram");
glLinkProgram(mProgramId);
+ ATRACE_END();
GLint status;
glGetProgramiv(mProgramId, GL_LINK_STATUS, &status);
@@ -87,6 +91,9 @@
Program::~Program() {
if (mInitialized) {
+ // This would ideally happen after linking the program
+ // but Tegra drivers, especially when perfhud is enabled,
+ // sometimes crash if we do so
glDetachShader(mProgramId, mVertexShader);
glDetachShader(mProgramId, mFragmentShader);
@@ -132,6 +139,8 @@
}
GLuint Program::buildShader(const char* source, GLenum type) {
+ ATRACE_CALL();
+
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, 0);
glCompileShader(shader);
@@ -153,20 +162,24 @@
void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
const mat4& transformMatrix, bool offset) {
- mat4 p(projectionMatrix);
- if (offset) {
- // offset screenspace xy by an amount that compensates for typical precision
- // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted
- // up and to the left.
- // This offset value is based on an assumption that some hardware may use as
- // little as 12.4 precision, so we offset by slightly more than 1/16.
- p.translate(.065, .065, 0);
+ if (projectionMatrix != mProjection) {
+ if (CC_LIKELY(!offset)) {
+ glUniformMatrix4fv(projection, 1, GL_FALSE, &projectionMatrix.data[0]);
+ } else {
+ mat4 p(projectionMatrix);
+ // offset screenspace xy by an amount that compensates for typical precision
+ // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted
+ // up and to the left.
+ // This offset value is based on an assumption that some hardware may use as
+ // little as 12.4 precision, so we offset by slightly more than 1/16.
+ p.translate(.065, .065);
+ glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]);
+ }
+ mProjection = projectionMatrix;
}
mat4 t(transformMatrix);
t.multiply(modelViewMatrix);
-
- glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]);
glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
}
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index e8b6d47..4f94afc 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -68,23 +68,22 @@
#define PROGRAM_BITMAP_WRAPS_SHIFT 9
#define PROGRAM_BITMAP_WRAPT_SHIFT 11
-#define PROGRAM_GRADIENT_TYPE_SHIFT 33
+#define PROGRAM_GRADIENT_TYPE_SHIFT 33 // 2 bits for gradient type
#define PROGRAM_MODULATE_SHIFT 35
-#define PROGRAM_IS_POINT_SHIFT 36
+#define PROGRAM_HAS_AA_SHIFT 36
-#define PROGRAM_HAS_AA_SHIFT 37
+#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 37
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 38
-#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
-#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
+#define PROGRAM_HAS_GAMMA_CORRECTION 39
-#define PROGRAM_HAS_GAMMA_CORRECTION 40
+#define PROGRAM_IS_SIMPLE_GRADIENT 40
-#define PROGRAM_IS_SIMPLE_GRADIENT 41
+#define PROGRAM_HAS_COLORS 41
-#define PROGRAM_HAS_COLORS 42
-
-#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
+#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
+#define PROGRAM_EMULATE_STENCIL 43
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -156,13 +155,11 @@
SkXfermode::Mode framebufferMode;
bool swapSrcDst;
- bool isPoint;
- float pointSize;
-
bool hasGammaCorrection;
float gamma;
bool hasDebugHighlight;
+ bool emulateStencil;
/**
* Resets this description. All fields are reset back to the default
@@ -199,9 +196,6 @@
framebufferMode = SkXfermode::kClear_Mode;
swapSrcDst = false;
- isPoint = false;
- pointSize = 0.0f;
-
hasGammaCorrection = false;
gamma = 2.2f;
@@ -267,7 +261,6 @@
key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
- if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
@@ -275,6 +268,7 @@
if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
+ if (emulateStencil) key |= programid(0x1) << PROGRAM_EMULATE_STENCIL;
return key;
}
@@ -430,10 +424,13 @@
bool mUse;
bool mInitialized;
+ // Uniforms caching
bool mHasColorUniform;
int mColorUniform;
bool mHasSampler;
+
+ mat4 mProjection;
}; // class Program
}; // namespace uirenderer
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 8eb85e5..a5ce6f6 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -53,8 +53,6 @@
const char* gVS_Header_Uniforms =
"uniform mat4 projection;\n" \
"uniform mat4 transform;\n";
-const char* gVS_Header_Uniforms_IsPoint =
- "uniform mediump float pointSize;\n";
const char* gVS_Header_Uniforms_HasGradient =
"uniform mat4 screenSpace;\n";
const char* gVS_Header_Uniforms_HasBitmap =
@@ -68,8 +66,6 @@
"varying float alpha;\n";
const char* gVS_Header_Varyings_HasBitmap =
"varying highp vec2 outBitmapTexCoords;\n";
-const char* gVS_Header_Varyings_PointHasBitmap =
- "varying highp vec2 outPointBitmapTexCoords;\n";
const char* gVS_Header_Varyings_HasGradient[6] = {
// Linear
"varying highp vec2 linear;\n"
@@ -118,12 +114,8 @@
};
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
-const char* gVS_Main_OutPointBitmapTexCoords =
- " outPointBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
const char* gVS_Main_Position =
" gl_Position = projection * transform * position;\n";
-const char* gVS_Main_PointSize =
- " gl_PointSize = pointSize;\n";
const char* gVS_Main_AAVertexShape =
" alpha = vtxAlpha;\n";
const char* gVS_Footer =
@@ -141,9 +133,6 @@
"precision mediump float;\n\n";
const char* gFS_Uniforms_Color =
"uniform vec4 color;\n";
-const char* gFS_Header_Uniforms_PointHasBitmap =
- "uniform vec2 textureDimension;\n"
- "uniform float pointSize;\n";
const char* gFS_Uniforms_TextureSampler =
"uniform sampler2D baseSampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
@@ -178,10 +167,6 @@
"\nvoid main(void) {\n"
" lowp vec4 fragColor;\n";
-const char* gFS_Main_PointBitmapTexCoords =
- " highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
- "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
-
const char* gFS_Main_Dither[2] = {
// ES 2.0
"texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
@@ -342,6 +327,12 @@
};
const char* gFS_Main_DebugHighlight =
" gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n";
+const char* gFS_Main_EmulateStencil =
+ " gl_FragColor.rgba = vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 1.0);\n"
+ " return;\n"
+ " /*\n";
+const char* gFS_Footer_EmulateStencil =
+ " */\n";
const char* gFS_Footer =
"}\n\n";
@@ -478,9 +469,6 @@
if (description.hasBitmap) {
shader.append(gVS_Header_Uniforms_HasBitmap);
}
- if (description.isPoint) {
- shader.append(gVS_Header_Uniforms_IsPoint);
- }
// Varyings
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
@@ -495,9 +483,7 @@
shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
}
if (description.hasBitmap) {
- shader.append(description.isPoint ?
- gVS_Header_Varyings_PointHasBitmap :
- gVS_Header_Varyings_HasBitmap);
+ shader.append(gVS_Header_Varyings_HasBitmap);
}
// Begin the shader
@@ -514,12 +500,7 @@
shader.append(gVS_Main_OutColors);
}
if (description.hasBitmap) {
- shader.append(description.isPoint ?
- gVS_Main_OutPointBitmapTexCoords :
- gVS_Main_OutBitmapTexCoords);
- }
- if (description.isPoint) {
- shader.append(gVS_Main_PointSize);
+ shader.append(gVS_Main_OutBitmapTexCoords);
}
// Output transformed position
shader.append(gVS_Main_Position);
@@ -570,9 +551,7 @@
shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
}
if (description.hasBitmap) {
- shader.append(description.isPoint ?
- gVS_Header_Varyings_PointHasBitmap :
- gVS_Header_Varyings_HasBitmap);
+ shader.append(gVS_Header_Varyings_HasBitmap);
}
// Uniforms
@@ -593,9 +572,6 @@
shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
gFS_Uniforms_Dither);
}
- if (description.hasBitmap && description.isPoint) {
- shader.append(gFS_Header_Uniforms_PointHasBitmap);
- }
if (description.hasGammaCorrection) {
shader.append(gFS_Uniforms_Gamma);
}
@@ -603,7 +579,7 @@
// Optimization for common cases
if (!description.isAA && !blendFramebuffer && !description.hasColors &&
description.colorOp == ProgramDescription::kColorNone &&
- !description.isPoint && !description.hasDebugHighlight) {
+ !description.hasDebugHighlight && !description.emulateStencil) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -683,6 +659,9 @@
// Begin the shader
shader.append(gFS_Main); {
+ if (description.emulateStencil) {
+ shader.append(gFS_Main_EmulateStencil);
+ }
// Stores the result in fragColor directly
if (description.hasTexture || description.hasExternalTexture) {
if (description.hasAlpha8Texture) {
@@ -703,9 +682,6 @@
shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
}
if (description.hasBitmap) {
- if (description.isPoint) {
- shader.append(gFS_Main_PointBitmapTexCoords);
- }
if (!description.isBitmapNpot) {
shader.append(gFS_Main_FetchBitmap);
} else {
@@ -757,6 +733,9 @@
shader.append(gFS_Main_DebugHighlight);
}
}
+ if (description.emulateStencil) {
+ shader.append(gFS_Footer_EmulateStencil);
+ }
// End the shader
shader.append(gFS_Footer);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 6eea00c..86b0aa2 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -71,9 +71,15 @@
/**
* Used to enable/disable overdraw debugging. The accepted values are
- * "true" and "false". The default value is "false".
+ * "show", "count" and "false". The default value is "false".
*/
-#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.show_overdraw"
+#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.overdraw"
+
+/**
+ * Used to enable/disable PerfHUD ES profiling. The accepted values
+ * are "true" and "false". The default value is "false".
+ */
+#define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling"
/**
* Used to enable/disable non-rectangular clipping debugging.
@@ -123,9 +129,9 @@
/**
* Indicates whether PBOs can be used to back pixel buffers.
- * Accepted values are "true" and "false".
+ * Accepted values are "true" and "false". Default is true.
*/
-#define PROPERTY_ENABLE_GPU_PIXEL_BUFFERS "hwui.use_gpu_pixel_buffers"
+#define PROPERTY_ENABLE_GPU_PIXEL_BUFFERS "ro.hwui.use_gpu_pixel_buffers"
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
@@ -133,6 +139,7 @@
#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
+#define PROPERTY_PATCH_CACHE_SIZE "ro.hwui.patch_cache_size"
#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
@@ -178,7 +185,7 @@
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
#define DEFAULT_PATH_CACHE_SIZE 10.0f
-#define DEFAULT_PATCH_CACHE_SIZE 512
+#define DEFAULT_PATCH_CACHE_SIZE 128 // in kB
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
#define DEFAULT_FBO_CACHE_SIZE 16
@@ -195,6 +202,8 @@
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
+// Converts a number of kilo-bytes into bytes
+#define KB(s) s * 1024
static DebugLevel readDebugLevel() {
char property[PROPERTY_VALUE_MAX];
diff --git a/libs/hwui/Query.h b/libs/hwui/Query.h
new file mode 100644
index 0000000..e25b16b
--- /dev/null
+++ b/libs/hwui/Query.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_QUERY_H
+#define ANDROID_HWUI_QUERY_H
+
+#include <GLES3/gl3.h>
+
+#include "Extensions.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A Query instance can be used to perform occlusion queries. If the device
+ * does not support occlusion queries, the result of a query will always be
+ * 0 and the result will always be marked available.
+ *
+ * To run an occlusion query successfully, you must start end end the query:
+ *
+ * Query query;
+ * query.begin();
+ * // execute OpenGL calls
+ * query.end();
+ * GLuint result = query.getResult();
+ */
+class Query {
+public:
+ /**
+ * Possible query targets.
+ */
+ enum Target {
+ /**
+ * Indicates if any sample passed the depth & stencil tests.
+ */
+ kTargetSamples = GL_ANY_SAMPLES_PASSED,
+ /**
+ * Indicates if any sample passed the depth & stencil tests.
+ * The implementation may choose to use a less precise version
+ * of the test, potentially resulting in false positives.
+ */
+ kTargetConservativeSamples = GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
+ };
+
+ /**
+ * Creates a new query with the specified target. The default
+ * target is kTargetSamples (of GL_ANY_SAMPLES_PASSED in OpenGL.)
+ */
+ Query(Target target = kTargetSamples): mActive(false), mTarget(target),
+ mCanQuery(Extensions::getInstance().hasOcclusionQueries()),
+ mQuery(0) {
+ }
+
+ ~Query() {
+ if (mQuery) {
+ glDeleteQueries(1, &mQuery);
+ }
+ }
+
+ /**
+ * Begins the query. If the query has already begun or if the device
+ * does not support occlusion queries, calling this method as no effect.
+ * After calling this method successfully, the query is marked active.
+ */
+ void begin() {
+ if (!mActive && mCanQuery) {
+ if (!mQuery) {
+ glGenQueries(1, &mQuery);
+ }
+
+ glBeginQuery(mTarget, mQuery);
+ mActive = true;
+ }
+ }
+
+ /**
+ * Ends the query. If the query has already begun or if the device
+ * does not support occlusion queries, calling this method as no effect.
+ * After calling this method successfully, the query is marked inactive.
+ */
+ void end() {
+ if (mQuery && mActive) {
+ glEndQuery(mTarget);
+ mActive = false;
+ }
+ }
+
+ /**
+ * Returns true if the query is active, false otherwise.
+ */
+ bool isActive() {
+ return mActive;
+ }
+
+ /**
+ * Returns true if the result of the query is available,
+ * false otherwise. Calling getResult() before the result
+ * is available may result in the calling thread being blocked.
+ * If the device does not support queries, this method always
+ * returns true.
+ */
+ bool isResultAvailable() {
+ if (!mQuery) return true;
+
+ GLuint result;
+ glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT_AVAILABLE, &result);
+ return result == GL_TRUE;
+ }
+
+ /**
+ * Returns the result of the query. If the device does not
+ * support queries this method will return 0.
+ *
+ * Calling this method implicitely calls end() if the query
+ * is currently active.
+ */
+ GLuint getResult() {
+ if (!mQuery) return 0;
+
+ end();
+
+ GLuint result;
+ glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT, &result);
+ return result;
+ }
+
+
+private:
+ bool mActive;
+ GLenum mTarget;
+ bool mCanQuery;
+ GLuint mQuery;
+
+}; // class Query
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_QUERY_H
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index f50ac3c..7531769 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -24,6 +24,10 @@
namespace android {
namespace uirenderer {
+#define RECT_STRING "%7.2f %7.2f %7.2f %7.2f"
+#define RECT_ARGS(r) \
+ (r).left, (r).top, (r).right, (r).bottom
+
///////////////////////////////////////////////////////////////////////////////
// Structs
///////////////////////////////////////////////////////////////////////////////
@@ -125,11 +129,11 @@
return intersect(r.left, r.top, r.right, r.bottom);
}
- inline bool contains(float l, float t, float r, float b) {
+ inline bool contains(float l, float t, float r, float b) const {
return l >= left && t >= top && r <= right && b <= bottom;
}
- inline bool contains(const Rect& r) {
+ inline bool contains(const Rect& r) const {
return contains(r.left, r.top, r.right, r.bottom);
}
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 347bd78..3f77021 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "OpenGLRenderer"
+
#include <SkPixelRef.h>
#include "ResourceCache.h"
#include "Caches.h"
@@ -60,7 +62,7 @@
}
void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
- SkSafeRef(bitmapResource->pixelRef());
+ bitmapResource->pixelRef()->globalRef();
SkSafeRef(bitmapResource->getColorTable());
incrementRefcount((void*) bitmapResource, kBitmap);
}
@@ -79,6 +81,10 @@
incrementRefcount((void*) filterResource, kColorFilter);
}
+void ResourceCache::incrementRefcount(Res_png_9patch* patchResource) {
+ incrementRefcount((void*) patchResource, kNinePatch);
+}
+
void ResourceCache::incrementRefcount(Layer* layerResource) {
incrementRefcount((void*) layerResource, kLayer);
}
@@ -94,7 +100,7 @@
}
void ResourceCache::incrementRefcountLocked(SkBitmap* bitmapResource) {
- SkSafeRef(bitmapResource->pixelRef());
+ bitmapResource->pixelRef()->globalRef();
SkSafeRef(bitmapResource->getColorTable());
incrementRefcountLocked((void*) bitmapResource, kBitmap);
}
@@ -113,6 +119,10 @@
incrementRefcountLocked((void*) filterResource, kColorFilter);
}
+void ResourceCache::incrementRefcountLocked(Res_png_9patch* patchResource) {
+ incrementRefcountLocked((void*) patchResource, kNinePatch);
+}
+
void ResourceCache::incrementRefcountLocked(Layer* layerResource) {
incrementRefcountLocked((void*) layerResource, kLayer);
}
@@ -123,7 +133,7 @@
}
void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) {
- SkSafeUnref(bitmapResource->pixelRef());
+ bitmapResource->pixelRef()->globalUnref();
SkSafeUnref(bitmapResource->getColorTable());
decrementRefcount((void*) bitmapResource);
}
@@ -142,6 +152,10 @@
decrementRefcount((void*) filterResource);
}
+void ResourceCache::decrementRefcount(Res_png_9patch* patchResource) {
+ decrementRefcount((void*) patchResource);
+}
+
void ResourceCache::decrementRefcount(Layer* layerResource) {
decrementRefcount((void*) layerResource);
}
@@ -160,7 +174,7 @@
}
void ResourceCache::decrementRefcountLocked(SkBitmap* bitmapResource) {
- SkSafeUnref(bitmapResource->pixelRef());
+ bitmapResource->pixelRef()->globalUnref();
SkSafeUnref(bitmapResource->getColorTable());
decrementRefcountLocked((void*) bitmapResource);
}
@@ -179,6 +193,10 @@
decrementRefcountLocked((void*) filterResource);
}
+void ResourceCache::decrementRefcountLocked(Res_png_9patch* patchResource) {
+ decrementRefcountLocked((void*) patchResource);
+}
+
void ResourceCache::decrementRefcountLocked(Layer* layerResource) {
decrementRefcountLocked((void*) layerResource);
}
@@ -265,6 +283,30 @@
}
}
+void ResourceCache::destructor(Res_png_9patch* resource) {
+ Mutex::Autolock _l(mLock);
+ destructorLocked(resource);
+}
+
+void ResourceCache::destructorLocked(Res_png_9patch* resource) {
+ ssize_t index = mCache->indexOfKey(resource);
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
+ if (ref == NULL) {
+ if (Caches::hasInstance()) {
+ Caches::getInstance().patchCache.removeDeferred(resource);
+ }
+ // If we're not tracking this resource, just delete it
+ // A Res_png_9patch is actually an array of byte that's larger
+ // than sizeof(Res_png_9patch). It must be freed as an array.
+ delete[] (int8_t*) resource;
+ return;
+ }
+ ref->destroyed = true;
+ if (ref->refCount == 0) {
+ deleteResourceReferenceLocked(resource, ref);
+ }
+}
+
/**
* Return value indicates whether resource was actually recycled, which happens when RefCnt
* reaches 0.
@@ -335,6 +377,16 @@
delete filter;
}
break;
+ case kNinePatch: {
+ if (Caches::hasInstance()) {
+ Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
+ }
+ // A Res_png_9patch is actually an array of byte that's larger
+ // than sizeof(Res_png_9patch). It must be freed as an array.
+ int8_t* patch = (int8_t*) resource;
+ delete[] patch;
+ }
+ break;
case kLayer: {
Layer* layer = (Layer*) resource;
Caches::getInstance().deleteLayerDeferred(layer);
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index ab493e5..ea0c1b5 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -22,7 +22,11 @@
#include <SkBitmap.h>
#include <SkiaColorFilter.h>
#include <SkiaShader.h>
+
#include <utils/KeyedVector.h>
+
+#include <androidfw/ResourceTypes.h>
+
#include "Layer.h"
namespace android {
@@ -35,6 +39,7 @@
kBitmap,
kShader,
kColorFilter,
+ kNinePatch,
kPath,
kLayer
};
@@ -69,35 +74,41 @@
void incrementRefcount(SkBitmap* resource);
void incrementRefcount(SkiaShader* resource);
void incrementRefcount(SkiaColorFilter* resource);
+ void incrementRefcount(Res_png_9patch* resource);
void incrementRefcount(Layer* resource);
void incrementRefcountLocked(SkPath* resource);
void incrementRefcountLocked(SkBitmap* resource);
void incrementRefcountLocked(SkiaShader* resource);
void incrementRefcountLocked(SkiaColorFilter* resource);
+ void incrementRefcountLocked(Res_png_9patch* resource);
void incrementRefcountLocked(Layer* resource);
void decrementRefcount(SkBitmap* resource);
void decrementRefcount(SkPath* resource);
void decrementRefcount(SkiaShader* resource);
void decrementRefcount(SkiaColorFilter* resource);
+ void decrementRefcount(Res_png_9patch* resource);
void decrementRefcount(Layer* resource);
void decrementRefcountLocked(SkBitmap* resource);
void decrementRefcountLocked(SkPath* resource);
void decrementRefcountLocked(SkiaShader* resource);
void decrementRefcountLocked(SkiaColorFilter* resource);
+ void decrementRefcountLocked(Res_png_9patch* resource);
void decrementRefcountLocked(Layer* resource);
void destructor(SkPath* resource);
void destructor(SkBitmap* resource);
void destructor(SkiaShader* resource);
void destructor(SkiaColorFilter* resource);
+ void destructor(Res_png_9patch* resource);
void destructorLocked(SkPath* resource);
void destructorLocked(SkBitmap* resource);
void destructorLocked(SkiaShader* resource);
void destructorLocked(SkiaColorFilter* resource);
+ void destructorLocked(Res_png_9patch* resource);
bool recycle(SkBitmap* resource);
bool recycleLocked(SkBitmap* resource);
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index c38eedb..797ed10 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -69,9 +69,13 @@
mGenerationId = shader.mGenerationId;
}
+SkiaShader::SkiaShader(): mCaches(NULL) {
+}
+
SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
- mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) {
+ mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
+ mCaches(NULL) {
setMatrix(matrix);
mGenerationId = 0;
}
@@ -87,7 +91,7 @@
}
void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ mCaches->bindTexture(texture->id);
texture->setWrapST(wrapS, wrapT);
}
@@ -114,7 +118,7 @@
}
void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
- Texture* texture = mTextureCache->get(mBitmap);
+ Texture* texture = mCaches->textureCache.get(mBitmap);
if (!texture) return;
mTexture = texture;
@@ -229,7 +233,7 @@
GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
+ Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
@@ -349,7 +353,7 @@
GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
+ Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
@@ -359,7 +363,7 @@
bindUniformColor(program->getUniform("endColor"), mColors[1]);
}
- Caches::getInstance().dither.setupProgram(program, textureUnit);
+ mCaches->dither.setupProgram(program, textureUnit);
mat4 screenSpace;
computeScreenSpaceMatrix(screenSpace, modelView);
@@ -394,12 +398,6 @@
return copy;
}
-void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) {
- SkiaShader::set(textureCache, gradientCache);
- mFirst->set(textureCache, gradientCache);
- mSecond->set(textureCache, gradientCache);
-}
-
void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
mFirst->describe(description, extensions);
mSecond->describe(description, extensions);
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index bc12b0d..a63431c 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -33,6 +33,8 @@
namespace android {
namespace uirenderer {
+class Caches;
+
///////////////////////////////////////////////////////////////////////////////
// Base shader
///////////////////////////////////////////////////////////////////////////////
@@ -77,9 +79,8 @@
return mType;
}
- virtual void set(TextureCache* textureCache, GradientCache* gradientCache) {
- mTextureCache = textureCache;
- mGradientCache = gradientCache;
+ virtual void setCaches(Caches& caches) {
+ mCaches = &caches;
}
uint32_t getGenerationId() {
@@ -103,8 +104,7 @@
void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView);
protected:
- SkiaShader() {
- }
+ SkiaShader();
/**
* The appropriate texture unit must have been activated prior to invoking
@@ -118,8 +118,7 @@
SkShader::TileMode mTileY;
bool mBlend;
- TextureCache* mTextureCache;
- GradientCache* mGradientCache;
+ Caches* mCaches;
mat4 mUnitMatrix;
mat4 mShaderMatrix;
@@ -229,7 +228,11 @@
~SkiaComposeShader();
SkiaShader* copy();
- void set(TextureCache* textureCache, GradientCache* gradientCache);
+ 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,
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 6976eaa..0b2c130 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -18,6 +18,7 @@
#include <utils/JenkinsHash.h>
+#include "Caches.h"
#include "Debug.h"
#include "TextDropShadowCache.h"
#include "Properties.h"
@@ -154,7 +155,7 @@
ALOGD("Shadow texture deleted, size = %d", texture->bitmapSize);
}
- glDeleteTextures(1, &texture->id);
+ texture->deleteTexture();
delete texture;
}
}
@@ -182,7 +183,9 @@
return NULL;
}
- texture = new ShadowTexture;
+ Caches& caches = Caches::getInstance();
+
+ texture = new ShadowTexture(caches);
texture->left = shadow.penX;
texture->top = shadow.penY;
texture->width = shadow.width;
@@ -202,7 +205,7 @@
glGenTextures(1, &texture->id);
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ caches.bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index 0bed72b6..04d7357 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -30,6 +30,8 @@
namespace android {
namespace uirenderer {
+class Caches;
+
struct ShadowText {
ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(NULL),
flags(0), italicStyle(0.0f), scaleX(0), text(NULL), positions(NULL) {
@@ -114,7 +116,7 @@
* Alpha texture used to represent a shadow.
*/
struct ShadowTexture: public Texture {
- ShadowTexture(): Texture() {
+ ShadowTexture(Caches& caches): Texture(caches) {
}
float left;
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
new file mode 100644
index 0000000..7923ce7
--- /dev/null
+++ b/libs/hwui/Texture.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include "Caches.h"
+#include "Texture.h"
+
+namespace android {
+namespace uirenderer {
+
+Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0),
+ cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
+ mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
+ mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
+ mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) {
+}
+
+Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0),
+ cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
+ mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
+ mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
+ mFirstFilter(true), mFirstWrap(true), mCaches(caches) {
+}
+
+void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
+ GLenum renderTarget) {
+
+ if (mFirstWrap || force || wrapS != mWrapS || wrapT != mWrapT) {
+ mFirstWrap = false;
+
+ mWrapS = wrapS;
+ mWrapT = wrapT;
+
+ if (bindTexture) {
+ mCaches.bindTexture(renderTarget, id);
+ }
+
+ glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
+ glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
+ }
+}
+
+void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force,
+ GLenum renderTarget) {
+
+ if (mFirstFilter || force || min != mMinFilter || mag != mMagFilter) {
+ mFirstFilter = false;
+
+ mMinFilter = min;
+ mMagFilter = mag;
+
+ if (bindTexture) {
+ mCaches.bindTexture(renderTarget, id);
+ }
+
+ if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
+
+ glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
+ glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
+ }
+}
+
+void Texture::deleteTexture() const {
+ mCaches.deleteTexture(id);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 8d88bdc..d48ec59 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -22,75 +22,39 @@
namespace android {
namespace uirenderer {
+class Caches;
+class UvMapper;
+
/**
* Represents an OpenGL texture.
*/
-struct Texture {
- Texture() {
- cleanup = false;
- bitmapSize = 0;
+class Texture {
+public:
+ Texture();
+ Texture(Caches& caches);
- wrapS = GL_CLAMP_TO_EDGE;
- wrapT = GL_CLAMP_TO_EDGE;
+ virtual ~Texture() { }
- minFilter = GL_NEAREST;
- magFilter = GL_NEAREST;
-
- mipMap = false;
-
- firstFilter = true;
- firstWrap = true;
-
- id = 0;
- }
-
- void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
+ inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
GLenum renderTarget = GL_TEXTURE_2D) {
setWrapST(wrap, wrap, bindTexture, force, renderTarget);
}
- void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false,
- GLenum renderTarget = GL_TEXTURE_2D) {
+ virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D);
- if (firstWrap || force || wrapS != this->wrapS || wrapT != this->wrapT) {
- firstWrap = false;
-
- this->wrapS = wrapS;
- this->wrapT = wrapT;
-
- if (bindTexture) {
- glBindTexture(renderTarget, id);
- }
-
- glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
- glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
- }
- }
-
- void setFilter(GLenum filter, bool bindTexture = false, bool force = false,
+ inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false,
GLenum renderTarget = GL_TEXTURE_2D) {
setFilterMinMag(filter, filter, bindTexture, force, renderTarget);
}
- void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, bool force = false,
- GLenum renderTarget = GL_TEXTURE_2D) {
+ virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D);
- if (firstFilter || force || min != minFilter || mag != magFilter) {
- firstFilter = false;
-
- minFilter = min;
- magFilter = mag;
-
- if (bindTexture) {
- glBindTexture(renderTarget, id);
- }
-
- if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
-
- glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
- glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
- }
- }
+ /**
+ * Convenience method to call glDeleteTextures() on this texture's id.
+ */
+ void deleteTexture() const;
/**
* Name of the texture.
@@ -125,21 +89,28 @@
*/
bool mipMap;
+ /**
+ * Optional, pointer to a texture coordinates mapper.
+ */
+ const UvMapper* uvMapper;
+
private:
/**
* Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE.
*/
- GLenum wrapS;
- GLenum wrapT;
+ GLenum mWrapS;
+ GLenum mWrapT;
/**
* Last filters set on this texture. Defaults to GL_NEAREST.
*/
- GLenum minFilter;
- GLenum magFilter;
+ GLenum mMinFilter;
+ GLenum mMagFilter;
- bool firstFilter;
- bool firstWrap;
+ bool mFirstFilter;
+ bool mFirstWrap;
+
+ Caches& mCaches;
}; // struct Texture
class AutoTexture {
@@ -147,7 +118,7 @@
AutoTexture(const Texture* texture): mTexture(texture) { }
~AutoTexture() {
if (mTexture && mTexture->cleanup) {
- glDeleteTextures(1, &mTexture->id);
+ mTexture->deleteTexture();
delete mTexture;
}
}
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 2378eb5..a63cac6 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -112,7 +112,7 @@
if (mDebugEnabled) {
ALOGD("Texture deleted, size = %d", texture->bitmapSize);
}
- glDeleteTextures(1, &texture->id);
+ texture->deleteTexture();
delete texture;
}
}
@@ -139,7 +139,7 @@
}
}
- texture = new Texture;
+ texture = new Texture();
texture->bitmapSize = size;
generateTexture(bitmap, texture, false);
@@ -162,7 +162,7 @@
}
Texture* TextureCache::getTransient(SkBitmap* bitmap) {
- Texture* texture = new Texture;
+ Texture* texture = new Texture();
texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
texture->cleanup = true;
@@ -235,7 +235,7 @@
texture->width = bitmap->width();
texture->height = bitmap->height();
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ Caches::getInstance().bindTexture(texture->id);
switch (bitmap->getConfig()) {
case SkBitmap::kA8_Config:
diff --git a/libs/hwui/UvMapper.h b/libs/hwui/UvMapper.h
new file mode 100644
index 0000000..70428d2
--- /dev/null
+++ b/libs/hwui/UvMapper.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_UV_MAPPER_H
+#define ANDROID_HWUI_UV_MAPPER_H
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * This class can be used to map UV coordinates from the [0..1]
+ * range to other arbitrary ranges. All the methods below assume
+ * that the input values lie in the [0..1] range already.
+ */
+class UvMapper {
+public:
+ /**
+ * Using this constructor is equivalent to not using any mapping at all.
+ * UV coordinates in the [0..1] range remain in the [0..1] range.
+ */
+ UvMapper(): mIdentity(true), mMinU(0.0f), mMaxU(1.0f), mMinV(0.0f), mMaxV(1.0f) {
+ }
+
+ /**
+ * Creates a new mapper with the specified ranges for U and V coordinates.
+ * The parameter minU must be < maxU and minV must be < maxV.
+ */
+ UvMapper(float minU, float maxU, float minV, float maxV):
+ mMinU(minU), mMaxU(maxU), mMinV(minV), mMaxV(maxV) {
+ checkIdentity();
+ }
+
+ /**
+ * Returns true if calling the map*() methods has no effect (that is,
+ * texture coordinates remain in the 0..1 range.)
+ */
+ bool isIdentity() const {
+ return mIdentity;
+ }
+
+ /**
+ * Changes the U and V mapping ranges.
+ * The parameter minU must be < maxU and minV must be < maxV.
+ */
+ void setMapping(float minU, float maxU, float minV, float maxV) {
+ mMinU = minU;
+ mMaxU = maxU;
+ mMinV = minV;
+ mMaxV = maxV;
+ checkIdentity();
+ }
+
+ /**
+ * Maps a single value in the U range.
+ */
+ void mapU(float& u) const {
+ if (!mIdentity) u = lerp(mMinU, mMaxU, u);
+ }
+
+ /**
+ * Maps a single value in the V range.
+ */
+ void mapV(float& v) const {
+ if (!mIdentity) v = lerp(mMinV, mMaxV, v);
+ }
+
+ /**
+ * Maps the specified rectangle in place. This method assumes:
+ * - left = min. U
+ * - top = min. V
+ * - right = max. U
+ * - bottom = max. V
+ */
+ void map(Rect& texCoords) const {
+ if (!mIdentity) {
+ texCoords.left = lerp(mMinU, mMaxU, texCoords.left);
+ texCoords.right = lerp(mMinU, mMaxU, texCoords.right);
+ texCoords.top = lerp(mMinV, mMaxV, texCoords.top);
+ texCoords.bottom = lerp(mMinV, mMaxV, texCoords.bottom);
+ }
+ }
+
+ /**
+ * Maps the specified UV coordinates in place.
+ */
+ void map(float& u1, float& v1, float& u2, float& v2) const {
+ if (!mIdentity) {
+ u1 = lerp(mMinU, mMaxU, u1);
+ u2 = lerp(mMinU, mMaxU, u2);
+ v1 = lerp(mMinV, mMaxV, v1);
+ v2 = lerp(mMinV, mMaxV, v2);
+ }
+ }
+
+ void dump() const {
+ ALOGD("mapper[minU=%.2f maxU=%.2f minV=%.2f maxV=%.2f]", mMinU, mMaxU, mMinV, mMaxV);
+ }
+
+private:
+ static float lerp(float start, float stop, float amount) {
+ return start + (stop - start) * amount;
+ }
+
+ void checkIdentity() {
+ mIdentity = mMinU == 0.0f && mMaxU == 1.0f && mMinV == 0.0f && mMaxV == 1.0f;
+ }
+
+ bool mIdentity;
+ float mMinU;
+ float mMaxU;
+ float mMinV;
+ float mMaxV;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_UV_MAPPER_H
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index 523120e..c06762f 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HWUI_VERTEX_H
#define ANDROID_HWUI_VERTEX_H
+#include "Vector.h"
+
namespace android {
namespace uirenderer {
@@ -30,6 +32,15 @@
vertex[0].position[0] = x;
vertex[0].position[1] = y;
}
+
+ static inline void set(Vertex* vertex, vec2 val) {
+ set(vertex, val.x, val.y);
+ }
+
+ static inline void copyWithOffset(Vertex* vertex, const Vertex& src, float x, float y) {
+ set(vertex, src.position[0] + x, src.position[1] + y);
+ }
+
}; // struct Vertex
/**
@@ -81,6 +92,12 @@
vertex[0].alpha = alpha;
}
+ static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src,
+ float x, float y) {
+ Vertex::set(vertex, src.position[0] + x, src.position[1] + y);
+ vertex[0].alpha = src.alpha;
+ }
+
static inline void setColor(AlphaVertex* vertex, float alpha) {
vertex[0].alpha = alpha;
}
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 6c5267d..5f15724 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -17,6 +17,7 @@
#include <SkGlyph.h>
#include "CacheTexture.h"
+#include "../Caches.h"
#include "../Debug.h"
#include "../Extensions.h"
#include "../PixelBuffer.h"
@@ -110,7 +111,8 @@
CacheTexture::CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCount) :
mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
mLinearFiltering(false), mDirty(false), mNumGlyphs(0),
- mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount) {
+ mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount),
+ mCaches(Caches::getInstance()) {
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
@@ -154,7 +156,7 @@
mTexture = NULL;
}
if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
+ mCaches.deleteTexture(mTextureId);
mTextureId = 0;
}
mDirty = false;
@@ -166,7 +168,7 @@
mLinearFiltering = linearFiltering;
const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
- if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
+ if (bind) mCaches.bindTexture(getTextureId());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
}
@@ -186,7 +188,7 @@
if (!mTextureId) {
glGenTextures(1, &mTextureId);
- glBindTexture(GL_TEXTURE_2D, mTextureId);
+ mCaches.bindTexture(mTextureId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Initialize texture dimensions
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mWidth, mHeight, 0,
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index ddcc836..208b1ff 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -30,6 +30,7 @@
namespace android {
namespace uirenderer {
+class Caches;
class PixelBuffer;
/**
@@ -150,9 +151,9 @@
float x3, float y3, float u3, float v3,
float x4, float y4, float u4, float v4) {
TextureVertex* mesh = mMesh + mCurrentQuad * 4;
- TextureVertex::set(mesh++, x1, y1, u1, v1);
TextureVertex::set(mesh++, x2, y2, u2, v2);
TextureVertex::set(mesh++, x3, y3, u3, v3);
+ TextureVertex::set(mesh++, x1, y1, u1, v1);
TextureVertex::set(mesh++, x4, y4, u4, v4);
mCurrentQuad++;
}
@@ -178,9 +179,10 @@
TextureVertex* mMesh;
uint32_t mCurrentQuad;
uint32_t mMaxQuadCount;
+ Caches& mCaches;
CacheBlock* mCacheBlocks;
- Rect mDirtyRect;
bool mHasES3;
+ Rect mDirtyRect;
};
}; // namespace uirenderer
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 011cfc1..18983d8 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -55,6 +55,7 @@
mStyle = paint->getStyle();
mStrokeWidth = paint->getStrokeWidth();
mAntiAliasing = paint->isAntiAlias();
+ mHinting = paint->getHinting();
mLookupTransform.reset();
mLookupTransform[SkMatrix::kMScaleX] = roundf(fmaxf(1.0f, matrix[mat4::kScaleX]));
mLookupTransform[SkMatrix::kMScaleY] = roundf(fmaxf(1.0f, matrix[mat4::kScaleY]));
@@ -80,6 +81,7 @@
hash = JenkinsHashMix(hash, android::hash_type(mStyle));
hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth));
hash = JenkinsHashMix(hash, int(mAntiAliasing));
+ hash = JenkinsHashMix(hash, android::hash_type(mHinting));
hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX]));
hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY]));
return JenkinsHashWhiten(hash);
@@ -111,6 +113,9 @@
deltaInt = int(lhs.mAntiAliasing) - int(rhs.mAntiAliasing);
if (deltaInt != 0) return deltaInt;
+ deltaInt = int(lhs.mHinting) - int(rhs.mHinting);
+ if (deltaInt != 0) return deltaInt;
+
if (lhs.mLookupTransform[SkMatrix::kMScaleX] <
rhs.mLookupTransform[SkMatrix::kMScaleX]) return -1;
if (lhs.mLookupTransform[SkMatrix::kMScaleX] >
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 52cca1c..9e7ec2d 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -69,6 +69,7 @@
uint8_t mStyle;
float mStrokeWidth;
bool mAntiAliasing;
+ uint8_t mHinting;
SkMatrix mLookupTransform;
SkMatrix mInverseLookupTransform;
};
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index 477314b..f2a216f 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -43,7 +43,7 @@
/**
* Returns true if this task manager can run tasks,
* false otherwise. This method will typically return
- * true on a single CPU core device.
+ * false on a single CPU core device.
*/
bool canRunTasks() const;
diff --git a/location/Android.mk b/location/Android.mk
index 12db2f7..feeb8ce 100644
--- a/location/Android.mk
+++ b/location/Android.mk
@@ -14,6 +14,4 @@
LOCAL_PATH := $(call my-dir)
-ifeq ($(TARGET_BUILD_APPS),)
include $(call all-makefiles-under, $(LOCAL_PATH))
-endif
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 8a5a739..150c289 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -35,6 +35,7 @@
import com.android.internal.location.ILocationProvider;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
+import com.android.internal.util.FastPrintWriter;
/**
* Base class for location providers implemented as unbundled services.
@@ -106,7 +107,7 @@
}
@Override
public void dump(FileDescriptor fd, String[] args) {
- PrintWriter pw = new PrintWriter(new FileOutputStream(fd));
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
onDump(fd, pw, args);
pw.flush();
}
diff --git a/media/java/android/drm/mobile1/DrmConstraintInfo.java b/media/java/android/drm/mobile1/DrmConstraintInfo.java
deleted file mode 100644
index 50ae8bd..0000000
--- a/media/java/android/drm/mobile1/DrmConstraintInfo.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.
- */
-
-package android.drm.mobile1;
-
-import java.util.Date;
-
-/**
- * This class provides interfaces to access the DRM constraint.
- */
-public class DrmConstraintInfo {
- /**
- * The constraint of count.
- */
- private int count;
-
- /**
- * The constraint of start date.
- */
- private long startDate;
-
- /**
- * The constraint of end date.
- */
- private long endDate;
-
- /**
- * The constraint of interval.
- */
- private long interval;
-
- /**
- * Construct the DrmConstraint.
- */
- DrmConstraintInfo() {
- count = -1;
- startDate = -1;
- endDate = -1;
- interval = -1;
- }
-
- /**
- * Get the count constraint.
- *
- * @return the count or -1 if no limit.
- */
- public int getCount() {
- return count;
- }
-
- /**
- * Get the start date constraint.
- *
- * @return the start date or null if no limit.
- */
- public Date getStartDate() {
- if (startDate == -1)
- return null;
-
- return new Date(startDate);
- }
-
- /**
- * Get the end date constraint.
- *
- * @return the end date or null if no limit.
- */
- public Date getEndDate() {
- if (endDate == -1)
- return null;
-
- return new Date(endDate);
- }
-
- /**
- * Get the Interval constraint.
- *
- * @return the interval or -1 if no limit.
- */
- public long getInterval() {
- return interval;
- }
-}
diff --git a/media/java/android/drm/mobile1/DrmException.java b/media/java/android/drm/mobile1/DrmException.java
deleted file mode 100644
index 7b06c92..0000000
--- a/media/java/android/drm/mobile1/DrmException.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-
-package android.drm.mobile1;
-
-import java.io.IOException;
-
-/**
- * A DrmException is thrown to report errors specific to handle DRM content and rights.
- */
-public class DrmException extends Exception
-{
- // TODO: add more specific DRM error codes.
-
- private DrmException() {
- }
-
- public DrmException(String message) {
- super(message);
- }
-}
diff --git a/media/java/android/drm/mobile1/DrmRawContent.java b/media/java/android/drm/mobile1/DrmRawContent.java
deleted file mode 100644
index 046b84a..0000000
--- a/media/java/android/drm/mobile1/DrmRawContent.java
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * 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.
- */
-
-package android.drm.mobile1;
-
-import java.io.*;
-
-/**
- * This class provides interfaces to access the DRM raw content.
- */
-public class DrmRawContent {
- /**
- * The "application/vnd.oma.drm.message" mime type.
- */
- public static final String DRM_MIMETYPE_MESSAGE_STRING = "application/vnd.oma.drm.message";
-
- /**
- * The "application/vnd.oma.drm.content" mime type.
- */
- public static final String DRM_MIMETYPE_CONTENT_STRING = "application/vnd.oma.drm.content";
-
- /**
- * The DRM delivery type: Forward-Lock
- */
- public static final int DRM_FORWARD_LOCK = 1;
-
- /**
- * The DRM delivery type: Combined Delivery
- */
- public static final int DRM_COMBINED_DELIVERY = 2;
-
- /**
- * The DRM delivery type: Separate Delivery
- */
- public static final int DRM_SEPARATE_DELIVERY = 3;
-
- /**
- * The DRM delivery type: Separate Delivery in DRM message
- */
- public static final int DRM_SEPARATE_DELIVERY_DM = 4;
-
- /**
- * The DRM media content length is unknown currently
- */
- public static final int DRM_UNKNOWN_DATA_LEN = -1;
-
-
- /**
- * The id of "application/vnd.oma.drm.message" mime type.
- */
- private static final int DRM_MIMETYPE_MESSAGE = 1;
-
- /**
- * The id of "application/vnd.oma.drm.content" mime type.
- */
- private static final int DRM_MIMETYPE_CONTENT = 2;
-
- /**
- * Successful operation.
- */
- private static final int JNI_DRM_SUCCESS = 0;
-
- /**
- * General failure.
- */
- private static final int JNI_DRM_FAILURE = -1;
-
- /**
- * Indicates the end of the DRM content is reached.
- */
- private static final int JNI_DRM_EOF = -2;
-
- /**
- * The media content length is unknown from native method
- */
- private static final int JNI_DRM_UNKNOWN_DATA_LEN = -3;
-
- /**
- * The member to save the original InputStream data.
- */
- private BufferedInputStream inData;
-
- /**
- * The member to save the original InputStream data length.
- */
- private int inDataLen;
-
- /**
- * The unique id to this DRM content. It will be initialized
- * in constructor by native method. And it will not be changed
- * after initialization.
- */
- private int id;
-
- /**
- * The rights issuer address of this DRM object.
- */
- private String rightsIssuer;
-
- /**
- * The media content type of this DRM object.
- */
- private String mediaType;
-
- /**
- * The delivery method type of this DRM object.
- */
- private int rawType;
-
-
- /**
- * Construct a DrmRawContent object.
- *
- * @param inRawdata object of DRM raw data stream.
- * @param len the length of raw data can be read.
- * @param mimeTypeStr the mime type of the DRM content.
- */
- public DrmRawContent(InputStream inRawdata, int len, String mimeTypeStr) throws DrmException, IOException {
- int mimeType;
-
- id = -1;
- inData = new BufferedInputStream(inRawdata, 1024);
- inDataLen = len;
-
- if (DRM_MIMETYPE_MESSAGE_STRING.equals(mimeTypeStr))
- mimeType = DRM_MIMETYPE_MESSAGE;
- else if (DRM_MIMETYPE_CONTENT_STRING.equals(mimeTypeStr))
- mimeType = DRM_MIMETYPE_CONTENT;
- else
- throw new IllegalArgumentException("mimeType must be DRM_MIMETYPE_MESSAGE or DRM_MIMETYPE_CONTENT");
-
- if (len <= 0)
- throw new IllegalArgumentException("len must be > 0");
-
- /* call native method to initialize this DRM content */
- id = nativeConstructDrmContent(inData, inDataLen, mimeType);
-
- if (JNI_DRM_FAILURE == id)
- throw new DrmException("nativeConstructDrmContent() returned JNI_DRM_FAILURE");
-
- /* init the rights issuer field. */
- rightsIssuer = nativeGetRightsAddress();
-
- /* init the raw content type. */
- rawType = nativeGetDeliveryMethod();
- if (JNI_DRM_FAILURE == rawType)
- throw new DrmException("nativeGetDeliveryMethod() returned JNI_DRM_FAILURE");
-
- /* init the media content type. */
- mediaType = nativeGetContentType();
- if (null == mediaType)
- throw new DrmException("nativeGetContentType() returned null");
- }
-
- /**
- * Get rights address from raw Seperate Delivery content.
- *
- * @return the string of the rights issuer address,
- * or null if no rights issuer.
- */
- public String getRightsAddress() {
- return rightsIssuer;
- }
-
- /**
- * Get the type of the raw DRM content.
- *
- * @return one of the following delivery type of this DRM content:
- * #DRM_FORWARD_LOCK
- * #DRM_COMBINED_DELIVERY
- * #DRM_SEPARATE_DELIVERY
- * #DRM_SEPARATE_DELIVERY_DM
- */
- public int getRawType() {
- return rawType;
- }
-
- /**
- * Get one InputStream object to read decrypted content.
- *
- * @param rights the rights object contain decrypted key.
- *
- * @return the InputStream object of decrypted media content.
- */
- public InputStream getContentInputStream(DrmRights rights) {
- if (null == rights)
- throw new NullPointerException();
-
- return new DrmInputStream(rights);
- }
-
- /**
- * Get the type of the decrypted media content.
- *
- * @return the decrypted media content type of this DRM content.
- */
- public String getContentType() {
- return mediaType;
- }
-
- /**
- * Get the length of the decrypted media content.
- *
- * @param rights the rights object contain decrypted key.
- *
- * @return the length of the decrypted media content.
- * #DRM_UNKNOWN_DATA_LEN if the length is unknown currently.
- */
- public int getContentLength(DrmRights rights) throws DrmException {
- /**
- * Because currently the media object associate with rights object
- * has been handled in native logic, so here it is not need to deal
- * the rights. But for the apps, it is mandatory for user to get
- * the rights object before get the media content length.
- */
- if (null == rights)
- throw new NullPointerException();
-
- int mediaLen = nativeGetContentLength();
-
- if (JNI_DRM_FAILURE == mediaLen)
- throw new DrmException("nativeGetContentLength() returned JNI_DRM_FAILURE");
-
- if (JNI_DRM_UNKNOWN_DATA_LEN == mediaLen)
- return DRM_UNKNOWN_DATA_LEN;
-
- return mediaLen;
- }
-
- /**
- * This class provide a InputStream to the DRM media content.
- */
- class DrmInputStream extends InputStream
- {
- /**
- * The flag to indicate whether this stream is closed or not.
- */
- private boolean isClosed;
-
- /**
- * The offset of this DRM content to be reset.
- */
- private int offset;
-
- /**
- * A byte of data to be readed.
- */
- private byte[] b;
-
- /**
- * Construct a DrmInputStream instance.
- */
- public DrmInputStream(DrmRights rights) {
- /**
- * Because currently the media object associate with rights object
- * has been handled in native logic, so here it is not need to deal
- * the rights. But for the apps, it is mandatory for user to get
- * the rights object before get the media content data.
- */
-
- isClosed = false;
- offset = 0;
- b = new byte[1];
- }
-
- /* Non-javadoc
- * @see java.io.InputStream#available()
- */
- public int available() throws IOException {
- /* call native method to get this DRM decrypted media content length */
- int len = nativeGetContentLength();
-
- if (JNI_DRM_FAILURE == len)
- throw new IOException();
-
- /* if the length is unknown, just return 0 for available value */
- if (JNI_DRM_UNKNOWN_DATA_LEN == len)
- return 0;
-
- int availableLen = len - offset;
- if (availableLen < 0)
- throw new IOException();
-
- return availableLen;
- }
-
- /* Non-javadoc
- * @see java.io.InputStream#read()
- */
- public int read() throws IOException {
- int res;
-
- res = read(b, 0, 1);
-
- if (-1 == res)
- return -1;
-
- return b[0] & 0xff;
- }
-
- /* Non-javadoc
- * @see java.io.InputStream#read(byte)
- */
- public int read(byte[] b) throws IOException {
- return read(b, 0, b.length);
- }
-
- /* Non-javadoc
- * @see java.io.InputStream#read(byte, int, int)
- */
- public int read(byte[] b, int off, int len) throws IOException {
- if (null == b)
- throw new NullPointerException();
- if (off < 0 || len < 0 || off + len > b.length)
- throw new IndexOutOfBoundsException();
- if (true == isClosed)
- throw new IOException();
-
- if (0 == len)
- return 0;
-
- len = nativeReadContent(b, off, len, offset);
-
- if (JNI_DRM_FAILURE == len)
- throw new IOException();
- else if (JNI_DRM_EOF == len)
- return -1;
-
- offset += len;
-
- return len;
- }
-
- /* Non-javadoc
- * @see java.io.InputStream#markSupported()
- */
- public boolean markSupported() {
- return false;
- }
-
- /* Non-javadoc
- * @see java.io.InputStream#mark(int)
- */
- public void mark(int readlimit) {
- }
-
- /* Non-javadoc
- * @see java.io.InputStream#reset()
- */
- public void reset() throws IOException {
- throw new IOException();
- }
-
- /* Non-javadoc
- * @see java.io.InputStream#skip()
- */
- public long skip(long n) throws IOException {
- return 0;
- }
-
- /* Non-javadoc
- * @see java.io.InputStream#close()
- */
- public void close() {
- isClosed = true;
- }
- }
-
- /**
- * native method: construct a DRM content according the mime type.
- *
- * @param data input DRM content data to be parsed.
- * @param len the length of the data.
- * @param mimeType the mime type of this DRM content. the value of this field includes:
- * #DRM_MIMETYPE_MESSAGE
- * #DRM_MIMETYPE_CONTENT
- *
- * @return #the id of the DRM content if succeed.
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeConstructDrmContent(InputStream data, int len, int mimeType);
-
- /**
- * native method: get this DRM content rights issuer.
- *
- * @return the address of rights issuer if in case of separate delivery.
- * null if not separete delivery, or otherwise.
- */
- private native String nativeGetRightsAddress();
-
- /**
- * native method: get this DRM content delivery type.
- *
- * @return the delivery method, the value may be one of the following:
- * #DRM_FORWARD_LOCK
- * #DRM_COMBINED_DELIVERY
- * #DRM_SEPARATE_DELIVERY
- * #DRM_SEPARATE_DELIVERY_DM
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeGetDeliveryMethod();
-
- /**
- * native method: get a piece of media content data.
- *
- * @param buf the buffer to save DRM media content data.
- * @param bufOff the offset of the buffer to start to save data.
- * @param len the number of byte to read.
- * @param mediaOff the offset of the media content data to start to read.
- *
- * @return the length of the media content data has been read.
- * #JNI_DRM_EOF if reach to end of the media content.
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeReadContent(byte[] buf, int bufOff, int len, int mediaOff);
-
- /**
- * native method: get this DRM content type.
- *
- * @return the decrypted media content type.
- * null if fail.
- */
- private native String nativeGetContentType();
-
- /**
- * native method: get this DRM decrypted media content length.
- *
- * @return the length of decrypted media content.
- * #JNI_DRM_FAILURE if fail.
- * #JNI_DRM_UNKNOWN_DATA_LEN if the length is unknown currently.
- */
- private native int nativeGetContentLength();
-
- /**
- * The finalizer of the DRMRawContent. Do some cleanup.
- */
- protected native void finalize();
-
-
- /**
- * Load the shared library to link the native methods.
- */
- static {
- try {
- System.loadLibrary("drm1_jni");
- }
- catch (UnsatisfiedLinkError ule) {
- System.err.println("WARNING: Could not load libdrm1_jni.so");
- }
- }
-}
diff --git a/media/java/android/drm/mobile1/DrmRights.java b/media/java/android/drm/mobile1/DrmRights.java
deleted file mode 100644
index bcccb6a..0000000
--- a/media/java/android/drm/mobile1/DrmRights.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.
- */
-
-package android.drm.mobile1;
-
-/**
- * This class provides interfaces to access the DRM rights.
- */
-public class DrmRights {
- /**
- * The DRM permission of play.
- */
- public static final int DRM_PERMISSION_PLAY = 1;
-
- /**
- * The DRM permission of display.
- */
- public static final int DRM_PERMISSION_DISPLAY = 2;
-
- /**
- * The DRM permission of execute.
- */
- public static final int DRM_PERMISSION_EXECUTE = 3;
-
- /**
- * The DRM permission of print.
- */
- public static final int DRM_PERMISSION_PRINT = 4;
-
- /**
- * Successful operation.
- */
- private static final int JNI_DRM_SUCCESS = 0;
-
- /**
- * General failure.
- */
- private static final int JNI_DRM_FAILURE = -1;
-
- /**
- * The uid of this rights object.
- */
- private String roId = "";
-
-
- /**
- * Construct the DrmRights.
- */
- public DrmRights() {
- }
-
- /**
- * Get the constraint of the given permission on this rights object.
- *
- * @param permission the given permission.
- *
- * @return a DrmConstraint instance.
- */
- public DrmConstraintInfo getConstraint(int permission) {
- DrmConstraintInfo c = new DrmConstraintInfo();
-
- /* call native method to get latest constraint information */
- int res = nativeGetConstraintInfo(permission, c);
-
- if (JNI_DRM_FAILURE == res)
- return null;
-
- return c;
- }
-
- /**
- * Consume the rights of the given permission.
- *
- * @param permission the given permission.
- *
- * @return true if consume success.
- * false if consume failure.
- */
- public boolean consumeRights(int permission) {
- /* call native method to consume and update rights */
- int res = nativeConsumeRights(permission);
-
- if (JNI_DRM_FAILURE == res)
- return false;
-
- return true;
- }
-
-
- /**
- * native method: get the constraint information of the given permission.
- *
- * @param permission the given permission.
- * @param constraint the instance of constraint.
- *
- * @return #JNI_DRM_SUCCESS if succeed.
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeGetConstraintInfo(int permission, DrmConstraintInfo constraint);
-
- /**
- * native method: consume the rights of the given permission.
- *
- * @param permission the given permission.
- *
- * @return #JNI_DRM_SUCCESS if succeed.
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeConsumeRights(int permission);
-
-
- /**
- * Load the shared library to link the native methods.
- */
- static {
- try {
- System.loadLibrary("drm1_jni");
- }
- catch (UnsatisfiedLinkError ule) {
- System.err.println("WARNING: Could not load libdrm1_jni.so");
- }
- }
-}
diff --git a/media/java/android/drm/mobile1/DrmRightsManager.java b/media/java/android/drm/mobile1/DrmRightsManager.java
deleted file mode 100644
index 1bc36ec..0000000
--- a/media/java/android/drm/mobile1/DrmRightsManager.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * 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.
- */
-
-package android.drm.mobile1;
-
-import java.io.*;
-import java.util.*;
-
-/**
- * This class provides interfaces to access the DRM right manager.
- */
-public class DrmRightsManager {
- /**
- * The "application/vnd.oma.drm.rights+xml" mime type.
- */
- public static final String DRM_MIMETYPE_RIGHTS_XML_STRING = "application/vnd.oma.drm.rights+xml";
-
- /**
- * The "application/vnd.oma.drm.rights+wbxml" mime type.
- */
- public static final String DRM_MIMETYPE_RIGHTS_WBXML_STRING = "application/vnd.oma.drm.rights+wbxml";
-
- /**
- * The id of "application/vnd.oma.drm.rights+xml" mime type.
- */
- private static final int DRM_MIMETYPE_RIGHTS_XML = 3;
-
- /**
- * The id of "application/vnd.oma.drm.rights+wbxml" mime type.
- */
- private static final int DRM_MIMETYPE_RIGHTS_WBXML = 4;
-
- /**
- * The id of "application/vnd.oma.drm.message" mime type.
- */
- private static final int DRM_MIMETYPE_MESSAGE = 1;
-
- /**
- * Successful operation.
- */
- private static final int JNI_DRM_SUCCESS = 0;
-
- /**
- * General failure.
- */
- private static final int JNI_DRM_FAILURE = -1;
-
- /**
- * The instance of the rights manager.
- */
- private static DrmRightsManager singleton = null;
-
-
- /**
- * Construct a DrmRightsManager
- */
- protected DrmRightsManager() {
- }
-
- /**
- * Get the DrmRightsManager instance.
- *
- * @return the instance of DrmRightsManager.
- */
- public static synchronized DrmRightsManager getInstance() {
- if (singleton == null) {
- singleton = new DrmRightsManager();
- }
-
- return singleton;
- }
-
- /**
- * Install one DRM rights and return one instance of DrmRights.
- *
- * @param rightsData raw rights data.
- * @param mimeTypeStr the mime type of the rights object.
- *
- * @return the instance of the installed DrmRights.
- */
- public synchronized DrmRights installRights(InputStream rightsData, int len, String mimeTypeStr) throws DrmException, IOException {
- int mimeType = 0;
-
- if (DRM_MIMETYPE_RIGHTS_XML_STRING.equals(mimeTypeStr))
- mimeType = DRM_MIMETYPE_RIGHTS_XML;
- else if (DRM_MIMETYPE_RIGHTS_WBXML_STRING.equals(mimeTypeStr))
- mimeType = DRM_MIMETYPE_RIGHTS_WBXML;
- else if (DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equals(mimeTypeStr))
- mimeType = DRM_MIMETYPE_MESSAGE;
- else
- throw new IllegalArgumentException("mimeType must be DRM_MIMETYPE_RIGHTS_XML or DRM_MIMETYPE_RIGHTS_WBXML or DRM_MIMETYPE_MESSAGE");
-
- if (len <= 0)
- return null;
-
- DrmRights rights = new DrmRights();
-
- /* call native method to install this rights object. */
- int res = nativeInstallDrmRights(rightsData, len, mimeType, rights);
-
- if (JNI_DRM_FAILURE == res)
- throw new DrmException("nativeInstallDrmRights() returned JNI_DRM_FAILURE");
-
- return rights;
- }
-
- /**
- * Query DRM rights of specified DRM raw content.
- *
- * @param content raw content object.
- *
- * @return the instance of DrmRights, or null if there is no rights.
- */
- public synchronized DrmRights queryRights(DrmRawContent content) {
- DrmRights rights = new DrmRights();
-
- /* call native method to query the rights */
- int res = nativeQueryRights(content, rights);
-
- if (JNI_DRM_FAILURE == res)
- return null;
-
- return rights;
- }
-
- /**
- * Get the list of all DRM rights saved in local client.
- *
- * @return the list of all the rights object.
- */
- public synchronized List getRightsList() {
- List rightsList = new ArrayList();
-
- /* call native method to get how many rights object in current agent */
- int num = nativeGetNumOfRights();
-
- if (JNI_DRM_FAILURE == num)
- return null;
-
- if (num > 0) {
- DrmRights[] rightsArray = new DrmRights[num];
- int i;
-
- for (i = 0; i < num; i++)
- rightsArray[i] = new DrmRights();
-
- /* call native method to get all the rights information */
- num = nativeGetRightsList(rightsArray, num);
-
- if (JNI_DRM_FAILURE == num)
- return null;
-
- /* add all rights informations to ArrayList */
- for (i = 0; i < num; i++)
- rightsList.add(rightsArray[i]);
- }
-
- return rightsList;
- }
-
- /**
- * Delete the specified DRM rights object.
- *
- * @param rights the specified rights object to be deleted.
- */
- public synchronized void deleteRights(DrmRights rights) {
- /* call native method to delete the specified rights object */
- int res = nativeDeleteRights(rights);
-
- if (JNI_DRM_FAILURE == res)
- return;
- }
-
-
- /**
- * native method: install rights object to local client.
- *
- * @param data input DRM rights object data to be installed.
- * @param len the length of the data.
- * @param mimeType the mime type of this DRM rights object. the value of this field includes:
- * #DRM_MIMETYPE_RIGHTS_XML
- * #DRM_MIMETYPE_RIGHTS_WBXML
- * @parma rights the instance of DRMRights to be filled.
- *
- * @return #JNI_DRM_SUCCESS if succeed.
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeInstallDrmRights(InputStream data, int len, int mimeType, DrmRights rights);
-
- /**
- * native method: query the given DRM content's rights object.
- *
- * @param content the given DRM content.
- * @param rights the instance of rights to set if have.
- *
- * @return #JNI_DRM_SUCCESS if succeed.
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeQueryRights(DrmRawContent content, DrmRights rights);
-
- /**
- * native method: get how many rights object in current DRM agent.
- *
- * @return the number of the rights object.
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeGetNumOfRights();
-
- /**
- * native method: get all the rights object in current local agent.
- *
- * @param rights the array instance of rights object.
- * @param numRights how many rights can be saved.
- *
- * @return the number of the rights object has been gotten.
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeGetRightsList(DrmRights[] rights, int numRights);
-
- /**
- * native method: delete a specified rights object.
- *
- * @param rights the specified rights object to be deleted.
- *
- * @return #JNI_DRM_SUCCESS if succeed.
- * #JNI_DRM_FAILURE if fail.
- */
- private native int nativeDeleteRights(DrmRights rights);
-
-
- /**
- * Load the shared library to link the native methods.
- */
- static {
- try {
- System.loadLibrary("drm1_jni");
- }
- catch (UnsatisfiedLinkError ule) {
- System.err.println("WARNING: Could not load libdrm1_jni.so");
- }
- }
-}
diff --git a/media/java/android/drm/mobile1/package.html b/media/java/android/drm/mobile1/package.html
deleted file mode 100644
index 1c9bf9d..0000000
--- a/media/java/android/drm/mobile1/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body>
- {@hide}
-</body>
-</html>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 93ab401..3144e8a 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -551,9 +551,10 @@
IAudioService service = getService();
try {
if (mUseMasterVolume) {
- service.adjustMasterVolume(direction, flags);
+ service.adjustMasterVolume(direction, flags, mContext.getBasePackageName());
} else {
- service.adjustStreamVolume(streamType, direction, flags);
+ service.adjustStreamVolume(streamType, direction, flags,
+ mContext.getBasePackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustStreamVolume", e);
@@ -581,9 +582,9 @@
IAudioService service = getService();
try {
if (mUseMasterVolume) {
- service.adjustMasterVolume(direction, flags);
+ service.adjustMasterVolume(direction, flags, mContext.getBasePackageName());
} else {
- service.adjustVolume(direction, flags);
+ service.adjustVolume(direction, flags, mContext.getBasePackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustVolume", e);
@@ -611,9 +612,10 @@
IAudioService service = getService();
try {
if (mUseMasterVolume) {
- service.adjustMasterVolume(direction, flags);
+ service.adjustMasterVolume(direction, flags, mContext.getBasePackageName());
} else {
- service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags);
+ service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
+ mContext.getBasePackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e);
@@ -632,7 +634,7 @@
public void adjustMasterVolume(int steps, int flags) {
IAudioService service = getService();
try {
- service.adjustMasterVolume(steps, flags);
+ service.adjustMasterVolume(steps, flags, mContext.getBasePackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustMasterVolume", e);
}
@@ -784,9 +786,9 @@
IAudioService service = getService();
try {
if (mUseMasterVolume) {
- service.setMasterVolume(index, flags);
+ service.setMasterVolume(index, flags, mContext.getBasePackageName());
} else {
- service.setStreamVolume(streamType, index, flags);
+ service.setStreamVolume(streamType, index, flags, mContext.getBasePackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setStreamVolume", e);
@@ -843,16 +845,16 @@
* Sets the volume index for master volume.
*
* @param index The volume index to set. See
- * {@link #getMasterMaxVolume(int)} for the largest valid value.
+ * {@link #getMasterMaxVolume()} for the largest valid value.
* @param flags One or more flags.
- * @see #getMasterMaxVolume(int)
- * @see #getMasterVolume(int)
+ * @see #getMasterMaxVolume()
+ * @see #getMasterVolume()
* @hide
*/
public void setMasterVolume(int index, int flags) {
IAudioService service = getService();
try {
- service.setMasterVolume(index, flags);
+ service.setMasterVolume(index, flags, mContext.getBasePackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setMasterVolume", e);
}
@@ -1563,7 +1565,8 @@
}
IAudioService service = getService();
try {
- service.adjustLocalOrRemoteStreamVolume(streamType, direction);
+ service.adjustLocalOrRemoteStreamVolume(streamType, direction,
+ mContext.getBasePackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustLocalOrRemoteStreamVolume", e);
}
@@ -1582,7 +1585,7 @@
*/
/**
* @hide
- * @deprecated Use {@link #setPrameters(String)} instead
+ * @deprecated Use {@link #setParameters(String)} instead
*/
@Deprecated public void setParameter(String key, String value) {
setParameters(key+"="+value);
@@ -1964,7 +1967,7 @@
try {
status = service.requestAudioFocus(streamType, durationHint, mICallBack,
mAudioFocusDispatcher, getIdForAudioFocusListener(l),
- mContext.getPackageName() /* package name */);
+ mContext.getBasePackageName() /* package name */);
} catch (RemoteException e) {
Log.e(TAG, "Can't call requestAudioFocus() on AudioService due to "+e);
}
@@ -1986,7 +1989,7 @@
try {
service.requestAudioFocus(streamType, durationHint, mICallBack, null,
AudioService.IN_VOICE_COMM_FOCUS_ID,
- "system" /* dump-friendly package name */);
+ mContext.getBasePackageName());
} catch (RemoteException e) {
Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService due to "+e);
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index aa91200..9e13ca4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -23,6 +23,7 @@
import android.app.Activity;
import android.app.ActivityManagerNative;
+import android.app.AppOpsManager;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
@@ -117,9 +118,10 @@
/** How long to delay before persisting a change in volume/ringer mode. */
private static final int PERSIST_DELAY = 500;
- private Context mContext;
- private ContentResolver mContentResolver;
- private boolean mVoiceCapable;
+ private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private final AppOpsManager mAppOps;
+ private final boolean mVoiceCapable;
/** The UI */
private VolumePanel mVolumePanel;
@@ -257,6 +259,23 @@
};
private int[] mStreamVolumeAlias;
+ /**
+ * Map AudioSystem.STREAM_* constants to app ops. This should be used
+ * after mapping through mStreamVolumeAlias.
+ */
+ private static final int[] STEAM_VOLUME_OPS = new int[] {
+ AppOpsManager.OP_AUDIO_VOICE_VOLUME, // STREAM_VOICE_CALL
+ AppOpsManager.OP_AUDIO_MEDIA_VOLUME, // STREAM_SYSTEM
+ AppOpsManager.OP_AUDIO_RING_VOLUME, // STREAM_RING
+ AppOpsManager.OP_AUDIO_MEDIA_VOLUME, // STREAM_MUSIC
+ AppOpsManager.OP_AUDIO_ALARM_VOLUME, // STREAM_ALARM
+ AppOpsManager.OP_AUDIO_NOTIFICATION_VOLUME, // STREAM_NOTIFICATION
+ AppOpsManager.OP_AUDIO_BLUETOOTH_VOLUME, // STREAM_BLUETOOTH_SCO
+ AppOpsManager.OP_AUDIO_MEDIA_VOLUME, // STREAM_SYSTEM_ENFORCED
+ AppOpsManager.OP_AUDIO_MEDIA_VOLUME, // STREAM_DTMF
+ AppOpsManager.OP_AUDIO_MEDIA_VOLUME, // STREAM_TTS
+ };
+
private final boolean mUseFixedVolume;
// stream names used by dumpStreamStates()
@@ -457,6 +476,7 @@
public AudioService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+ mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
mVoiceCapable = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_voice_capable);
@@ -787,23 +807,26 @@
///////////////////////////////////////////////////////////////////////////
/** @see AudioManager#adjustVolume(int, int) */
- public void adjustVolume(int direction, int flags) {
- adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
+ public void adjustVolume(int direction, int flags, String callingPackage) {
+ adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags,
+ callingPackage);
}
/** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
* on streamType: fixed to STREAM_MUSIC */
- public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
+ public void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
+ String callingPackage) {
if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
- adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0);
+ adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0, callingPackage);
}
}
/** @see AudioManager#adjustVolume(int, int) */
- public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
+ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
+ String callingPackage) {
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
int streamType;
if (mVolumeControlStream != -1) {
@@ -826,12 +849,13 @@
//if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
} else {
- adjustStreamVolume(streamType, direction, flags);
+ adjustStreamVolume(streamType, direction, flags, callingPackage);
}
}
/** @see AudioManager#adjustStreamVolume(int, int, int) */
- public void adjustStreamVolume(int streamType, int direction, int flags) {
+ public void adjustStreamVolume(int streamType, int direction, int flags,
+ String callingPackage) {
if (mUseFixedVolume) {
return;
}
@@ -852,6 +876,11 @@
boolean adjustVolume = true;
int step;
+ if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+
// reset any pending volume command
synchronized (mSafeMediaVolumeState) {
mPendingVolumeCommand = null;
@@ -917,7 +946,7 @@
}
/** @see AudioManager#adjustMasterVolume(int, int) */
- public void adjustMasterVolume(int steps, int flags) {
+ public void adjustMasterVolume(int steps, int flags, String callingPackage) {
if (mUseFixedVolume) {
return;
}
@@ -932,7 +961,7 @@
}
//Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
- setMasterVolume(volume, flags);
+ setMasterVolume(volume, flags, callingPackage);
}
// StreamVolumeCommand contains the information needed to defer the process of
@@ -968,27 +997,33 @@
}
/** @see AudioManager#setStreamVolume(int, int, int) */
- public void setStreamVolume(int streamType, int index, int flags) {
+ public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
if (mUseFixedVolume) {
return;
}
ensureValidStreamType(streamType);
- VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]];
+ int streamTypeAlias = mStreamVolumeAlias[streamType];
+ VolumeStreamState streamState = mStreamStates[streamTypeAlias];
final int device = getDeviceForStream(streamType);
int oldIndex;
+ if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+
synchronized (mSafeMediaVolumeState) {
// reset any pending volume command
mPendingVolumeCommand = null;
oldIndex = streamState.getIndex(device);
- index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
+ index = rescaleIndex(index * 10, streamType, streamTypeAlias);
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
- if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
+ if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
((device & mFixedVolumeDevices) != 0)) {
flags |= AudioManager.FLAG_FIXED_VOLUME;
@@ -1003,7 +1038,7 @@
}
}
- if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) {
+ if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {
mVolumePanel.postDisplaySafeVolumeWarning(flags);
mPendingVolumeCommand = new StreamVolumeCommand(
streamType, index, flags, device);
@@ -1261,11 +1296,16 @@
return getLastAudibleMasterVolume();
}
- public void setMasterVolume(int volume, int flags) {
+ public void setMasterVolume(int volume, int flags, String callingPackage) {
if (mUseFixedVolume) {
return;
}
+ if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+
if (volume < 0) {
volume = 0;
} else if (volume > MAX_MASTER_VOLUME) {
@@ -4397,6 +4437,11 @@
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
+ if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
+ callingPackageName) != AppOpsManager.MODE_ALLOWED) {
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+
synchronized(mAudioFocusLock) {
if (!canReassignAudioFocus()) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
@@ -5197,6 +5242,10 @@
if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
return;
}
+ if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(),
+ mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
RemoteControlStackEntry rcse = null;
boolean wasInsideStack = false;
try {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 9768a78..7322716 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -26,7 +26,7 @@
/**
* The AudioTrack class manages and plays a single audio resource for Java applications.
- * It allows streaming PCM audio buffers to the audio hardware for playback. This is
+ * It allows streaming of PCM audio buffers to the audio sink for playback. This is
* achieved by "pushing" the data to the AudioTrack object using one of the
* {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods.
*
@@ -53,8 +53,10 @@
* can play before running out of data.<br>
* For an AudioTrack using the static mode, this size is the maximum size of the sound that can
* be played from it.<br>
- * For the streaming mode, data will be written to the hardware in chunks of
+ * For the streaming mode, data will be written to the audio sink in chunks of
* sizes less than or equal to the total buffer size.
+ *
+ * AudioTrack is not final and thus permits subclasses, but such use is not recommended.
*/
public class AudioTrack
{
@@ -130,7 +132,7 @@
private static final int ERROR_NATIVESETUP_NATIVEINITFAILED = -20;
// Events:
- // to keep in sync with frameworks/base/include/media/AudioTrack.h
+ // to keep in sync with frameworks/av/include/media/AudioTrack.h
/**
* Event id denotes when playback head has reached a previously set marker.
*/
@@ -159,9 +161,10 @@
*/
private final Object mPlayStateLock = new Object();
/**
- * Size of the native audio buffer.
+ * Sizes of the native audio buffer.
*/
private int mNativeBufferSizeInBytes = 0;
+ private int mNativeBufferSizeInFrames = 0;
/**
* Handler for marker events coming from the native code.
*/
@@ -171,7 +174,7 @@
*/
private final Looper mInitializationLooper;
/**
- * The audio data sampling rate in Hz.
+ * The audio data source sampling rate in Hz.
*/
private int mSampleRate; // initialized by all constructors
/**
@@ -192,7 +195,7 @@
*/
private int mStreamType = AudioManager.STREAM_MUSIC;
/**
- * The way audio is consumed by the hardware, streaming or static.
+ * The way audio is consumed by the audio sink, streaming or static.
*/
private int mDataLoadMode = MODE_STREAM;
/**
@@ -236,17 +239,20 @@
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
* {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
- * @param sampleRateInHz the sample rate expressed in Hertz.
+ * @param sampleRateInHz the initial source sample rate expressed in Hz.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
- * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read
- * from for playback. If using the AudioTrack in streaming mode, you can write data into
- * this buffer in smaller chunks than this size. If using the AudioTrack in static mode,
- * this is the maximum size of the sound that will be played for this instance.
+ * @param bufferSizeInBytes the total size (in bytes) of the internal buffer where audio data is
+ * read from for playback.
+ * If track's creation mode is {@link #MODE_STREAM}, you can write data into
+ * this buffer in chunks less than or equal to this size, and it is typical to use
+ * chunks of 1/2 of the total size to permit double-buffering.
+ * If the track's creation mode is {@link #MODE_STATIC},
+ * this is the maximum length sample, or audio clip, that can be played by this instance.
* See {@link #getMinBufferSize(int, int, int)} to determine the minimum required buffer size
* for the successful creation of an AudioTrack instance in streaming mode. Using values
* smaller than getMinBufferSize() will result in an initialization failure.
@@ -257,7 +263,7 @@
int bufferSizeInBytes, int mode)
throws IllegalArgumentException {
this(streamType, sampleRateInHz, channelConfig, audioFormat,
- bufferSizeInBytes, mode, 0);
+ bufferSizeInBytes, mode, 0 /*session*/);
}
/**
@@ -267,7 +273,7 @@
* is provided when creating an AudioEffect, this effect will be applied only to audio tracks
* and media players in the same session and not to the output mix.
* When an AudioTrack is created without specifying a session, it will create its own session
- * which can be retreived by calling the {@link #getAudioSessionId()} method.
+ * which can be retrieved by calling the {@link #getAudioSessionId()} method.
* If a non-zero session ID is provided, this AudioTrack will share effects attached to this
* session
* with all other media players or audio tracks in the same session, otherwise a new session
@@ -276,7 +282,7 @@
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
* {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
- * @param sampleRateInHz the sample rate expressed in Hertz.
+ * @param sampleRateInHz the initial source sample rate expressed in Hz.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -464,7 +470,7 @@
}
- // Convenience method for the contructor's audio buffer size check.
+ // Convenience method for the constructor's audio buffer size check.
// preconditions:
// mChannelCount is valid
// mAudioFormat is valid
@@ -480,6 +486,7 @@
}
mNativeBufferSizeInBytes = audioBufferSize;
+ mNativeBufferSizeInFrames = audioBufferSize / frameSizeInBytes;
}
@@ -559,7 +566,6 @@
/**
* Returns the configured channel configuration.
-
* See {@link AudioFormat#CHANNEL_OUT_MONO}
* and {@link AudioFormat#CHANNEL_OUT_STEREO}.
*/
@@ -577,8 +583,7 @@
/**
* Returns the state of the AudioTrack instance. This is useful after the
* AudioTrack instance has been created to check if it was initialized
- * properly. This ensures that the appropriate hardware resources have been
- * acquired.
+ * properly. This ensures that the appropriate resources have been acquired.
* @see #STATE_INITIALIZED
* @see #STATE_NO_STATIC_DATA
* @see #STATE_UNINITIALIZED
@@ -600,14 +605,26 @@
}
/**
- * Returns the native frame count used by the hardware.
+ * Returns the "native frame count", derived from the bufferSizeInBytes specified at
+ * creation time and converted to frame units.
+ * If track's creation mode is {@link #MODE_STATIC},
+ * it is equal to the specified bufferSizeInBytes converted to frame units.
+ * If track's creation mode is {@link #MODE_STREAM},
+ * it is typically greater than or equal to the specified bufferSizeInBytes converted to frame
+ * units; it may be rounded up to a larger value if needed by the target device implementation.
+ * @deprecated Only accessible by subclasses, which are not recommended for AudioTrack.
+ * See {@link AudioManager#getProperty(String)} for key
+ * {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
*/
+ @Deprecated
protected int getNativeFrameCount() {
return native_get_native_frame_count();
}
/**
* Returns marker position expressed in frames.
+ * @return marker position in wrapping frame units similar to {@link #getPlaybackHeadPosition},
+ * or zero if marker is disabled.
*/
public int getNotificationMarkerPosition() {
return native_get_marker_pos();
@@ -615,13 +632,19 @@
/**
* Returns the notification update period expressed in frames.
+ * Zero means that no position update notifications are being delivered.
*/
public int getPositionNotificationPeriod() {
return native_get_pos_update_period();
}
/**
- * Returns the playback head position expressed in frames
+ * Returns the playback head position expressed in frames.
+ * Though the "int" type is signed 32-bits, the value should be reinterpreted as if it is
+ * unsigned 32-bits. That is, the next position after 0x7FFFFFFF is (int) 0x80000000.
+ * This is a continuously advancing counter. It will wrap (overflow) periodically,
+ * for example approximately once every 27:03:11 hours:minutes:seconds at 44.1 kHz.
+ * It is reset to zero by flush(), reload(), and stop().
*/
public int getPlaybackHeadPosition() {
return native_get_position();
@@ -640,7 +663,7 @@
}
/**
- * Returns the hardware output sample rate
+ * Returns the output sample rate in Hz for the specified stream type.
*/
static public int getNativeOutputSampleRate(int streamType) {
return native_get_output_sample_rate(streamType);
@@ -651,7 +674,10 @@
* object to be created in the {@link #MODE_STREAM} mode. Note that this size doesn't
* guarantee a smooth playback under load, and higher values should be chosen according to
* the expected frequency at which the buffer will be refilled with additional data to play.
- * @param sampleRateInHz the sample rate expressed in Hertz.
+ * For example, if you intend to dynamically set the source sample rate of an AudioTrack
+ * to a higher value than the initial source sample rate, be sure to configure the buffer size
+ * based on the highest planned sample rate.
+ * @param sampleRateInHz the source sample rate expressed in Hz.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -659,8 +685,7 @@
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
* @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed,
- * or {@link #ERROR} if the implementation was unable to query the hardware for its output
- * properties,
+ * or {@link #ERROR} if unable to query for output properties,
* or the minimum buffer size expressed in bytes.
*/
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
@@ -793,10 +818,13 @@
/**
* Sets the playback sample rate for this track. This sets the sampling rate at which
- * the audio data will be consumed and played back, not the original sampling rate of the
- * content. Setting it to half the sample rate of the content will cause the playback to
- * last twice as long, but will also result in a negative pitch shift.
- * The valid sample rate range is from 1Hz to twice the value returned by
+ * the audio data will be consumed and played back
+ * (as set by the sampleRateInHz parameter in the
+ * {@link #AudioTrack(int, int, int, int, int, int)} constructor),
+ * not the original sampling rate of the
+ * content. For example, setting it to half the sample rate of the content will cause the
+ * playback to last twice as long, but will also result in a pitch shift down by one octave.
+ * The valid sample rate range is from 1 Hz to twice the value returned by
* {@link #getNativeOutputSampleRate(int)}.
* @param sampleRateInHz the sample rate expressed in Hz
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
@@ -814,8 +842,11 @@
/**
- * Sets the position of the notification marker.
- * @param markerInFrames marker in frames
+ * Sets the position of the notification marker. At most one marker can be active.
+ * @param markerInFrames marker position in wrapping frame units similar to
+ * {@link #getPlaybackHeadPosition}, or zero to disable the marker.
+ * To set a marker at a position which would appear as zero due to wraparound,
+ * a workaround is to use a non-zero position near zero, such as -1 or 1.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
@@ -845,6 +876,8 @@
* The track must be stopped or paused for the position to be changed,
* and must use the {@link #MODE_STATIC} mode.
* @param positionInFrames playback head position expressed in frames
+ * Zero corresponds to start of buffer.
+ * The position must not be greater than the buffer size in frames, or negative.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
@@ -853,18 +886,28 @@
getPlayState() == PLAYSTATE_PLAYING) {
return ERROR_INVALID_OPERATION;
}
+ if (!(0 <= positionInFrames && positionInFrames <= mNativeBufferSizeInFrames)) {
+ return ERROR_BAD_VALUE;
+ }
return native_set_position(positionInFrames);
}
/**
* Sets the loop points and the loop count. The loop can be infinite.
* Similarly to setPlaybackHeadPosition,
- * the track must be stopped or paused for the position to be changed,
+ * the track must be stopped or paused for the loop points to be changed,
* and must use the {@link #MODE_STATIC} mode.
* @param startInFrames loop start marker expressed in frames
+ * Zero corresponds to start of buffer.
+ * The start marker must not be greater than or equal to the buffer size in frames, or negative.
* @param endInFrames loop end marker expressed in frames
+ * The total buffer size in frames corresponds to end of buffer.
+ * The end marker must not be greater than the buffer size in frames.
+ * For looping, the end marker must not be less than or equal to the start marker,
+ * but to disable looping
+ * it is permitted for start marker, end marker, and loop count to all be 0.
* @param loopCount the number of times the loop is looped.
- * A value of -1 means infinite looping.
+ * A value of -1 means infinite looping, and 0 disables looping.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
@@ -873,14 +916,23 @@
getPlayState() == PLAYSTATE_PLAYING) {
return ERROR_INVALID_OPERATION;
}
+ if (loopCount == 0) {
+ ; // explicitly allowed as an exception to the loop region range check
+ } else if (!(0 <= startInFrames && startInFrames < mNativeBufferSizeInFrames &&
+ startInFrames < endInFrames && endInFrames <= mNativeBufferSizeInFrames)) {
+ return ERROR_BAD_VALUE;
+ }
return native_set_loop(startInFrames, endInFrames, loopCount);
}
/**
- * Sets the initialization state of the instance. To be used in an AudioTrack subclass
- * constructor to set a subclass-specific post-initialization state.
+ * Sets the initialization state of the instance. This method was originally intended to be used
+ * in an AudioTrack subclass constructor to set a subclass-specific post-initialization state.
+ * However, subclasses of AudioTrack are no longer recommended, so this method is obsolete.
* @param state the state of the AudioTrack instance
+ * @deprecated Only accessible by subclasses, which are not recommended for AudioTrack.
*/
+ @Deprecated
protected void setState(int state) {
mState = state;
}
@@ -891,6 +943,7 @@
//--------------------
/**
* Starts playing an AudioTrack.
+ * If track's creation mode is {@link #MODE_STATIC}, you must have called write() prior.
*
* @throws IllegalStateException
*/
@@ -955,7 +1008,8 @@
/**
* Flushes the audio data currently queued for playback. Any data that has
- * not been played back will be discarded.
+ * not been played back will be discarded. No-op if not stopped or paused,
+ * or if the track's creation mode is not {@link #MODE_STREAM}.
*/
public void flush() {
if (mState == STATE_INITIALIZED) {
@@ -966,11 +1020,13 @@
}
/**
- * Writes the audio data to the audio hardware for playback. Will block until
- * all data has been written to the audio mixer.
+ * Writes the audio data to the audio sink for playback (streaming mode),
+ * or copies audio data for later playback (static buffer mode).
+ * In streaming mode, will block until all data has been written to the audio sink.
+ * In static buffer mode, copies the data to the buffer starting at offset 0.
* Note that the actual playback of this data might occur after this function
* returns. This function is thread safe with respect to {@link #stop} calls,
- * in which case all of the specified data might not be written to the mixer.
+ * in which case all of the specified data might not be written to the audio sink.
*
* @param audioData the array that holds the data to play.
* @param offsetInBytes the offset expressed in bytes in audioData where the data to play
@@ -1007,16 +1063,18 @@
/**
- * Writes the audio data to the audio hardware for playback. Will block until
- * all data has been written to the audio mixer.
+ * Writes the audio data to the audio sink for playback (streaming mode),
+ * or copies audio data for later playback (static buffer mode).
+ * In streaming mode, will block until all data has been written to the audio sink.
+ * In static buffer mode, copies the data to the buffer starting at offset 0.
* Note that the actual playback of this data might occur after this function
* returns. This function is thread safe with respect to {@link #stop} calls,
- * in which case all of the specified data might not be written to the mixer.
+ * in which case all of the specified data might not be written to the audio sink.
*
* @param audioData the array that holds the data to play.
* @param offsetInShorts the offset expressed in shorts in audioData where the data to play
* starts.
- * @param sizeInShorts the number of bytes to read in audioData after the offset.
+ * @param sizeInShorts the number of shorts to read in audioData after the offset.
* @return the number of shorts that were written or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
@@ -1049,8 +1107,8 @@
/**
* Notifies the native resource to reuse the audio data already loaded in the native
- * layer. This call is only valid with AudioTrack instances that don't use the streaming
- * model.
+ * layer, that is to rewind to start of buffer.
+ * The track's creation mode must be {@link #MODE_STATIC}.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
@@ -1091,8 +1149,9 @@
/**
* Sets the send level of the audio track to the attached auxiliary effect
- * {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
- * <p>By default the send level is 0, so even if an effect is attached to the player
+ * {@link #attachAuxEffect(int)}. The level value range is 0.0f to 1.0f.
+ * Values are clamped to the (0.0f, 1.0f) interval if outside this range.
+ * <p>By default the send level is 0.0f, so even if an effect is attached to the player
* this method must be called for the effect to be applied.
* <p>Note that the passed level value is a raw scalar. UI controls should be scaled
* logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index fda8c1b..e60ecc4 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -34,21 +34,23 @@
*/
interface IAudioService {
- void adjustVolume(int direction, int flags);
+ void adjustVolume(int direction, int flags, String callingPackage);
- oneway void adjustLocalOrRemoteStreamVolume(int streamType, int direction);
+ oneway void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
+ String callingPackage);
- void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
+ void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
+ String callingPackage);
- void adjustStreamVolume(int streamType, int direction, int flags);
+ void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage);
- void adjustMasterVolume(int direction, int flags);
+ void adjustMasterVolume(int direction, int flags, String callingPackage);
- void setStreamVolume(int streamType, int index, int flags);
+ void setStreamVolume(int streamType, int index, int flags, String callingPackage);
oneway void setRemoteStreamVolume(int index);
- void setMasterVolume(int index, int flags);
+ void setMasterVolume(int index, int flags, String callingPackage);
void setStreamSolo(int streamType, boolean state, IBinder cb);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
new file mode 100644
index 0000000..f55756c
--- /dev/null
+++ b/media/java/android/media/Image.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.graphics.ImageFormat;
+import java.nio.ByteBuffer;
+import java.lang.AutoCloseable;
+
+/**
+ * <p>A single complete image buffer to use with a media source such as a
+ * {@link MediaCodec} or a
+ * {@link android.hardware.photography.CameraDevice}.</p>
+ *
+ * <p>This class allows for efficient direct application access to the pixel
+ * data of the Image through one or more
+ * {@link java.nio.ByteBuffer ByteBuffers}. Each buffer is encapsulated in a
+ * {@link Plane} that describes the layout of the pixel data in that plane. Due
+ * to this direct access, and unlike the {@link android.graphics.Bitmap} class,
+ * Images are not directly usable as as UI resources.</p>
+ *
+ * <p>Since Images are often directly produced or consumed by hardware
+ * components, they are a limited resource shared across the system, and should
+ * be closed as soon as they are no longer needed.</p>
+ *
+ * <p>For example, when using the {@link ImageReader} class to read out Images
+ * from various media sources, not closing old Image objects will prevent the
+ * availability of new Images once
+ * {@link ImageReader#getMaxImages the maximum outstanding image count} is
+ * reached.</p>
+ *
+ * @see ImageReader
+ */
+public interface Image extends AutoCloseable {
+ /**
+ * Get the format for this image. This format determines the number of
+ * ByteBuffers needed to represent the image, and the general layout of the
+ * pixel data in each in ByteBuffer.
+ *
+ * The format is one of the values from
+ * {@link android.graphics.ImageFormat}. The mapping between the formats and
+ * the planes is as follows:
+ *
+ * <table>
+ * <th>
+ * <td>Format</td>
+ * <td>Plane count</td>
+ * <td>Layout details</td>
+ * </th>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#JPEG}</td>
+ * <td>1</td>
+ * <td>Compressed data, so row and pixel strides are 0. To uncompress, use
+ * {@link android.graphics.BitmapFactory#decodeByteArray}.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#YUV_420_888}</td>
+ * <td>3</td>
+ * <td>A luminance plane followed by the Cb and Cr chroma planes.
+ * The chroma planes have half the width and height of the luminance
+ * plane (4:2:0 subsampling). Each pixel sample in each plane has 8 bits.
+ * Each plane has its own row stride and pixel stride.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#RAW_SENSOR}</td>
+ * <td>1</td>
+ * <td>A single plane of raw sensor image data, with 16 bits per color
+ * sample. The details of the layout need to be queried from the source of
+ * the raw sensor data, such as
+ * {@link android.hardware.photography.CameraDevice}.
+ * </td>
+ * </tr>
+ * </table>
+ *
+ * @see android.graphics.ImageFormat
+ */
+ public int getFormat();
+
+ /**
+ * The width of the image in pixels. For formats where some color channels
+ * are subsampled, this is the width of the largest-resolution plane.
+ */
+ public int getWidth();
+
+ /**
+ * The height of the image in pixels. For formats where some color channels
+ * are subsampled, this is the height of the largest-resolution plane.
+ */
+ public int getHeight();
+
+ /**
+ * Get the timestamp associated with this frame. The timestamp is measured
+ * in nanoseconds, and is monotonically increasing. However, the zero point
+ * and whether the timestamp can be compared against other sources of time
+ * or images depend on the source of this image.
+ */
+ public long getTimestamp();
+
+ /**
+ * Get the array of pixel planes for this Image. The number of planes is
+ * determined by the format of the Image.
+ */
+ public Plane[] getPlanes();
+
+ /**
+ * Free up this frame for reuse. After calling this method, calling any
+ * methods on this Image will result in an IllegalStateException, and
+ * attempting to read from ByteBuffers returned by an earlier
+ * {@code Plane#getBuffer} call will have undefined behavior.
+ */
+ public void close();
+
+ /**
+ * <p>A single color plane of image data.</p>
+ *
+ * <p>The number and meaning of the planes in an Image are determined by the
+ * format of the Image.</p>
+ *
+ * <p>Once the Image has been closed, any access to the the plane's
+ * ByteBuffer will fail.</p>
+ *
+ * @see #getFormat
+ */
+ public interface Plane {
+ /**
+ * <p>The row stride for this color plane, in bytes.
+ *
+ * <p>This is the distance between the start of two consecutive rows of
+ * pixels in the image.</p>
+ */
+ public int getRowStride();
+ /**
+ * <p>The distance between adjacent pixel samples, in bytes.</p>
+ *
+ * <p>This is the distance between two consecutive pixel values in a row
+ * of pixels. It may be larger than the size of a single pixel to
+ * account for interleaved image data or padded formats.</p>
+ */
+ public int getPixelStride();
+ /**
+ * <p>Get a set of direct {@link java.nio.ByteBuffer byte buffers}
+ * containing the frame data.</p>
+ *
+ * @return the byte buffer containing the image data for this plane.
+ */
+ public ByteBuffer getBuffer();
+ }
+
+}
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
new file mode 100644
index 0000000..8f09b54
--- /dev/null
+++ b/media/java/android/media/ImageReader.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.graphics.ImageFormat;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.Surface;
+
+import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * <p>The ImageReader class allows direct application access to image data
+ * rendered into a {@link android.view.Surface}</p>
+ *
+ * <p>Several Android media API classes accept Surface objects as targets to
+ * render to, including {@link MediaPlayer}, {@link MediaCodec},
+ * {@link android.hardware.photography.CameraDevice}, and
+ * {@link android.renderscript.Allocation RenderScript Allocations}. The image
+ * sizes and formats that can be used with each source vary, and should be
+ * checked in the documentation for the specific API.</p>
+ *
+ * <p>The image data is encapsulated in {@link Image} objects, and multiple such
+ * objects can be accessed at the same time, up to the number specified by the
+ * {@code maxImages} constructor parameter. New images sent to an ImageReader
+ * through its Surface are queued until accessed through the
+ * {@link #getNextImage} call. Due to memory limits, an image source will
+ * eventually stall or drop Images in trying to render to the Surface if the
+ * ImageReader does not obtain and release Images at a rate equal to the
+ * production rate.</p>
+ */
+public final class ImageReader implements AutoCloseable {
+
+ /**
+ * <p>Create a new reader for images of the desired size and format.</p>
+ *
+ * <p>The maxImages parameter determines the maximum number of {@link Image}
+ * objects that can be be acquired from the ImageReader
+ * simultaneously. Requesting more buffers will use up more memory, so it is
+ * important to use only the minimum number necessary for the use case.</p>
+ *
+ * <p>The valid sizes and formats depend on the source of the image
+ * data.</p>
+ *
+ * @param width the width in pixels of the Images that this reader will
+ * produce.
+ * @param height the height in pixels of the Images that this reader will
+ * produce.
+ * @param format the format of the Image that this reader will produce. This
+ * must be one of the {@link android.graphics.ImageFormat} constants.
+ * @param maxImages the maximum number of images the user will want to
+ * access simultaneously. This should be as small as possible to limit
+ * memory use. Once maxImages Images are obtained by the user, one of them
+ * has to be released before a new Image will become available for access
+ * through getNextImage(). Must be greater than 0.
+ *
+ * @see Image
+ */
+ public ImageReader(int width, int height, int format, int maxImages) {
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mMaxImages = maxImages;
+
+ if (width < 1 || height < 1) {
+ throw new IllegalArgumentException(
+ "The image dimensions must be positive");
+ }
+ if (mMaxImages < 1) {
+ throw new IllegalArgumentException(
+ "Maximum outstanding image count must be at least 1");
+ }
+
+ mNumPlanes = getNumPlanesFromFormat();
+
+ nativeInit(new WeakReference<ImageReader>(this), width, height, format, maxImages);
+
+ mSurface = nativeGetSurface();
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public int getImageFormat() {
+ return mFormat;
+ }
+
+ public int getMaxImages() {
+ return mMaxImages;
+ }
+
+ /**
+ * <p>Get a Surface that can be used to produce Images for this
+ * ImageReader.</p>
+ *
+ * <p>Until valid image data is rendered into this Surface, the
+ * {@link #getNextImage} method will return {@code null}. Only one source
+ * can be producing data into this Surface at the same time, although the
+ * same Surface can be reused with a different API once the first source is
+ * disconnected from the Surface.</p>
+ *
+ * @return A Surface to use for a drawing target for various APIs.
+ */
+ public Surface getSurface() {
+ return mSurface;
+ }
+
+ /**
+ * <p>Get the next Image from the ImageReader's queue. Returns {@code null}
+ * if no new image is available.</p>
+ *
+ * @return a new frame of image data, or {@code null} if no image data is
+ * available.
+ */
+ public Image getNextImage() {
+ SurfaceImage si = new SurfaceImage();
+ if (nativeImageSetup(si)) {
+ // create SurfacePlane objects
+ si.createSurfacePlanes();
+ si.setImageValid(true);
+ return si;
+ }
+ return null;
+ }
+
+ /**
+ * <p>Return the frame to the ImageReader for reuse.</p>
+ */
+ public void releaseImage(Image i) {
+ if (! (i instanceof SurfaceImage) ) {
+ throw new IllegalArgumentException(
+ "This image was not produced by an ImageReader");
+ }
+ SurfaceImage si = (SurfaceImage) i;
+ if (si.getReader() != this) {
+ throw new IllegalArgumentException(
+ "This image was not produced by this ImageReader");
+ }
+
+ si.clearSurfacePlanes();
+ nativeReleaseImage(i);
+ si.setImageValid(false);
+ }
+
+ /**
+ * Register a listener to be invoked when a new image becomes available
+ * from the ImageReader.
+ * @param listener the listener that will be run
+ * @param handler The handler on which the listener should be invoked, or null
+ * if the listener should be invoked on the calling thread's looper.
+ */
+ public void setImageAvailableListener(OnImageAvailableListener listener, Handler handler) {
+ mImageListener = listener;
+
+ Looper looper;
+ mHandler = handler;
+ if (mHandler == null) {
+ if ((looper = Looper.myLooper()) != null) {
+ mHandler = new Handler();
+ } else {
+ throw new IllegalArgumentException(
+ "Looper doesn't exist in the calling thread");
+ }
+ }
+ }
+
+ /**
+ * Callback interface for being notified that a new image is available.
+ * The onImageAvailable is called per image basis, that is, callback fires for every new frame
+ * available from ImageReader.
+ */
+ public interface OnImageAvailableListener {
+ /**
+ * Callback that is called when a new image is available from ImageReader.
+ * @param reader the ImageReader the callback is associated with.
+ * @see ImageReader
+ * @see Image
+ */
+ void onImageAvailable(ImageReader reader);
+ }
+
+ /**
+ * Free up all the resources associated with this ImageReader. After
+ * Calling this method, this ImageReader can not be used. calling
+ * any methods on this ImageReader and Images previously provided by {@link #getNextImage}
+ * will result in an IllegalStateException, and attempting to read from
+ * ByteBuffers returned by an earlier {@code Plane#getBuffer} call will
+ * have undefined behavior.
+ */
+ @Override
+ public void close() {
+ nativeClose();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private int getNumPlanesFromFormat() {
+ switch (mFormat) {
+ case ImageFormat.YV12:
+ case ImageFormat.YUV_420_888:
+ case ImageFormat.NV21:
+ return 3;
+ case ImageFormat.NV16:
+ return 2;
+ case ImageFormat.RGB_565:
+ case ImageFormat.JPEG:
+ case ImageFormat.YUY2:
+ case ImageFormat.Y8:
+ case ImageFormat.Y16:
+ case ImageFormat.RAW_SENSOR:
+ return 1;
+ default:
+ throw new UnsupportedOperationException(
+ String.format("Invalid format specified %d", mFormat));
+ }
+ }
+
+ /**
+ * Called from Native code when an Event happens.
+ */
+ private static void postEventFromNative(Object selfRef) {
+ WeakReference weakSelf = (WeakReference)selfRef;
+ final ImageReader ir = (ImageReader)weakSelf.get();
+ if (ir == null) {
+ return;
+ }
+
+ if (ir.mHandler != null) {
+ ir.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ ir.mImageListener.onImageAvailable(ir);
+ }
+ });
+ }
+ }
+
+ private final int mWidth;
+ private final int mHeight;
+ private final int mFormat;
+ private final int mMaxImages;
+ private final int mNumPlanes;
+ private final Surface mSurface;
+
+ private Handler mHandler;
+ private OnImageAvailableListener mImageListener;
+
+ /**
+ * This field is used by native code, do not access or modify.
+ */
+ private long mNativeContext;
+
+ private class SurfaceImage implements android.media.Image {
+ public SurfaceImage() {
+ mIsImageValid = false;
+ }
+
+ @Override
+ public void close() {
+ if (mIsImageValid) {
+ ImageReader.this.releaseImage(this);
+ }
+ }
+
+ public ImageReader getReader() {
+ return ImageReader.this;
+ }
+
+ @Override
+ public int getFormat() {
+ if (mIsImageValid) {
+ return ImageReader.this.mFormat;
+ } else {
+ throw new IllegalStateException("Image is already released");
+ }
+ }
+
+ @Override
+ public int getWidth() {
+ if (mIsImageValid) {
+ return ImageReader.this.mWidth;
+ } else {
+ throw new IllegalStateException("Image is already released");
+ }
+ }
+
+ @Override
+ public int getHeight() {
+ if (mIsImageValid) {
+ return ImageReader.this.mHeight;
+ } else {
+ throw new IllegalStateException("Image is already released");
+ }
+ }
+
+ @Override
+ public long getTimestamp() {
+ if (mIsImageValid) {
+ return mTimestamp;
+ } else {
+ throw new IllegalStateException("Image is already released");
+ }
+ }
+
+ @Override
+ public Plane[] getPlanes() {
+ if (mIsImageValid) {
+ // Shallow copy is fine.
+ return mPlanes.clone();
+ } else {
+ throw new IllegalStateException("Image is already released");
+ }
+ }
+
+ @Override
+ protected final void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void setImageValid(boolean isValid) {
+ mIsImageValid = isValid;
+ }
+
+ private boolean isImageValid() {
+ return mIsImageValid;
+ }
+
+ private void clearSurfacePlanes() {
+ if (mIsImageValid) {
+ for (int i = 0; i < mPlanes.length; i++) {
+ if (mPlanes[i] != null) {
+ mPlanes[i].clearBuffer();
+ mPlanes[i] = null;
+ }
+ }
+ }
+ }
+
+ private void createSurfacePlanes() {
+ mPlanes = new SurfacePlane[ImageReader.this.mNumPlanes];
+ for (int i = 0; i < ImageReader.this.mNumPlanes; i++) {
+ mPlanes[i] = nativeCreatePlane(i);
+ }
+ }
+ private class SurfacePlane implements android.media.Image.Plane {
+ // SurfacePlane instance is created by native code when a new SurfaceImage is created
+ private SurfacePlane(int index, int rowStride, int pixelStride) {
+ mIndex = index;
+ mRowStride = rowStride;
+ mPixelStride = pixelStride;
+ }
+
+ @Override
+ public ByteBuffer getBuffer() {
+ if (SurfaceImage.this.isImageValid() == false) {
+ throw new IllegalStateException("Image is already released");
+ }
+ if (mBuffer != null) {
+ return mBuffer;
+ } else {
+ mBuffer = SurfaceImage.this.nativeImageGetBuffer(mIndex);
+ // Set the byteBuffer order according to host endianness (native order),
+ // otherwise, the byteBuffer order defaults to ByteOrder.BIG_ENDIAN.
+ return mBuffer.order(ByteOrder.nativeOrder());
+ }
+ }
+
+ @Override
+ public int getPixelStride() {
+ if (SurfaceImage.this.isImageValid()) {
+ return mPixelStride;
+ } else {
+ throw new IllegalStateException("Image is already released");
+ }
+ }
+
+ @Override
+ public int getRowStride() {
+ if (SurfaceImage.this.isImageValid()) {
+ return mRowStride;
+ } else {
+ throw new IllegalStateException("Image is already released");
+ }
+ }
+
+ private void clearBuffer() {
+ mBuffer = null;
+ }
+
+ final private int mIndex;
+ final private int mPixelStride;
+ final private int mRowStride;
+
+ private ByteBuffer mBuffer;
+ }
+
+ /**
+ * This field is used to keep track of native object and used by native code only.
+ * Don't modify.
+ */
+ private long mLockedBuffer;
+
+ /**
+ * This field is set by native code during nativeImageSetup().
+ */
+ private long mTimestamp;
+
+ private SurfacePlane[] mPlanes;
+ private boolean mIsImageValid;
+
+ private synchronized native ByteBuffer nativeImageGetBuffer(int idx);
+ private synchronized native SurfacePlane nativeCreatePlane(int idx);
+ }
+
+ private synchronized native void nativeInit(Object weakSelf, int w, int h,
+ int fmt, int maxImgs);
+ private synchronized native void nativeClose();
+ private synchronized native void nativeReleaseImage(Image i);
+ private synchronized native Surface nativeGetSurface();
+ private synchronized native boolean nativeImageSetup(Image i);
+
+ /*
+ * We use a class initializer to allow the native code to cache some
+ * field offsets.
+ */
+ private static native void nativeClassInit();
+ static {
+ System.loadLibrary("media_jni");
+ nativeClassInit();
+ }
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d703642c..8cf0b4b 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -164,7 +164,8 @@
*
* The following is a partial list of defined mime types and their semantics:
* <ul>
- * <li>"video/x-vnd.on2.vp8" - VPX video (i.e. video in .webm)
+ * <li>"video/x-vnd.on2.vp8" - VP8 video (i.e. video in .webm)
+ * <li>"video/x-vnd.on2.vp9" - VP9 video (i.e. video in .webm)
* <li>"video/avc" - H.264/AVC video
* <li>"video/mp4v-es" - MPEG4 video
* <li>"video/3gpp" - H.263 video
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index b729640..241c7fa 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -639,7 +639,6 @@
*
* @param reply Output parcel with the data returned by the
* native player.
- *
* {@hide}
*/
public void invoke(Parcel request, Parcel reply) {
@@ -1410,13 +1409,6 @@
}
/**
- * Currently not implemented, returns null.
- * @deprecated
- * @hide
- */
- public native Bitmap getFrameAt(int msec) throws IllegalStateException;
-
- /**
* Sets the audio session ID.
*
* @param sessionId the audio session ID.
@@ -1458,100 +1450,6 @@
*/
public native void attachAuxEffect(int effectId);
- /* Do not change these values (starting with KEY_PARAMETER) without updating
- * their counterparts in include/media/mediaplayer.h!
- */
-
- // There are currently no defined keys usable from Java with get*Parameter.
- // But if any keys are defined, the order must be kept in sync with include/media/mediaplayer.h.
- // private static final int KEY_PARAMETER_... = ...;
-
- /**
- * Sets the parameter indicated by key.
- * @param key key indicates the parameter to be set.
- * @param value value of the parameter to be set.
- * @return true if the parameter is set successfully, false otherwise
- * {@hide}
- */
- public native boolean setParameter(int key, Parcel value);
-
- /**
- * Sets the parameter indicated by key.
- * @param key key indicates the parameter to be set.
- * @param value value of the parameter to be set.
- * @return true if the parameter is set successfully, false otherwise
- * {@hide}
- */
- public boolean setParameter(int key, String value) {
- Parcel p = Parcel.obtain();
- p.writeString(value);
- boolean ret = setParameter(key, p);
- p.recycle();
- return ret;
- }
-
- /**
- * Sets the parameter indicated by key.
- * @param key key indicates the parameter to be set.
- * @param value value of the parameter to be set.
- * @return true if the parameter is set successfully, false otherwise
- * {@hide}
- */
- public boolean setParameter(int key, int value) {
- Parcel p = Parcel.obtain();
- p.writeInt(value);
- boolean ret = setParameter(key, p);
- p.recycle();
- return ret;
- }
-
- /*
- * Gets the value of the parameter indicated by key.
- * @param key key indicates the parameter to get.
- * @param reply value of the parameter to get.
- */
- private native void getParameter(int key, Parcel reply);
-
- /**
- * Gets the value of the parameter indicated by key.
- * The caller is responsible for recycling the returned parcel.
- * @param key key indicates the parameter to get.
- * @return value of the parameter.
- * {@hide}
- */
- public Parcel getParcelParameter(int key) {
- Parcel p = Parcel.obtain();
- getParameter(key, p);
- return p;
- }
-
- /**
- * Gets the value of the parameter indicated by key.
- * @param key key indicates the parameter to get.
- * @return value of the parameter.
- * {@hide}
- */
- public String getStringParameter(int key) {
- Parcel p = Parcel.obtain();
- getParameter(key, p);
- String ret = p.readString();
- p.recycle();
- return ret;
- }
-
- /**
- * Gets the value of the parameter indicated by key.
- * @param key key indicates the parameter to get.
- * @return value of the parameter.
- * {@hide}
- */
- public int getIntParameter(int key) {
- Parcel p = Parcel.obtain();
- getParameter(key, p);
- int ret = p.readInt();
- p.recycle();
- return ret;
- }
/**
* Sets the send level of the player to the attached auxiliary effect
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 5c58503..420a195 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -16,6 +16,7 @@
package android.media;
+import android.app.ActivityThread;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -875,44 +876,45 @@
boolean wantScan = false;
boolean blockScan = false;
WifiDisplay[] oldDisplays = oldStatus != null ?
- oldStatus.getRememberedDisplays() : WifiDisplay.EMPTY_ARRAY;
+ oldStatus.getDisplays() : WifiDisplay.EMPTY_ARRAY;
WifiDisplay[] newDisplays;
- WifiDisplay[] availableDisplays;
WifiDisplay activeDisplay;
if (newStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
- newDisplays = newStatus.getRememberedDisplays();
- availableDisplays = newStatus.getAvailableDisplays();
+ newDisplays = newStatus.getDisplays();
activeDisplay = newStatus.getActiveDisplay();
} else {
- newDisplays = availableDisplays = WifiDisplay.EMPTY_ARRAY;
+ newDisplays = WifiDisplay.EMPTY_ARRAY;
activeDisplay = null;
}
for (int i = 0; i < newDisplays.length; i++) {
final WifiDisplay d = newDisplays[i];
- final boolean available = findMatchingDisplay(d, availableDisplays) != null;
- RouteInfo route = findWifiDisplayRoute(d);
- if (route == null) {
- route = makeWifiDisplayRoute(d, available);
- addRouteStatic(route);
- wantScan = true;
- } else {
- updateWifiDisplayRoute(route, d, available, newStatus);
- }
- if (d.equals(activeDisplay)) {
- selectRouteStatic(route.getSupportedTypes(), route);
+ if (d.isRemembered()) {
+ RouteInfo route = findWifiDisplayRoute(d);
+ if (route == null) {
+ route = makeWifiDisplayRoute(d, newStatus);
+ addRouteStatic(route);
+ wantScan = true;
+ } else {
+ updateWifiDisplayRoute(route, d, newStatus);
+ }
+ if (d.equals(activeDisplay)) {
+ selectRouteStatic(route.getSupportedTypes(), route);
- // Don't scan if we're already connected to a wifi display,
- // the scanning process can cause a hiccup with some configurations.
- blockScan = true;
+ // Don't scan if we're already connected to a wifi display,
+ // the scanning process can cause a hiccup with some configurations.
+ blockScan = true;
+ }
}
}
for (int i = 0; i < oldDisplays.length; i++) {
final WifiDisplay d = oldDisplays[i];
- final WifiDisplay newDisplay = findMatchingDisplay(d, newDisplays);
- if (newDisplay == null) {
- removeRoute(findWifiDisplayRoute(d));
+ if (d.isRemembered()) {
+ final WifiDisplay newDisplay = findMatchingDisplay(d, newDisplays);
+ if (newDisplay == null || !newDisplay.isRemembered()) {
+ removeRoute(findWifiDisplayRoute(d));
+ }
}
}
@@ -923,42 +925,20 @@
sStatic.mLastKnownWifiDisplayStatus = newStatus;
}
- static RouteInfo makeWifiDisplayRoute(WifiDisplay display, boolean available) {
- final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
- newRoute.mDeviceAddress = display.getDeviceAddress();
- newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
- newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
- newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
-
- newRoute.setStatusCode(available ?
- RouteInfo.STATUS_AVAILABLE : RouteInfo.STATUS_CONNECTING);
- newRoute.mEnabled = available;
-
- newRoute.mName = display.getFriendlyDisplayName();
- newRoute.mDescription = sStatic.mResources.getText(
- com.android.internal.R.string.wireless_display_route_description);
-
- newRoute.mPresentationDisplay = choosePresentationDisplayForRoute(newRoute,
- sStatic.getAllPresentationDisplays());
- return newRoute;
- }
-
- private static void updateWifiDisplayRoute(RouteInfo route, WifiDisplay display,
- boolean available, WifiDisplayStatus wifiDisplayStatus) {
- final boolean isScanning =
- wifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING;
-
- boolean changed = false;
+ static int getWifiDisplayStatusCode(WifiDisplay d, WifiDisplayStatus wfdStatus) {
int newStatus = RouteInfo.STATUS_NONE;
- if (available) {
- newStatus = isScanning ? RouteInfo.STATUS_SCANNING : RouteInfo.STATUS_AVAILABLE;
+ if (wfdStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
+ newStatus = RouteInfo.STATUS_SCANNING;
+ } else if (d.isAvailable()) {
+ newStatus = d.canConnect() ?
+ RouteInfo.STATUS_AVAILABLE: RouteInfo.STATUS_IN_USE;
} else {
newStatus = RouteInfo.STATUS_NOT_AVAILABLE;
}
- if (display.equals(wifiDisplayStatus.getActiveDisplay())) {
- final int activeState = wifiDisplayStatus.getActiveDisplayState();
+ if (d.equals(wfdStatus.getActiveDisplay())) {
+ final int activeState = wfdStatus.getActiveDisplayState();
switch (activeState) {
case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
newStatus = RouteInfo.STATUS_NONE;
@@ -972,22 +952,51 @@
}
}
+ return newStatus;
+ }
+
+ static boolean isWifiDisplayEnabled(WifiDisplay d, WifiDisplayStatus wfdStatus) {
+ return d.isAvailable() && (d.canConnect() || d.equals(wfdStatus.getActiveDisplay()));
+ }
+
+ static RouteInfo makeWifiDisplayRoute(WifiDisplay display, WifiDisplayStatus wfdStatus) {
+ final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
+ newRoute.mDeviceAddress = display.getDeviceAddress();
+ newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
+ newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
+ newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
+
+ newRoute.setStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
+ newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus);
+ newRoute.mName = display.getFriendlyDisplayName();
+ newRoute.mDescription = sStatic.mResources.getText(
+ com.android.internal.R.string.wireless_display_route_description);
+
+ newRoute.mPresentationDisplay = choosePresentationDisplayForRoute(newRoute,
+ sStatic.getAllPresentationDisplays());
+ return newRoute;
+ }
+
+ private static void updateWifiDisplayRoute(
+ RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus) {
+ boolean changed = false;
final String newName = display.getFriendlyDisplayName();
if (!route.getName().equals(newName)) {
route.mName = newName;
changed = true;
}
- changed |= route.mEnabled != available;
- route.mEnabled = available;
+ boolean enabled = isWifiDisplayEnabled(display, wfdStatus);
+ changed |= route.mEnabled != enabled;
+ route.mEnabled = enabled;
- changed |= route.setStatusCode(newStatus);
+ changed |= route.setStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
if (changed) {
dispatchRouteChanged(route);
}
- if (!available && route == sStatic.mSelectedRoute) {
+ if (!enabled && route == sStatic.mSelectedRoute) {
// Oops, no longer available. Reselect the default.
final RouteInfo defaultRoute = sStatic.mDefaultAudioVideo;
selectRouteStatic(defaultRoute.getSupportedTypes(), defaultRoute);
@@ -1068,6 +1077,7 @@
/** @hide */ public static final int STATUS_CONNECTING = 2;
/** @hide */ public static final int STATUS_AVAILABLE = 3;
/** @hide */ public static final int STATUS_NOT_AVAILABLE = 4;
+ /** @hide */ public static final int STATUS_IN_USE = 5;
private Object mTag;
@@ -1179,6 +1189,9 @@
case STATUS_NOT_AVAILABLE:
resId = com.android.internal.R.string.media_route_status_not_available;
break;
+ case STATUS_IN_USE:
+ resId = com.android.internal.R.string.media_route_status_in_use;
+ break;
}
mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
return true;
@@ -1292,7 +1305,8 @@
public void requestSetVolume(int volume) {
if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
try {
- sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0);
+ sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
+ ActivityThread.currentPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error setting local stream volume", e);
}
@@ -1312,7 +1326,8 @@
try {
final int volume =
Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
- sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0);
+ sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
+ ActivityThread.currentPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error setting local stream volume", e);
}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index ebbfad9..c335e55 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -24,7 +24,6 @@
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
-import android.provider.DrmStore;
import android.provider.MediaStore;
import android.provider.Settings;
import android.util.Log;
@@ -50,12 +49,6 @@
MediaStore.Audio.Media.TITLE
};
- private static final String[] DRM_COLUMNS = new String[] {
- DrmStore.Audio._ID,
- DrmStore.Audio.DATA,
- DrmStore.Audio.TITLE
- };
-
private final Context mContext;
private final AudioManager mAudioManager;
private final boolean mAllowRemote;
@@ -101,8 +94,8 @@
}
/**
- * Returns a human-presentable title for ringtone. Looks in media and DRM
- * content providers. If not in either, uses the filename
+ * Returns a human-presentable title for ringtone. Looks in media
+ * content provider. If not in either, uses the filename
*
* @param context A context used for querying.
*/
@@ -131,9 +124,7 @@
}
} else {
try {
- if (DrmStore.AUTHORITY.equals(authority)) {
- cursor = res.query(uri, DRM_COLUMNS, null, null, null);
- } else if (MediaStore.AUTHORITY.equals(authority)) {
+ if (MediaStore.AUTHORITY.equals(authority)) {
cursor = res.query(uri, MEDIA_COLUMNS, null, null, null);
}
} catch (SecurityException e) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 5e18bfa..8e4004b 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -27,7 +27,6 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
-import android.provider.DrmStore;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.System;
@@ -85,7 +84,6 @@
* {@link #EXTRA_RINGTONE_SHOW_DEFAULT},
* {@link #EXTRA_RINGTONE_SHOW_SILENT}, {@link #EXTRA_RINGTONE_TYPE},
* {@link #EXTRA_RINGTONE_DEFAULT_URI}, {@link #EXTRA_RINGTONE_TITLE},
- * {@link #EXTRA_RINGTONE_INCLUDE_DRM}.
* <p>
* Output: {@link #EXTRA_RINGTONE_PICKED_URI}.
*/
@@ -113,7 +111,9 @@
/**
* Given to the ringtone picker as a boolean. Whether to include DRM ringtones.
+ * @deprecated DRM ringtones are no longer supported
*/
+ @Deprecated
public static final String EXTRA_RINGTONE_INCLUDE_DRM =
"android.intent.extra.ringtone.INCLUDE_DRM";
@@ -183,12 +183,6 @@
MediaStore.Audio.Media.TITLE_KEY
};
- private static final String[] DRM_COLUMNS = new String[] {
- DrmStore.Audio._ID, DrmStore.Audio.TITLE,
- "\"" + DrmStore.Audio.CONTENT_URI + "\"",
- DrmStore.Audio.TITLE + " AS " + MediaStore.Audio.Media.TITLE_KEY
- };
-
private static final String[] MEDIA_COLUMNS = new String[] {
MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
"\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "\"",
@@ -228,8 +222,6 @@
private boolean mStopPreviousRingtone = true;
private Ringtone mPreviousRingtone;
-
- private boolean mIncludeDrm;
/**
* Constructs a RingtoneManager. This constructor is recommended as its
@@ -328,18 +320,26 @@
*
* @return Whether DRM ringtones will be included.
* @see #setIncludeDrm(boolean)
+ * Obsolete - always returns false
+ * @deprecated DRM ringtones are no longer supported
*/
+ @Deprecated
public boolean getIncludeDrm() {
- return mIncludeDrm;
+ return false;
}
/**
* Sets whether to include DRM ringtones.
*
* @param includeDrm Whether to include DRM ringtones.
+ * Obsolete - no longer has any effect
+ * @deprecated DRM ringtones are no longer supported
*/
+ @Deprecated
public void setIncludeDrm(boolean includeDrm) {
- mIncludeDrm = includeDrm;
+ if (includeDrm) {
+ Log.w(TAG, "setIncludeDrm no longer supported");
+ }
}
/**
@@ -363,10 +363,9 @@
}
final Cursor internalCursor = getInternalRingtones();
- final Cursor drmCursor = mIncludeDrm ? getDrmRingtones() : null;
final Cursor mediaCursor = getMediaRingtones();
- return mCursor = new SortCursor(new Cursor[] { internalCursor, drmCursor, mediaCursor },
+ return mCursor = new SortCursor(new Cursor[] { internalCursor, mediaCursor },
MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
}
@@ -462,10 +461,6 @@
uri = getValidRingtoneUriFromCursorAndClose(context, rm.getMediaRingtones());
}
- if (uri == null) {
- uri = getValidRingtoneUriFromCursorAndClose(context, rm.getDrmRingtones());
- }
-
return uri;
}
@@ -487,16 +482,9 @@
private Cursor getInternalRingtones() {
return query(
MediaStore.Audio.Media.INTERNAL_CONTENT_URI, INTERNAL_COLUMNS,
- constructBooleanTrueWhereClause(mFilterColumns, mIncludeDrm),
+ constructBooleanTrueWhereClause(mFilterColumns),
null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
}
-
- private Cursor getDrmRingtones() {
- // DRM store does not have any columns to use for filtering
- return query(
- DrmStore.Audio.CONTENT_URI, DRM_COLUMNS,
- null, null, DrmStore.Audio.TITLE);
- }
private Cursor getMediaRingtones() {
// Get the external media cursor. First check to see if it is mounted.
@@ -506,7 +494,7 @@
status.equals(Environment.MEDIA_MOUNTED_READ_ONLY))
? query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
- constructBooleanTrueWhereClause(mFilterColumns, mIncludeDrm), null,
+ constructBooleanTrueWhereClause(mFilterColumns), null,
MediaStore.Audio.Media.DEFAULT_SORT_ORDER)
: null;
}
@@ -536,7 +524,7 @@
* @param columns The columns that must be true.
* @return The where clause.
*/
- private static String constructBooleanTrueWhereClause(List<String> columns, boolean includeDrm) {
+ private static String constructBooleanTrueWhereClause(List<String> columns) {
if (columns == null) return null;
@@ -554,15 +542,6 @@
sb.append(")");
- if (!includeDrm) {
- // If not DRM files should be shown, the where clause
- // will be something like "(is_notification=1) and is_drm=0"
- sb.append(" and ");
- sb.append(MediaStore.MediaColumns.IS_DRM);
- sb.append("=0");
- }
-
-
return sb.toString();
}
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 587af47..5127479 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -16,19 +16,21 @@
package android.media;
-import android.util.AndroidRuntimeException;
-import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
-import android.os.ParcelFileDescriptor;
+import java.io.IOException;
import java.lang.ref.WeakReference;
+
import android.content.Context;
import android.content.res.AssetFileDescriptor;
-import java.io.IOException;
-
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemProperties;
+import android.util.AndroidRuntimeException;
+import android.util.Log;
+
/**
* The SoundPool class manages and plays audio resources for applications.
@@ -102,24 +104,8 @@
* another level, a new SoundPool is created, sounds are loaded, and play
* resumes.</p>
*/
-public class SoundPool
-{
- static { System.loadLibrary("soundpool"); }
-
- private final static String TAG = "SoundPool";
- private final static boolean DEBUG = false;
-
- private int mNativeContext; // accessed by native methods
-
- private EventHandler mEventHandler;
- private OnLoadCompleteListener mOnLoadCompleteListener;
-
- private final Object mLock;
-
- // SoundPool messages
- //
- // must match SoundPool.h
- private static final int SAMPLE_LOADED = 1;
+public class SoundPool {
+ private final SoundPoolDelegate mImpl;
/**
* Constructor. Constructs a SoundPool object with the following
@@ -135,12 +121,11 @@
* @return a SoundPool object, or null if creation failed
*/
public SoundPool(int maxStreams, int streamType, int srcQuality) {
-
- // do native setup
- if (native_setup(new WeakReference(this), maxStreams, streamType, srcQuality) != 0) {
- throw new RuntimeException("Native setup failed");
+ if (SystemProperties.getBoolean("config.disable_media", false)) {
+ mImpl = new SoundPoolStub();
+ } else {
+ mImpl = new SoundPoolImpl(this, maxStreams, streamType, srcQuality);
}
- mLock = new Object();
}
/**
@@ -151,25 +136,8 @@
* a value of 1 for future compatibility.
* @return a sound ID. This value can be used to play or unload the sound.
*/
- public int load(String path, int priority)
- {
- // pass network streams to player
- if (path.startsWith("http:"))
- return _load(path, priority);
-
- // try local path
- int id = 0;
- try {
- File f = new File(path);
- ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
- if (fd != null) {
- id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
- fd.close();
- }
- } catch (java.io.IOException e) {
- Log.e(TAG, "error loading " + path);
- }
- return id;
+ public int load(String path, int priority) {
+ return mImpl.load(path, priority);
}
/**
@@ -188,17 +156,7 @@
* @return a sound ID. This value can be used to play or unload the sound.
*/
public int load(Context context, int resId, int priority) {
- AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
- int id = 0;
- if (afd != null) {
- id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
- try {
- afd.close();
- } catch (java.io.IOException ex) {
- //Log.d(TAG, "close failed:", ex);
- }
- }
- return id;
+ return mImpl.load(context, resId, priority);
}
/**
@@ -210,15 +168,7 @@
* @return a sound ID. This value can be used to play or unload the sound.
*/
public int load(AssetFileDescriptor afd, int priority) {
- if (afd != null) {
- long len = afd.getLength();
- if (len < 0) {
- throw new AndroidRuntimeException("no length for fd");
- }
- return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);
- } else {
- return 0;
- }
+ return mImpl.load(afd, priority);
}
/**
@@ -236,13 +186,9 @@
* @return a sound ID. This value can be used to play or unload the sound.
*/
public int load(FileDescriptor fd, long offset, long length, int priority) {
- return _load(fd, offset, length, priority);
+ return mImpl.load(fd, offset, length, priority);
}
- private native final int _load(String uri, int priority);
-
- private native final int _load(FileDescriptor fd, long offset, long length, int priority);
-
/**
* Unload a sound from a sound ID.
*
@@ -253,7 +199,9 @@
* @param soundID a soundID returned by the load() function
* @return true if just unloaded, false if previously unloaded
*/
- public native final boolean unload(int soundID);
+ public final boolean unload(int soundID) {
+ return mImpl.unload(soundID);
+ }
/**
* Play a sound from a sound ID.
@@ -279,8 +227,11 @@
* @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
* @return non-zero streamID if successful, zero if failed
*/
- public native final int play(int soundID, float leftVolume, float rightVolume,
- int priority, int loop, float rate);
+ public final int play(int soundID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate) {
+ return mImpl.play(
+ soundID, leftVolume, rightVolume, priority, loop, rate);
+ }
/**
* Pause a playback stream.
@@ -293,7 +244,9 @@
*
* @param streamID a streamID returned by the play() function
*/
- public native final void pause(int streamID);
+ public final void pause(int streamID) {
+ mImpl.pause(streamID);
+ }
/**
* Resume a playback stream.
@@ -305,7 +258,9 @@
*
* @param streamID a streamID returned by the play() function
*/
- public native final void resume(int streamID);
+ public final void resume(int streamID) {
+ mImpl.resume(streamID);
+ }
/**
* Pause all active streams.
@@ -315,7 +270,9 @@
* are playing. It also sets a flag so that any streams that
* are playing can be resumed by calling autoResume().
*/
- public native final void autoPause();
+ public final void autoPause() {
+ mImpl.autoPause();
+ }
/**
* Resume all previously active streams.
@@ -323,7 +280,9 @@
* Automatically resumes all streams that were paused in previous
* calls to autoPause().
*/
- public native final void autoResume();
+ public final void autoResume() {
+ mImpl.autoResume();
+ }
/**
* Stop a playback stream.
@@ -336,7 +295,9 @@
*
* @param streamID a streamID returned by the play() function
*/
- public native final void stop(int streamID);
+ public final void stop(int streamID) {
+ mImpl.stop(streamID);
+ }
/**
* Set stream volume.
@@ -350,8 +311,10 @@
* @param leftVolume left volume value (range = 0.0 to 1.0)
* @param rightVolume right volume value (range = 0.0 to 1.0)
*/
- public native final void setVolume(int streamID,
- float leftVolume, float rightVolume);
+ public final void setVolume(int streamID,
+ float leftVolume, float rightVolume) {
+ mImpl.setVolume(streamID, leftVolume, rightVolume);
+ }
/**
* Similar, except set volume of all channels to same value.
@@ -371,7 +334,9 @@
*
* @param streamID a streamID returned by the play() function
*/
- public native final void setPriority(int streamID, int priority);
+ public final void setPriority(int streamID, int priority) {
+ mImpl.setPriority(streamID, priority);
+ }
/**
* Set loop mode.
@@ -384,7 +349,9 @@
* @param streamID a streamID returned by the play() function
* @param loop loop mode (0 = no loop, -1 = loop forever)
*/
- public native final void setLoop(int streamID, int loop);
+ public final void setLoop(int streamID, int loop) {
+ mImpl.setLoop(streamID, loop);
+ }
/**
* Change playback rate.
@@ -398,14 +365,11 @@
* @param streamID a streamID returned by the play() function
* @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
*/
- public native final void setRate(int streamID, float rate);
+ public final void setRate(int streamID, float rate) {
+ mImpl.setRate(streamID, rate);
+ }
- /**
- * Interface definition for a callback to be invoked when all the
- * sounds are loaded.
- */
- public interface OnLoadCompleteListener
- {
+ public interface OnLoadCompleteListener {
/**
* Called when a sound has completed loading.
*
@@ -419,64 +383,8 @@
/**
* Sets the callback hook for the OnLoadCompleteListener.
*/
- public void setOnLoadCompleteListener(OnLoadCompleteListener listener)
- {
- synchronized(mLock) {
- if (listener != null) {
- // setup message handler
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else {
- mEventHandler = null;
- }
- } else {
- mEventHandler = null;
- }
- mOnLoadCompleteListener = listener;
- }
- }
-
- private class EventHandler extends Handler
- {
- private SoundPool mSoundPool;
-
- public EventHandler(SoundPool soundPool, Looper looper) {
- super(looper);
- mSoundPool = soundPool;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case SAMPLE_LOADED:
- if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
- synchronized(mLock) {
- if (mOnLoadCompleteListener != null) {
- mOnLoadCompleteListener.onLoadComplete(mSoundPool, msg.arg1, msg.arg2);
- }
- }
- break;
- default:
- Log.e(TAG, "Unknown message type " + msg.what);
- return;
- }
- }
- }
-
- // post event from native code to message handler
- private static void postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)
- {
- SoundPool soundPool = (SoundPool)((WeakReference)weakRef).get();
- if (soundPool == null)
- return;
-
- if (soundPool.mEventHandler != null) {
- Message m = soundPool.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
- soundPool.mEventHandler.sendMessage(m);
- }
+ public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {
+ mImpl.setOnLoadCompleteListener(listener);
}
/**
@@ -486,9 +394,286 @@
* object. The SoundPool can no longer be used and the reference
* should be set to null.
*/
- public native final void release();
+ public final void release() {
+ mImpl.release();
+ }
- private native final int native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality);
+ /**
+ * Interface for SoundPool implementations.
+ * SoundPool is statically referenced and unconditionally called from all
+ * over the framework, so we can't simply omit the class or make it throw
+ * runtime exceptions, as doing so would break the framework. Instead we
+ * now select either a real or no-op impl object based on whether media is
+ * enabled.
+ *
+ * @hide
+ */
+ /* package */ interface SoundPoolDelegate {
+ public int load(String path, int priority);
+ public int load(Context context, int resId, int priority);
+ public int load(AssetFileDescriptor afd, int priority);
+ public int load(
+ FileDescriptor fd, long offset, long length, int priority);
+ public boolean unload(int soundID);
+ public int play(
+ int soundID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate);
+ public void pause(int streamID);
+ public void resume(int streamID);
+ public void autoPause();
+ public void autoResume();
+ public void stop(int streamID);
+ public void setVolume(int streamID, float leftVolume, float rightVolume);
+ public void setVolume(int streamID, float volume);
+ public void setPriority(int streamID, int priority);
+ public void setLoop(int streamID, int loop);
+ public void setRate(int streamID, float rate);
+ public void setOnLoadCompleteListener(OnLoadCompleteListener listener);
+ public void release();
+ }
- protected void finalize() { release(); }
+
+ /**
+ * Real implementation of the delegate interface. This was formerly the
+ * body of SoundPool itself.
+ */
+ /* package */ static class SoundPoolImpl implements SoundPoolDelegate {
+ static { System.loadLibrary("soundpool"); }
+
+ private final static String TAG = "SoundPool";
+ private final static boolean DEBUG = false;
+
+ private int mNativeContext; // accessed by native methods
+
+ private EventHandler mEventHandler;
+ private SoundPool.OnLoadCompleteListener mOnLoadCompleteListener;
+ private SoundPool mProxy;
+
+ private final Object mLock;
+
+ // SoundPool messages
+ //
+ // must match SoundPool.h
+ private static final int SAMPLE_LOADED = 1;
+
+ public SoundPoolImpl(SoundPool proxy, int maxStreams, int streamType, int srcQuality) {
+
+ // do native setup
+ if (native_setup(new WeakReference(this), maxStreams, streamType, srcQuality) != 0) {
+ throw new RuntimeException("Native setup failed");
+ }
+ mLock = new Object();
+ mProxy = proxy;
+ }
+
+ public int load(String path, int priority)
+ {
+ // pass network streams to player
+ if (path.startsWith("http:"))
+ return _load(path, priority);
+
+ // try local path
+ int id = 0;
+ try {
+ File f = new File(path);
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+ if (fd != null) {
+ id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
+ fd.close();
+ }
+ } catch (java.io.IOException e) {
+ Log.e(TAG, "error loading " + path);
+ }
+ return id;
+ }
+
+ public int load(Context context, int resId, int priority) {
+ AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
+ int id = 0;
+ if (afd != null) {
+ id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
+ try {
+ afd.close();
+ } catch (java.io.IOException ex) {
+ //Log.d(TAG, "close failed:", ex);
+ }
+ }
+ return id;
+ }
+
+ public int load(AssetFileDescriptor afd, int priority) {
+ if (afd != null) {
+ long len = afd.getLength();
+ if (len < 0) {
+ throw new AndroidRuntimeException("no length for fd");
+ }
+ return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);
+ } else {
+ return 0;
+ }
+ }
+
+ public int load(FileDescriptor fd, long offset, long length, int priority) {
+ return _load(fd, offset, length, priority);
+ }
+
+ private native final int _load(String uri, int priority);
+
+ private native final int _load(FileDescriptor fd, long offset, long length, int priority);
+
+ public native final boolean unload(int soundID);
+
+ public native final int play(int soundID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate);
+
+ public native final void pause(int streamID);
+
+ public native final void resume(int streamID);
+
+ public native final void autoPause();
+
+ public native final void autoResume();
+
+ public native final void stop(int streamID);
+
+ public native final void setVolume(int streamID,
+ float leftVolume, float rightVolume);
+
+ public void setVolume(int streamID, float volume) {
+ setVolume(streamID, volume, volume);
+ }
+
+ public native final void setPriority(int streamID, int priority);
+
+ public native final void setLoop(int streamID, int loop);
+
+ public native final void setRate(int streamID, float rate);
+
+ public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
+ {
+ synchronized(mLock) {
+ if (listener != null) {
+ // setup message handler
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mEventHandler = new EventHandler(mProxy, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mEventHandler = new EventHandler(mProxy, looper);
+ } else {
+ mEventHandler = null;
+ }
+ } else {
+ mEventHandler = null;
+ }
+ mOnLoadCompleteListener = listener;
+ }
+ }
+
+ private class EventHandler extends Handler
+ {
+ private SoundPool mSoundPool;
+
+ public EventHandler(SoundPool soundPool, Looper looper) {
+ super(looper);
+ mSoundPool = soundPool;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case SAMPLE_LOADED:
+ if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
+ synchronized(mLock) {
+ if (mOnLoadCompleteListener != null) {
+ mOnLoadCompleteListener.onLoadComplete(mSoundPool, msg.arg1, msg.arg2);
+ }
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown message type " + msg.what);
+ return;
+ }
+ }
+ }
+
+ // post event from native code to message handler
+ private static void postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)
+ {
+ SoundPoolImpl soundPoolImpl = (SoundPoolImpl)((WeakReference)weakRef).get();
+ if (soundPoolImpl == null)
+ return;
+
+ if (soundPoolImpl.mEventHandler != null) {
+ Message m = soundPoolImpl.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
+ soundPoolImpl.mEventHandler.sendMessage(m);
+ }
+ }
+
+ public native final void release();
+
+ private native final int native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality);
+
+ protected void finalize() { release(); }
+ }
+
+ /**
+ * No-op implementation of SoundPool.
+ * Used when media is disabled by the system.
+ * @hide
+ */
+ /* package */ static class SoundPoolStub implements SoundPoolDelegate {
+ public SoundPoolStub() { }
+
+ public int load(String path, int priority) {
+ return 0;
+ }
+
+ public int load(Context context, int resId, int priority) {
+ return 0;
+ }
+
+ public int load(AssetFileDescriptor afd, int priority) {
+ return 0;
+ }
+
+ public int load(FileDescriptor fd, long offset, long length, int priority) {
+ return 0;
+ }
+
+ public final boolean unload(int soundID) {
+ return true;
+ }
+
+ public final int play(int soundID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate) {
+ return 0;
+ }
+
+ public final void pause(int streamID) { }
+
+ public final void resume(int streamID) { }
+
+ public final void autoPause() { }
+
+ public final void autoResume() { }
+
+ public final void stop(int streamID) { }
+
+ public final void setVolume(int streamID,
+ float leftVolume, float rightVolume) { }
+
+ public void setVolume(int streamID, float volume) {
+ }
+
+ public final void setPriority(int streamID, int priority) { }
+
+ public final void setLoop(int streamID, int loop) { }
+
+ public final void setRate(int streamID, float rate) { }
+
+ public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) {
+ }
+
+ public final void release() { }
+ }
}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 416a2a1..63a61e2 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,6 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ android_media_ImageReader.cpp \
android_media_MediaCrypto.cpp \
android_media_MediaCodec.cpp \
android_media_MediaCodecList.cpp \
@@ -56,6 +57,8 @@
frameworks/av/media/libstagefright/codecs/amrnb/common/include \
frameworks/av/media/mtp \
frameworks/native/include/media/openmax \
+ $(call include-path-for, libhardware)/hardware \
+ system/media/camera/include \
$(PV_INCLUDES) \
$(JNI_H_INCLUDE) \
$(call include-path-for, corecg graphics)
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
new file mode 100644
index 0000000..7866df4
--- /dev/null
+++ b/media/jni/android_media_ImageReader.cpp
@@ -0,0 +1,780 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ImageReader_JNI"
+#include <utils/Log.h>
+#include <utils/misc.h>
+#include <utils/List.h>
+
+#include <cstdio>
+
+#include <gui/CpuConsumer.h>
+#include <gui/Surface.h>
+#include <camera3.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_view_Surface.h>
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
+
+#define ANDROID_MEDIA_IMAGEREADER_JNI_ID "mCpuConsumer"
+#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext"
+#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mLockedBuffer"
+#define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID "mTimestamp"
+
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+enum {
+ IMAGE_READER_MAX_NUM_PLANES = 3,
+};
+
+struct fields_t {
+ // For ImageReader class
+ jfieldID imageReaderContext;
+ jmethodID postEvent;
+ // For SurfaceImage class
+ jfieldID buffer;
+ jfieldID timeStamp;
+};
+
+struct classInfo_t {
+ jclass clazz;
+ jmethodID ctor;
+};
+
+static fields_t fields;
+static classInfo_t surfPlaneClassInfo;
+
+// ----------------------------------------------------------------------------
+
+class JNIImageReaderContext : public CpuConsumer::FrameAvailableListener
+{
+public:
+ JNIImageReaderContext(JNIEnv* env, jobject weakThiz, jclass clazz, int maxImages);
+
+ virtual ~JNIImageReaderContext();
+
+ virtual void onFrameAvailable();
+
+ CpuConsumer::LockedBuffer* getLockedBuffer();
+
+ void returnLockedBuffer(CpuConsumer::LockedBuffer* buffer);
+
+ CpuConsumer* getCpuConsumer() { return mConsumer.get(); }
+
+ void setCpuConsumer(sp<CpuConsumer> consumer) { mConsumer = consumer; }
+
+ void setBufferFormat(int format) { mFormat = format; }
+ int getBufferFormat() { return mFormat; }
+
+ void setBufferWidth(int width) { mWidth = width; }
+ int getBufferWidth() { return mWidth; }
+
+ void setBufferHeight(int height) { mHeight = height; }
+ int getBufferHeight() { return mHeight; }
+
+private:
+ static JNIEnv* getJNIEnv(bool* needsDetach);
+ static void detachJNI();
+
+ List<CpuConsumer::LockedBuffer*> mBuffers;
+ sp<CpuConsumer> mConsumer;
+ jobject mWeakThiz;
+ jclass mClazz;
+ int mFormat;
+ int mWidth;
+ int mHeight;
+};
+
+JNIImageReaderContext::JNIImageReaderContext(JNIEnv* env,
+ jobject weakThiz, jclass clazz, int maxImages) :
+ mWeakThiz(env->NewGlobalRef(weakThiz)),
+ mClazz((jclass)env->NewGlobalRef(clazz)) {
+ for (int i = 0; i < maxImages; i++) {
+ CpuConsumer::LockedBuffer *buffer = new CpuConsumer::LockedBuffer;
+ mBuffers.push_back(buffer);
+ }
+}
+
+JNIEnv* JNIImageReaderContext::getJNIEnv(bool* needsDetach) {
+ LOG_ALWAYS_FATAL_IF(needsDetach == NULL, "needsDetach is null!!!");
+ *needsDetach = false;
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ int result = vm->AttachCurrentThread(&env, (void*) &args);
+ if (result != JNI_OK) {
+ ALOGE("thread attach failed: %#x", result);
+ return NULL;
+ }
+ *needsDetach = true;
+ }
+ return env;
+}
+
+void JNIImageReaderContext::detachJNI() {
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ int result = vm->DetachCurrentThread();
+ if (result != JNI_OK) {
+ ALOGE("thread detach failed: %#x", result);
+ }
+}
+
+CpuConsumer::LockedBuffer* JNIImageReaderContext::getLockedBuffer() {
+ if (mBuffers.empty()) {
+ return NULL;
+ }
+ // Return a LockedBuffer pointer and remove it from the list
+ List<CpuConsumer::LockedBuffer*>::iterator it = mBuffers.begin();
+ CpuConsumer::LockedBuffer* buffer = *it;
+ mBuffers.erase(it);
+ return buffer;
+}
+
+void JNIImageReaderContext::returnLockedBuffer(CpuConsumer::LockedBuffer * buffer) {
+ mBuffers.push_back(buffer);
+}
+
+JNIImageReaderContext::~JNIImageReaderContext() {
+ bool needsDetach = false;
+ JNIEnv* env = getJNIEnv(&needsDetach);
+ if (env != NULL) {
+ env->DeleteGlobalRef(mWeakThiz);
+ env->DeleteGlobalRef(mClazz);
+ } else {
+ ALOGW("leaking JNI object references");
+ }
+ if (needsDetach) {
+ detachJNI();
+ }
+
+ // Delete LockedBuffers
+ for (List<CpuConsumer::LockedBuffer *>::iterator it = mBuffers.begin();
+ it != mBuffers.end(); it++) {
+ delete *it;
+ }
+ mBuffers.clear();
+ mConsumer.clear();
+}
+
+void JNIImageReaderContext::onFrameAvailable()
+{
+ ALOGV("%s: frame available", __FUNCTION__);
+ bool needsDetach = false;
+ JNIEnv* env = getJNIEnv(&needsDetach);
+ if (env != NULL) {
+ env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
+ } else {
+ ALOGW("onFrameAvailable event will not posted");
+ }
+ if (needsDetach) {
+ detachJNI();
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+extern "C" {
+
+static JNIImageReaderContext* ImageReader_getContext(JNIEnv* env, jobject thiz)
+{
+ JNIImageReaderContext *ctx;
+ ctx = reinterpret_cast<JNIImageReaderContext *>
+ (env->GetLongField(thiz, fields.imageReaderContext));
+ return ctx;
+}
+
+static CpuConsumer* ImageReader_getCpuConsumer(JNIEnv* env, jobject thiz)
+{
+ ALOGV("%s:", __FUNCTION__);
+ JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz);
+ if (ctx == NULL) {
+ jniThrowRuntimeException(env, "ImageReaderContext is not initialized");
+ return NULL;
+ }
+ return ctx->getCpuConsumer();
+}
+
+static void ImageReader_setNativeContext(JNIEnv* env,
+ jobject thiz, sp<JNIImageReaderContext> ctx)
+{
+ ALOGV("%s:", __FUNCTION__);
+ JNIImageReaderContext* const p = ImageReader_getContext(env, thiz);
+ if (ctx != 0) {
+ ctx->incStrong((void*)ImageReader_setNativeContext);
+ }
+ if (p) {
+ p->decStrong((void*)ImageReader_setNativeContext);
+ }
+ env->SetLongField(thiz, fields.imageReaderContext, reinterpret_cast<jlong>(ctx.get()));
+}
+
+static CpuConsumer::LockedBuffer* Image_getLockedBuffer(JNIEnv* env, jobject image)
+{
+ return reinterpret_cast<CpuConsumer::LockedBuffer*>(env->GetLongField(image, fields.buffer));
+}
+
+static void Image_setBuffer(JNIEnv* env, jobject thiz,
+ const CpuConsumer::LockedBuffer* buffer)
+{
+ env->SetLongField(thiz, fields.buffer, reinterpret_cast<jlong>(buffer));
+}
+
+// Some formats like JPEG defined with different values between android.graphics.ImageFormat and
+// graphics.h, need convert to the one defined in graphics.h here.
+static int Image_getPixelFormat(JNIEnv* env, int format)
+{
+ int jpegFormat, rawSensorFormat;
+ jfieldID fid;
+
+ ALOGV("%s: format = 0x%x", __FUNCTION__, format);
+
+ jclass imageFormatClazz = env->FindClass("android/graphics/ImageFormat");
+ ALOG_ASSERT(imageFormatClazz != NULL);
+
+ fid = env->GetStaticFieldID(imageFormatClazz, "JPEG", "I");
+ jpegFormat = env->GetStaticIntField(imageFormatClazz, fid);
+ fid = env->GetStaticFieldID(imageFormatClazz, "RAW_SENSOR", "I");
+ rawSensorFormat = env->GetStaticIntField(imageFormatClazz, fid);
+
+ // Translate the JPEG to BLOB for camera purpose, an add more if more mismatch is found.
+ if (format == jpegFormat) {
+ format = HAL_PIXEL_FORMAT_BLOB;
+ }
+ // Same thing for RAW_SENSOR format
+ if (format == rawSensorFormat) {
+ format = HAL_PIXEL_FORMAT_RAW_SENSOR;
+ }
+
+ return format;
+}
+
+static uint32_t Image_getJpegSize(CpuConsumer::LockedBuffer* buffer)
+{
+ ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!");
+ uint32_t size = 0;
+ uint32_t width = buffer->width;
+ uint8_t* jpegBuffer = buffer->data;
+
+ // First check for JPEG transport header at the end of the buffer
+ uint8_t* header = jpegBuffer + (width - sizeof(struct camera3_jpeg_blob));
+ struct camera3_jpeg_blob *blob = (struct camera3_jpeg_blob*)(header);
+ if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID) {
+ size = blob->jpeg_size;
+ ALOGV("%s: Jpeg size = %d", __FUNCTION__, size);
+ }
+
+ // failed to find size, default to whole buffer
+ if (size == 0) {
+ size = width;
+ }
+
+ return size;
+}
+
+static void Image_getLockedBufferInfo(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx,
+ uint8_t **base, uint32_t *size)
+{
+ ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!");
+ ALOG_ASSERT(base != NULL, "base is NULL!!!");
+ ALOG_ASSERT(size != NULL, "size is NULL!!!");
+ ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0));
+
+ ALOGV("%s: buffer: 0x%p", __FUNCTION__, buffer);
+
+ uint32_t dataSize, ySize, cSize, cStride;
+ uint8_t *cb, *cr;
+ uint8_t *pData = NULL;
+
+ dataSize = ySize = cSize = cStride = 0;
+ int32_t fmt = buffer->format;
+ switch (fmt) {
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ pData =
+ (idx == 0) ?
+ buffer->data :
+ (idx == 1) ?
+ buffer->dataCb :
+ buffer->dataCr;
+ if (idx == 0) {
+ dataSize = buffer->stride * buffer->height;
+ } else {
+ dataSize = buffer->chromaStride * buffer->height / 2;
+ }
+ break;
+ // NV21
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ cr = buffer->data + (buffer->stride * buffer->height);
+ cb = cr + 1;
+ ySize = buffer->width * buffer->height;
+ cSize = buffer->width * buffer->height / 2;
+
+ pData =
+ (idx == 0) ?
+ buffer->data :
+ (idx == 1) ?
+ cb:
+ cr;
+
+ dataSize = (idx == 0) ? ySize : cSize;
+ break;
+ case HAL_PIXEL_FORMAT_YV12:
+ // Y and C stride need to be 16 pixel aligned.
+ LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
+ "Stride is not 16 pixel aligned %d", buffer->stride);
+
+ ySize = buffer->stride * buffer->height;
+ cStride = ALIGN(buffer->stride / 2, 16);
+ cr = buffer->data + ySize;
+ cSize = cStride * buffer->height / 2;
+ cb = cr + cSize;
+
+ pData =
+ (idx == 0) ?
+ buffer->data :
+ (idx == 1) ?
+ cb :
+ cr;
+ dataSize = (idx == 0) ? ySize : cSize;
+ break;
+ case HAL_PIXEL_FORMAT_Y8:
+ // Single plane, 8bpp.
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height;
+ break;
+ case HAL_PIXEL_FORMAT_Y16:
+ // Single plane, 16bpp, strides are specified in pixels, not in bytes
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height * 2;
+ break;
+ case HAL_PIXEL_FORMAT_BLOB:
+ // Used for JPEG data, height must be 1, width == size, single plane.
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ ALOG_ASSERT(buffer->height == 1, "JPEG should has height value %d", buffer->height);
+
+ pData = buffer->data;
+ dataSize = Image_getJpegSize(buffer);
+ break;
+ case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ // Single plane 16bpp bayer data.
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ pData = buffer->data;
+ dataSize = buffer->width * 2 * buffer->height;
+ break;
+ default:
+ jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
+ "Pixel format: 0x%x is unsupported", fmt);
+ break;
+ }
+
+ *base = pData;
+ *size = dataSize;
+}
+
+static jint Image_imageGetPixelStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx)
+{
+ ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
+ ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0), "Index is out of range:%d", idx);
+
+ int pixelStride = 0;
+ ALOG_ASSERT(buffer != NULL, "buffer is NULL");
+
+ int32_t fmt = buffer->format;
+ switch (fmt) {
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ pixelStride = (idx == 0) ? 1 : buffer->chromaStep;
+ break;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ pixelStride = (idx == 0) ? 1 : 2;
+ break;
+ case HAL_PIXEL_FORMAT_Y8:
+ // Single plane 8bpp data.
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ pixelStride;
+ break;
+ case HAL_PIXEL_FORMAT_YV12:
+ pixelStride = 1;
+ break;
+ case HAL_PIXEL_FORMAT_BLOB:
+ // Used for JPEG data, single plane, row and pixel strides are 0
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ pixelStride = 0;
+ break;
+ case HAL_PIXEL_FORMAT_Y16:
+ case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ // Single plane 16bpp data.
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ pixelStride = 2;
+ break;
+ default:
+ jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
+ "Pixel format: 0x%x is unsupported", fmt);
+ break;
+ }
+
+ return pixelStride;
+}
+
+static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx)
+{
+ ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
+ ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0));
+
+ int rowStride = 0;
+ ALOG_ASSERT(buffer != NULL, "buffer is NULL");
+
+ int32_t fmt = buffer->format;
+
+ switch (fmt) {
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ rowStride = (idx == 0) ? buffer->stride : buffer->chromaStride;
+ break;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ rowStride = buffer->width;
+ break;
+ case HAL_PIXEL_FORMAT_YV12:
+ LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
+ "Stride is not 16 pixel aligned %d", buffer->stride);
+ rowStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
+ break;
+ case HAL_PIXEL_FORMAT_BLOB:
+ // Used for JPEG data, single plane, row and pixel strides are 0
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ rowStride = 0;
+ break;
+ case HAL_PIXEL_FORMAT_Y8:
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
+ "Stride is not 16 pixel aligned %d", buffer->stride);
+ rowStride = buffer->stride;
+ break;
+ case HAL_PIXEL_FORMAT_Y16:
+ case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ // In native side, strides are specified in pixels, not in bytes.
+ // Single plane 16bpp bayer data. even width/height,
+ // row stride multiple of 16 pixels (32 bytes)
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
+ "Stride is not 16 pixel aligned %d", buffer->stride);
+ rowStride = buffer->stride * 2;
+ break;
+ default:
+ ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt);
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
+ "unsupported buffer format");
+ break;
+ }
+
+ return rowStride;
+}
+
+// ----------------------------------------------------------------------------
+
+static void ImageReader_classInit(JNIEnv* env, jclass clazz)
+{
+ ALOGV("%s:", __FUNCTION__);
+
+ jclass imageClazz = env->FindClass("android/media/ImageReader$SurfaceImage");
+ LOG_ALWAYS_FATAL_IF(imageClazz == NULL,
+ "can't find android/graphics/ImageReader$SurfaceImage");
+ fields.buffer = env->GetFieldID(imageClazz, ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID, "J");
+ LOG_ALWAYS_FATAL_IF(fields.buffer == NULL,
+ "can't find android/graphics/ImageReader.%s",
+ ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID);
+
+ fields.timeStamp = env->GetFieldID(imageClazz, ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID, "J");
+ LOG_ALWAYS_FATAL_IF(fields.timeStamp == NULL,
+ "can't find android/graphics/ImageReader.%s",
+ ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID);
+
+ fields.imageReaderContext = env->GetFieldID(clazz, ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID, "J");
+ LOG_ALWAYS_FATAL_IF(fields.imageReaderContext == NULL,
+ "can't find android/graphics/ImageReader.%s",
+ ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID);
+
+ fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
+ "(Ljava/lang/Object;)V");
+ LOG_ALWAYS_FATAL_IF(fields.postEvent == NULL,
+ "can't find android/graphics/ImageReader.postEventFromNative");
+
+ jclass planeClazz = env->FindClass("android/media/ImageReader$SurfaceImage$SurfacePlane");
+ LOG_ALWAYS_FATAL_IF(planeClazz == NULL, "Can not find SurfacePlane class");
+ // FindClass only gives a local reference of jclass object.
+ surfPlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz);
+ surfPlaneClassInfo.ctor = env->GetMethodID(surfPlaneClassInfo.clazz, "<init>",
+ "(Landroid/media/ImageReader$SurfaceImage;III)V");
+ LOG_ALWAYS_FATAL_IF(surfPlaneClassInfo.ctor == NULL, "Can not find SurfacePlane constructor");
+}
+
+static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz,
+ jint width, jint height, jint format, jint maxImages)
+{
+ status_t res;
+ int nativeFormat;
+
+ ALOGV("%s: width:%d, height: %d, format: 0x%x, maxImages:%d",
+ __FUNCTION__, width, height, format, maxImages);
+
+ nativeFormat = Image_getPixelFormat(env, format);
+
+ sp<CpuConsumer> consumer = new CpuConsumer(maxImages);
+ // TODO: throw dvm exOutOfMemoryError?
+ if (consumer == NULL) {
+ jniThrowRuntimeException(env, "Failed to allocate native CpuConsumer");
+ return;
+ }
+
+ jclass clazz = env->GetObjectClass(thiz);
+ if (clazz == NULL) {
+ jniThrowRuntimeException(env, "Can't find android/graphics/ImageReader");
+ return;
+ }
+ sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages));
+ ctx->setCpuConsumer(consumer);
+ consumer->setFrameAvailableListener(ctx);
+ ImageReader_setNativeContext(env, thiz, ctx);
+ ctx->setBufferFormat(nativeFormat);
+ ctx->setBufferWidth(width);
+ ctx->setBufferHeight(height);
+
+ // Set the width/height/format to the CpuConsumer
+ res = consumer->setDefaultBufferSize(width, height);
+ if (res != OK) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to set CpuConsumer buffer size");
+ return;
+ }
+ res = consumer->setDefaultBufferFormat(nativeFormat);
+ if (res != OK) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to set CpuConsumer buffer format");
+ }
+}
+
+static void ImageReader_close(JNIEnv* env, jobject thiz)
+{
+ ALOGV("%s:", __FUNCTION__);
+
+ JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz);
+ if (ctx == NULL) {
+ // ImageReader is already closed.
+ return;
+ }
+
+ CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz);
+ if (consumer != NULL) {
+ consumer->abandon();
+ consumer->setFrameAvailableListener(NULL);
+ }
+ ImageReader_setNativeContext(env, thiz, NULL);
+}
+
+static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image)
+{
+ ALOGV("%s:", __FUNCTION__);
+ JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
+ if (ctx == NULL) {
+ ALOGW("ImageReader#close called before Image#close, consider calling Image#close first");
+ return;
+ }
+
+ CpuConsumer* consumer = ctx->getCpuConsumer();
+ CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, image);
+ if (!buffer) {
+ ALOGW("Image already released!!!");
+ return;
+ }
+ consumer->unlockBuffer(*buffer);
+ Image_setBuffer(env, image, NULL);
+ ctx->returnLockedBuffer(buffer);
+}
+
+static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz,
+ jobject image)
+{
+ ALOGV("%s:", __FUNCTION__);
+ JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
+ if (ctx == NULL) {
+ jniThrowRuntimeException(env, "ImageReaderContext is not initialized");
+ return false;
+ }
+
+ CpuConsumer* consumer = ctx->getCpuConsumer();
+ CpuConsumer::LockedBuffer* buffer = ctx->getLockedBuffer();
+ if (buffer == NULL) {
+ ALOGE("Unable to acquire a lockedBuffer, very likely client tries to lock more than"
+ "maxImages buffers");
+ return false;
+ }
+ status_t res = consumer->lockNextBuffer(buffer);
+ if (res != NO_ERROR) {
+ ALOGE("%s Fail to lockNextBuffer with error: 0x%x ", __FUNCTION__, res);
+ return false;
+ }
+
+
+ // Check if the left-top corner of the crop rect is origin, we currently assume this point is
+ // zero, will revist this once this assumption turns out problematic.
+ Point lt = buffer->crop.leftTop();
+ if (lt.x != 0 || lt.y != 0) {
+ ALOGE("crop left: %d, top = %d", lt.x, lt.y);
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
+ "crop left top corner need to at origin");
+ }
+
+ // Check if the producer buffer configurations match what ImageReader configured.
+ // We want to fail for the very first image because this case is too bad.
+ int outputWidth = buffer->width;
+ int outputHeight = buffer->height;
+
+ // Correct with/height when crop is set.
+ if (buffer->crop.getWidth() > 0) {
+ outputWidth = buffer->crop.getWidth() + 1;
+ }
+ if (buffer->crop.getHeight() > 0) {
+ outputHeight = buffer->crop.getHeight() + 1;
+ }
+
+ int imageReaderWidth = ctx->getBufferWidth();
+ int imageReaderHeight = ctx->getBufferHeight();
+ if ((imageReaderWidth != outputWidth) ||
+ (imageReaderHeight != outputHeight)) {
+ // Spew warning for now, since MediaCodec decoder has a bug to setup the right crop
+ // TODO: make it throw exception once the decoder bug is fixed.
+ ALOGW("Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d",
+ outputWidth, outputHeight, imageReaderWidth, imageReaderHeight);
+ }
+
+ if (ctx->getBufferFormat() != buffer->format) {
+ // Return the buffer to the queue.
+ consumer->unlockBuffer(*buffer);
+ ctx->returnLockedBuffer(buffer);
+
+ // Throw exception
+ ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x",
+ buffer->format, ctx->getBufferFormat());
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
+ "The producer output buffer configuration doesn't match the ImageReader"
+ "configured");
+ return false;
+ }
+ // Set SurfaceImage instance member variables
+ Image_setBuffer(env, image, buffer);
+ env->SetLongField(image, fields.timeStamp, static_cast<jlong>(buffer->timestamp));
+
+ return true;
+}
+
+static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz)
+{
+ ALOGV("%s: ", __FUNCTION__);
+
+ CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz);
+ if (consumer == NULL) {
+ jniThrowRuntimeException(env, "CpuConsumer is uninitialized");
+ return NULL;
+ }
+
+ // Wrap the IGBP in a Java-language Surface.
+ return android_view_Surface_createFromIGraphicBufferProducer(env,
+ consumer->getProducerInterface());
+}
+
+static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx)
+{
+ int rowStride, pixelStride;
+ ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
+
+ CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
+
+ ALOG_ASSERT(buffer != NULL);
+ if (buffer == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Image was released");
+ }
+ rowStride = Image_imageGetRowStride(env, buffer, idx);
+ pixelStride = Image_imageGetPixelStride(env, buffer, idx);
+
+ jobject surfPlaneObj = env->NewObject(surfPlaneClassInfo.clazz, surfPlaneClassInfo.ctor,
+ thiz, idx, rowStride, pixelStride);
+
+ return surfPlaneObj;
+}
+
+static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx)
+{
+ uint8_t *base = NULL;
+ uint32_t size = 0;
+ jobject byteBuffer;
+
+ ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
+
+ CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
+
+ if (buffer == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Image was released");
+ }
+
+ // Create byteBuffer from native buffer
+ Image_getLockedBufferInfo(env, buffer, idx, &base, &size);
+ byteBuffer = env->NewDirectByteBuffer(base, size);
+ // TODO: throw dvm exOutOfMemoryError?
+ if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Failed to allocate ByteBuffer");
+ }
+
+ return byteBuffer;
+}
+
+} // extern "C"
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gImageReaderMethods[] = {
+ {"nativeClassInit", "()V", (void*)ImageReader_classInit },
+ {"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init },
+ {"nativeClose", "()V", (void*)ImageReader_close },
+ {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
+ {"nativeImageSetup", "(Landroid/media/Image;)Z", (void*)ImageReader_imageSetup },
+ {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
+};
+
+static JNINativeMethod gImageMethods[] = {
+ {"nativeImageGetBuffer", "(I)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer },
+ {"nativeCreatePlane", "(I)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
+ (void*)Image_createSurfacePlane },
+};
+
+int register_android_media_ImageReader(JNIEnv *env) {
+
+ int ret1 = AndroidRuntime::registerNativeMethods(env,
+ "android/media/ImageReader", gImageReaderMethods, NELEM(gImageReaderMethods));
+
+ int ret2 = AndroidRuntime::registerNativeMethods(env,
+ "android/media/ImageReader$SurfaceImage", gImageMethods, NELEM(gImageMethods));
+
+ return (ret1 || ret2);
+}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 7c607ea..9b66c06 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -525,14 +525,6 @@
process_media_player_call( env, thiz, mp->setVolume(leftVolume, rightVolume), NULL, NULL );
}
-// FIXME: deprecated
-static jobject
-android_media_MediaPlayer_getFrameAt(JNIEnv *env, jobject thiz, jint msec)
-{
- return NULL;
-}
-
-
// Sends the request and reply parcels to the media player via the
// binder interface.
static jint
@@ -781,39 +773,6 @@
return ret;
}
-static jboolean
-android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
-{
- ALOGV("setParameter: key %d", key);
- sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return false;
- }
-
- Parcel *request = parcelForJavaObject(env, java_request);
- status_t err = mp->setParameter(key, *request);
- if (err == OK) {
- return true;
- } else {
- return false;
- }
-}
-
-static void
-android_media_MediaPlayer_getParameter(JNIEnv *env, jobject thiz, jint key, jobject java_reply)
-{
- ALOGV("getParameter: key %d", key);
- sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- Parcel *reply = parcelForJavaObject(env, java_reply);
- process_media_player_call(env, thiz, mp->getParameter(key, reply), NULL, NULL );
-}
-
static void
android_media_MediaPlayer_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject java_player)
{
@@ -912,7 +871,6 @@
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping},
{"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
- {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
{"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
@@ -924,8 +882,6 @@
{"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
- {"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_setParameter},
- {"getParameter", "(ILandroid/os/Parcel;)V", (void *)android_media_MediaPlayer_getParameter},
{"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint},
{"setNextMediaPlayer", "(Landroid/media/MediaPlayer;)V", (void *)android_media_MediaPlayer_setNextMediaPlayer},
{"updateProxyConfig", "(Landroid/net/ProxyProperties;)V", (void *)android_media_MediaPlayer_updateProxyConfig},
@@ -940,6 +896,7 @@
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
+extern int register_android_media_ImageReader(JNIEnv *env);
extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_Drm(JNIEnv *env);
extern int register_android_media_MediaCodec(JNIEnv *env);
@@ -967,6 +924,11 @@
}
assert(env != NULL);
+ if (register_android_media_ImageReader(env) < 0) {
+ ALOGE("ERROR: ImageReader native registration failed");
+ goto bail;
+ }
+
if (register_android_media_MediaPlayer(env) < 0) {
ALOGE("ERROR: MediaPlayer native registration failed\n");
goto bail;
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 5835b9f..ed8d7c1 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- android_media_SoundPool.cpp
+ android_media_SoundPool_SoundPoolImpl.cpp
LOCAL_SHARED_LIBRARIES := \
liblog \
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
deleted file mode 100644
index 9658856..0000000
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdio.h>
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "SoundPool-JNI"
-
-#include <utils/Log.h>
-#include <nativehelper/jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <media/SoundPool.h>
-
-using namespace android;
-
-static struct fields_t {
- jfieldID mNativeContext;
- jmethodID mPostEvent;
- jclass mSoundPoolClass;
-} fields;
-
-static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
- return (SoundPool*)env->GetIntField(thiz, fields.mNativeContext);
-}
-
-// ----------------------------------------------------------------------------
-static int
-android_media_SoundPool_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
-{
- ALOGV("android_media_SoundPool_load_URL");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (path == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return 0;
- }
- const char* s = env->GetStringUTFChars(path, NULL);
- int id = ap->load(s, priority);
- env->ReleaseStringUTFChars(path, s);
- return id;
-}
-
-static int
-android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
- jlong offset, jlong length, jint priority)
-{
- ALOGV("android_media_SoundPool_load_FD");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return 0;
- return ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
- int64_t(offset), int64_t(length), int(priority));
-}
-
-static bool
-android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
- ALOGV("android_media_SoundPool_unload\n");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return 0;
- return ap->unload(sampleID);
-}
-
-static int
-android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
- jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
- jfloat rate)
-{
- ALOGV("android_media_SoundPool_play\n");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return 0;
- return ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
-}
-
-static void
-android_media_SoundPool_pause(JNIEnv *env, jobject thiz, jint channelID)
-{
- ALOGV("android_media_SoundPool_pause");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return;
- ap->pause(channelID);
-}
-
-static void
-android_media_SoundPool_resume(JNIEnv *env, jobject thiz, jint channelID)
-{
- ALOGV("android_media_SoundPool_resume");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return;
- ap->resume(channelID);
-}
-
-static void
-android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz)
-{
- ALOGV("android_media_SoundPool_autoPause");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return;
- ap->autoPause();
-}
-
-static void
-android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz)
-{
- ALOGV("android_media_SoundPool_autoResume");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return;
- ap->autoResume();
-}
-
-static void
-android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID)
-{
- ALOGV("android_media_SoundPool_stop");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return;
- ap->stop(channelID);
-}
-
-static void
-android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
- float leftVolume, float rightVolume)
-{
- ALOGV("android_media_SoundPool_setVolume");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return;
- ap->setVolume(channelID, leftVolume, rightVolume);
-}
-
-static void
-android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
- int priority)
-{
- ALOGV("android_media_SoundPool_setPriority");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return;
- ap->setPriority(channelID, priority);
-}
-
-static void
-android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
- int loop)
-{
- ALOGV("android_media_SoundPool_setLoop");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return;
- ap->setLoop(channelID, loop);
-}
-
-static void
-android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
- float rate)
-{
- ALOGV("android_media_SoundPool_setRate");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap == NULL) return;
- ap->setRate(channelID, rate);
-}
-
-static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
-{
- ALOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
-}
-
-static jint
-android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
-{
- ALOGV("android_media_SoundPool_native_setup");
- SoundPool *ap = new SoundPool(maxChannels, (audio_stream_type_t) streamType, srcQuality);
- if (ap == NULL) {
- return -1;
- }
-
- // save pointer to SoundPool C++ object in opaque field in Java object
- env->SetIntField(thiz, fields.mNativeContext, (int)ap);
-
- // set callback with weak reference
- jobject globalWeakRef = env->NewGlobalRef(weakRef);
- ap->setCallback(android_media_callback, globalWeakRef);
- return 0;
-}
-
-static void
-android_media_SoundPool_release(JNIEnv *env, jobject thiz)
-{
- ALOGV("android_media_SoundPool_release");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (ap != NULL) {
-
- // release weak reference
- jobject weakRef = (jobject) ap->getUserData();
- if (weakRef != NULL) {
- env->DeleteGlobalRef(weakRef);
- }
-
- // clear callback and native context
- ap->setCallback(NULL, NULL);
- env->SetIntField(thiz, fields.mNativeContext, 0);
- delete ap;
- }
-}
-
-// ----------------------------------------------------------------------------
-
-// Dalvik VM type signatures
-static JNINativeMethod gMethods[] = {
- { "_load",
- "(Ljava/lang/String;I)I",
- (void *)android_media_SoundPool_load_URL
- },
- { "_load",
- "(Ljava/io/FileDescriptor;JJI)I",
- (void *)android_media_SoundPool_load_FD
- },
- { "unload",
- "(I)Z",
- (void *)android_media_SoundPool_unload
- },
- { "play",
- "(IFFIIF)I",
- (void *)android_media_SoundPool_play
- },
- { "pause",
- "(I)V",
- (void *)android_media_SoundPool_pause
- },
- { "resume",
- "(I)V",
- (void *)android_media_SoundPool_resume
- },
- { "autoPause",
- "()V",
- (void *)android_media_SoundPool_autoPause
- },
- { "autoResume",
- "()V",
- (void *)android_media_SoundPool_autoResume
- },
- { "stop",
- "(I)V",
- (void *)android_media_SoundPool_stop
- },
- { "setVolume",
- "(IFF)V",
- (void *)android_media_SoundPool_setVolume
- },
- { "setPriority",
- "(II)V",
- (void *)android_media_SoundPool_setPriority
- },
- { "setLoop",
- "(II)V",
- (void *)android_media_SoundPool_setLoop
- },
- { "setRate",
- "(IF)V",
- (void *)android_media_SoundPool_setRate
- },
- { "native_setup",
- "(Ljava/lang/Object;III)I",
- (void*)android_media_SoundPool_native_setup
- },
- { "release",
- "()V",
- (void*)android_media_SoundPool_release
- }
-};
-
-static const char* const kClassPathName = "android/media/SoundPool";
-
-jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
- JNIEnv* env = NULL;
- jint result = -1;
- jclass clazz;
-
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- ALOGE("ERROR: GetEnv failed\n");
- goto bail;
- }
- assert(env != NULL);
-
- clazz = env->FindClass(kClassPathName);
- if (clazz == NULL) {
- ALOGE("Can't find %s", kClassPathName);
- goto bail;
- }
-
- fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "I");
- if (fields.mNativeContext == NULL) {
- ALOGE("Can't find SoundPool.mNativeContext");
- goto bail;
- }
-
- fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
- "(Ljava/lang/Object;IIILjava/lang/Object;)V");
- if (fields.mPostEvent == NULL) {
- ALOGE("Can't find android/media/SoundPool.postEventFromNative");
- goto bail;
- }
-
- // create a reference to class. Technically, we're leaking this reference
- // since it's a static object.
- fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
-
- if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
- goto bail;
-
- /* success -- return valid version number */
- result = JNI_VERSION_1_4;
-
-bail:
- return result;
-}
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
new file mode 100644
index 0000000..2604850
--- /dev/null
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -0,0 +1,327 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool-JNI"
+
+#include <utils/Log.h>
+#include <nativehelper/jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <media/SoundPool.h>
+
+using namespace android;
+
+static struct fields_t {
+ jfieldID mNativeContext;
+ jmethodID mPostEvent;
+ jclass mSoundPoolClass;
+} fields;
+
+static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
+ return (SoundPool*)env->GetIntField(thiz, fields.mNativeContext);
+}
+
+// ----------------------------------------------------------------------------
+static int
+android_media_SoundPool_SoundPoolImpl_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_load_URL");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (path == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return 0;
+ }
+ const char* s = env->GetStringUTFChars(path, NULL);
+ int id = ap->load(s, priority);
+ env->ReleaseStringUTFChars(path, s);
+ return id;
+}
+
+static int
+android_media_SoundPool_SoundPoolImpl_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
+ jlong offset, jlong length, jint priority)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_load_FD");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return 0;
+ return ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
+ int64_t(offset), int64_t(length), int(priority));
+}
+
+static bool
+android_media_SoundPool_SoundPoolImpl_unload(JNIEnv *env, jobject thiz, jint sampleID) {
+ ALOGV("android_media_SoundPool_SoundPoolImpl_unload\n");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return 0;
+ return ap->unload(sampleID);
+}
+
+static int
+android_media_SoundPool_SoundPoolImpl_play(JNIEnv *env, jobject thiz, jint sampleID,
+ jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
+ jfloat rate)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_play\n");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return 0;
+ return ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_pause(JNIEnv *env, jobject thiz, jint channelID)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_pause");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->pause(channelID);
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_resume(JNIEnv *env, jobject thiz, jint channelID)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_resume");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->resume(channelID);
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_autoPause(JNIEnv *env, jobject thiz)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_autoPause");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->autoPause();
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_autoResume(JNIEnv *env, jobject thiz)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_autoResume");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->autoResume();
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_stop(JNIEnv *env, jobject thiz, jint channelID)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_stop");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->stop(channelID);
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_setVolume(JNIEnv *env, jobject thiz, jint channelID,
+ float leftVolume, float rightVolume)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_setVolume");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->setVolume(channelID, leftVolume, rightVolume);
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_setPriority(JNIEnv *env, jobject thiz, jint channelID,
+ int priority)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_setPriority");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->setPriority(channelID, priority);
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_setLoop(JNIEnv *env, jobject thiz, jint channelID,
+ int loop)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_setLoop");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->setLoop(channelID, loop);
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_setRate(JNIEnv *env, jobject thiz, jint channelID,
+ float rate)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_setRate");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->setRate(channelID, rate);
+}
+
+static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
+{
+ ALOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
+}
+
+static jint
+android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_native_setup");
+ SoundPool *ap = new SoundPool(maxChannels, (audio_stream_type_t) streamType, srcQuality);
+ if (ap == NULL) {
+ return -1;
+ }
+
+ // save pointer to SoundPool C++ object in opaque field in Java object
+ env->SetIntField(thiz, fields.mNativeContext, (int)ap);
+
+ // set callback with weak reference
+ jobject globalWeakRef = env->NewGlobalRef(weakRef);
+ ap->setCallback(android_media_callback, globalWeakRef);
+ return 0;
+}
+
+static void
+android_media_SoundPool_SoundPoolImpl_release(JNIEnv *env, jobject thiz)
+{
+ ALOGV("android_media_SoundPool_SoundPoolImpl_release");
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap != NULL) {
+
+ // release weak reference
+ jobject weakRef = (jobject) ap->getUserData();
+ if (weakRef != NULL) {
+ env->DeleteGlobalRef(weakRef);
+ }
+
+ // clear callback and native context
+ ap->setCallback(NULL, NULL);
+ env->SetIntField(thiz, fields.mNativeContext, 0);
+ delete ap;
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+// Dalvik VM type signatures
+static JNINativeMethod gMethods[] = {
+ { "_load",
+ "(Ljava/lang/String;I)I",
+ (void *)android_media_SoundPool_SoundPoolImpl_load_URL
+ },
+ { "_load",
+ "(Ljava/io/FileDescriptor;JJI)I",
+ (void *)android_media_SoundPool_SoundPoolImpl_load_FD
+ },
+ { "unload",
+ "(I)Z",
+ (void *)android_media_SoundPool_SoundPoolImpl_unload
+ },
+ { "play",
+ "(IFFIIF)I",
+ (void *)android_media_SoundPool_SoundPoolImpl_play
+ },
+ { "pause",
+ "(I)V",
+ (void *)android_media_SoundPool_SoundPoolImpl_pause
+ },
+ { "resume",
+ "(I)V",
+ (void *)android_media_SoundPool_SoundPoolImpl_resume
+ },
+ { "autoPause",
+ "()V",
+ (void *)android_media_SoundPool_SoundPoolImpl_autoPause
+ },
+ { "autoResume",
+ "()V",
+ (void *)android_media_SoundPool_SoundPoolImpl_autoResume
+ },
+ { "stop",
+ "(I)V",
+ (void *)android_media_SoundPool_SoundPoolImpl_stop
+ },
+ { "setVolume",
+ "(IFF)V",
+ (void *)android_media_SoundPool_SoundPoolImpl_setVolume
+ },
+ { "setPriority",
+ "(II)V",
+ (void *)android_media_SoundPool_SoundPoolImpl_setPriority
+ },
+ { "setLoop",
+ "(II)V",
+ (void *)android_media_SoundPool_SoundPoolImpl_setLoop
+ },
+ { "setRate",
+ "(IF)V",
+ (void *)android_media_SoundPool_SoundPoolImpl_setRate
+ },
+ { "native_setup",
+ "(Ljava/lang/Object;III)I",
+ (void*)android_media_SoundPool_SoundPoolImpl_native_setup
+ },
+ { "release",
+ "()V",
+ (void*)android_media_SoundPool_SoundPoolImpl_release
+ }
+};
+
+static const char* const kClassPathName = "android/media/SoundPool$SoundPoolImpl";
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ JNIEnv* env = NULL;
+ jint result = -1;
+ jclass clazz;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGE("ERROR: GetEnv failed\n");
+ goto bail;
+ }
+ assert(env != NULL);
+
+ clazz = env->FindClass(kClassPathName);
+ if (clazz == NULL) {
+ ALOGE("Can't find %s", kClassPathName);
+ goto bail;
+ }
+
+ fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (fields.mNativeContext == NULL) {
+ ALOGE("Can't find SoundPoolImpl.mNativeContext");
+ goto bail;
+ }
+
+ fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
+ "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ if (fields.mPostEvent == NULL) {
+ ALOGE("Can't find android/media/SoundPoolImpl.postEventFromNative");
+ goto bail;
+ }
+
+ // create a reference to class. Technically, we're leaking this reference
+ // since it's a static object.
+ fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
+
+ if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
+ goto bail;
+
+ /* success -- return valid version number */
+ result = JNI_VERSION_1_4;
+
+bail:
+ return result;
+}
diff --git a/media/libdrm/Android.mk b/media/libdrm/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/media/libdrm/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/media/libdrm/mobile1/Android.mk b/media/libdrm/mobile1/Android.mk
deleted file mode 100644
index 7356f46..0000000
--- a/media/libdrm/mobile1/Android.mk
+++ /dev/null
@@ -1,83 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# ---------------------------------------
-# First project
-#
-# Build DRM1 core library
-#
-# Output: libdrm1.so
-# ---------------------------------------
-include $(CLEAR_VARS)
-
-ifeq ($(TARGET_ARCH), arm)
-LOCAL_DRM_CFLAG = -DDRM_DEVICE_ARCH_ARM
-endif
-
-ifeq ($(TARGET_ARCH), x86)
-LOCAL_DRM_CFLAG = -DDRM_DEVICE_ARCH_X86
-endif
-
-# DRM 1.0 core source files
-LOCAL_SRC_FILES := \
- src/objmng/drm_decoder.c \
- src/objmng/drm_file.c \
- src/objmng/drm_i18n.c \
- src/objmng/drm_time.c \
- src/objmng/drm_api.c \
- src/objmng/drm_rights_manager.c \
- src/parser/parser_dcf.c \
- src/parser/parser_dm.c \
- src/parser/parser_rel.c \
- src/xml/xml_tinyparser.c
-
-# Header files path
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/include/objmng \
- $(LOCAL_PATH)/include/parser \
- $(LOCAL_PATH)/include/xml \
- external/openssl/include \
- $(call include-path-for, system-core)/cutils
-
-LOCAL_CFLAGS := $(LOCAL_DRM_CFLAG)
-
-LOCAL_SHARED_LIBRARIES := \
- libutils \
- libcutils \
- liblog \
- libcrypto
-
-LOCAL_MODULE := libdrm1
-
-include $(BUILD_SHARED_LIBRARY)
-
-# ---------------------------------------
-# Second project
-#
-# Build DRM1 Java Native Interface(JNI) library
-#
-# Output: libdrm1_jni.so
-# ------------------------------------------------
-include $(CLEAR_VARS)
-
-# Source files of DRM1 Java Native Interfaces
-LOCAL_SRC_FILES := \
- src/jni/drm1_jni.c
-
-# Header files path
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/include/parser \
- $(JNI_H_INCLUDE) \
- $(call include-path-for, system-core)/cutils
-
-
-LOCAL_SHARED_LIBRARIES := libdrm1 \
- libnativehelper \
- libutils \
- libcutils \
- liblog
-
-LOCAL_MODULE := libdrm1_jni
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libdrm/mobile1/include/drm_common_types.h b/media/libdrm/mobile1/include/drm_common_types.h
deleted file mode 100644
index c6bea61..0000000
--- a/media/libdrm/mobile1/include/drm_common_types.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __COMMON_TYPES_H__
-#define __COMMON_TYPES_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#define Trace(...)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __COMMON_TYPES_H__ */
diff --git a/media/libdrm/mobile1/include/jni/drm1_jni.h b/media/libdrm/mobile1/include/jni/drm1_jni.h
deleted file mode 100644
index 64e78ad..0000000
--- a/media/libdrm/mobile1/include/jni/drm1_jni.h
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __DRM1_JNI_H__
-#define __DRM1_JNI_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class android_drm_mobile1_DrmRawContent */
-
-#undef android_drm_mobile1_DrmRawContent_DRM_FORWARD_LOCK
-#define android_drm_mobile1_DrmRawContent_DRM_FORWARD_LOCK 1L
-#undef android_drm_mobile1_DrmRawContent_DRM_COMBINED_DELIVERY
-#define android_drm_mobile1_DrmRawContent_DRM_COMBINED_DELIVERY 2L
-#undef android_drm_mobile1_DrmRawContent_DRM_SEPARATE_DELIVERY
-#define android_drm_mobile1_DrmRawContent_DRM_SEPARATE_DELIVERY 3L
-#undef android_drm_mobile1_DrmRawContent_DRM_SEPARATE_DELIVERY_DM
-#define android_drm_mobile1_DrmRawContent_DRM_SEPARATE_DELIVERY_DM 4L
-#undef android_drm_mobile1_DrmRawContent_DRM_MIMETYPE_MESSAGE
-#define android_drm_mobile1_DrmRawContent_DRM_MIMETYPE_MESSAGE 1L
-#undef android_drm_mobile1_DrmRawContent_DRM_MIMETYPE_CONTENT
-#define android_drm_mobile1_DrmRawContent_DRM_MIMETYPE_CONTENT 2L
-#undef android_drm_mobile1_DrmRawContent_JNI_DRM_SUCCESS
-#define android_drm_mobile1_DrmRawContent_JNI_DRM_SUCCESS 0L
-#undef android_drm_mobile1_DrmRawContent_JNI_DRM_FAILURE
-#define android_drm_mobile1_DrmRawContent_JNI_DRM_FAILURE -1L
-#undef android_drm_mobile1_DrmRawContent_JNI_DRM_EOF
-#define android_drm_mobile1_DrmRawContent_JNI_DRM_EOF -2L
-#undef android_drm_mobile1_DrmRawContent_JNI_DRM_UNKNOWN_DATA_LEN
-#define android_drm_mobile1_DrmRawContent_JNI_DRM_UNKNOWN_DATA_LEN -3L
-/*
- * Class: android_drm_mobile1_DrmRawContent
- * Method: nativeConstructDrmContent
- * Signature: (Ljava/io/InputStream;II)I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRawContent_nativeConstructDrmContent
- (JNIEnv *, jobject, jobject, jint, jint);
-
-/*
- * Class: android_drm_mobile1_DrmRawContent
- * Method: nativeGetRightsAddress
- * Signature: ()Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_android_drm_mobile1_DrmRawContent_nativeGetRightsAddress
- (JNIEnv *, jobject);
-
-/*
- * Class: android_drm_mobile1_DrmRawContent
- * Method: nativeGetDeliveryMethod
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRawContent_nativeGetDeliveryMethod
- (JNIEnv *, jobject);
-
-/*
- * Class: android_drm_mobile1_DrmRawContent
- * Method: nativeReadPieceOfContent
- * Signature: ([BIII)I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRawContent_nativeReadContent
- (JNIEnv *, jobject, jbyteArray, jint, jint, jint);
-
-/*
- * Class: android_drm_mobile1_DrmRawContent
- * Method: nativeGetContentType
- * Signature: ()Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_android_drm_mobile1_DrmRawContent_nativeGetContentType
- (JNIEnv *, jobject);
-
-/*
- * Class: android_drm_mobile1_DrmRawContent
- * Method: nativeGetContentLength
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRawContent_nativeGetContentLength
- (JNIEnv *, jobject);
-
-/*
- * Class: android_drm_mobile1_DrmRawContent
- * Method: finalize
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_android_drm_mobile1_DrmRawContent_finalize
- (JNIEnv *, jobject);
-
-/* Header for class android_drm_mobile1_DrmRights */
-
-#undef android_drm_mobile1_DrmRights_DRM_PERMISSION_PLAY
-#define android_drm_mobile1_DrmRights_DRM_PERMISSION_PLAY 1L
-#undef android_drm_mobile1_DrmRights_DRM_PERMISSION_DISPLAY
-#define android_drm_mobile1_DrmRights_DRM_PERMISSION_DISPLAY 2L
-#undef android_drm_mobile1_DrmRights_DRM_PERMISSION_EXECUTE
-#define android_drm_mobile1_DrmRights_DRM_PERMISSION_EXECUTE 3L
-#undef android_drm_mobile1_DrmRights_DRM_PERMISSION_PRINT
-#define android_drm_mobile1_DrmRights_DRM_PERMISSION_PRINT 4L
-#undef android_drm_mobile1_DrmRights_DRM_CONSUME_RIGHTS_SUCCESS
-#define android_drm_mobile1_DrmRights_DRM_CONSUME_RIGHTS_SUCCESS 0L
-#undef android_drm_mobile1_DrmRights_DRM_CONSUME_RIGHTS_FAILURE
-#define android_drm_mobile1_DrmRights_DRM_CONSUME_RIGHTS_FAILURE -1L
-#undef android_drm_mobile1_DrmRights_JNI_DRM_SUCCESS
-#define android_drm_mobile1_DrmRights_JNI_DRM_SUCCESS 0L
-#undef android_drm_mobile1_DrmRights_JNI_DRM_FAILURE
-#define android_drm_mobile1_DrmRights_JNI_DRM_FAILURE -1L
-/*
- * Class: android_drm_mobile1_DrmRights
- * Method: nativeGetConstraintInfo
- * Signature: (ILandroid/drm/mobile1/DrmConstraintInfo;)I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRights_nativeGetConstraintInfo
- (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class: android_drm_mobile1_DrmRights
- * Method: nativeConsumeRights
- * Signature: (I)I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRights_nativeConsumeRights
- (JNIEnv *, jobject, jint);
-
-/* Header for class android_drm_mobile1_DrmRightsManager */
-
-#undef android_drm_mobile1_DrmRightsManager_DRM_MIMETYPE_RIGHTS_XML
-#define android_drm_mobile1_DrmRightsManager_DRM_MIMETYPE_RIGHTS_XML 3L
-#undef android_drm_mobile1_DrmRightsManager_DRM_MIMETYPE_RIGHTS_WBXML
-#define android_drm_mobile1_DrmRightsManager_DRM_MIMETYPE_RIGHTS_WBXML 4L
-#undef android_drm_mobile1_DrmRightsManager_DRM_MIMETYPE_MESSAGE
-#define android_drm_mobile1_DrmRightsManager_DRM_MIMETYPE_MESSAGE 1L
-#undef android_drm_mobile1_DrmRightsManager_JNI_DRM_SUCCESS
-#define android_drm_mobile1_DrmRightsManager_JNI_DRM_SUCCESS 0L
-#undef android_drm_mobile1_DrmRightsManager_JNI_DRM_FAILURE
-#define android_drm_mobile1_DrmRightsManager_JNI_DRM_FAILURE -1L
-/* Inaccessible static: singleton */
-/*
- * Class: android_drm_mobile1_DrmRightsManager
- * Method: nativeInstallDrmRights
- * Signature: (Ljava/io/InputStream;IILandroid/drm/mobile1/DrmRights;)I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRightsManager_nativeInstallDrmRights
- (JNIEnv *, jobject, jobject, jint, jint, jobject);
-
-/*
- * Class: android_drm_mobile1_DrmRightsManager
- * Method: nativeQueryRights
- * Signature: (Landroid/drm/mobile1/DrmRawContent;Landroid/drm/mobile1/DrmRights;)I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRightsManager_nativeQueryRights
- (JNIEnv *, jobject, jobject, jobject);
-
-/*
- * Class: android_drm_mobile1_DrmRightsManager
- * Method: nativeGetRightsNumber
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRightsManager_nativeGetNumOfRights
- (JNIEnv *, jobject);
-
-/*
- * Class: android_drm_mobile1_DrmRightsManager
- * Method: nativeGetRightsList
- * Signature: ([Landroid/drm/mobile1/DrmRights;I)I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRightsManager_nativeGetRightsList
- (JNIEnv *, jobject, jobjectArray, jint);
-
-/*
- * Class: android_drm_mobile1_DrmRightsManager
- * Method: nativeDeleteRights
- * Signature: (Landroid/drm/mobile1/DrmRights;)I
- */
-JNIEXPORT jint JNICALL Java_android_drm_mobile1_DrmRightsManager_nativeDeleteRights
- (JNIEnv *, jobject, jobject);
-
-/**
- * DRM return value defines
- */
-#define JNI_DRM_SUCCESS \
- android_drm_mobile1_DrmRawContent_JNI_DRM_SUCCESS /**< Successful operation */
-#define JNI_DRM_FAILURE \
- android_drm_mobile1_DrmRawContent_JNI_DRM_FAILURE /**< General failure */
-#define JNI_DRM_EOF \
- android_drm_mobile1_DrmRawContent_JNI_DRM_EOF /**< Indicates the end of the DRM content is reached */
-#define JNI_DRM_UNKNOWN_DATA_LEN \
- android_drm_mobile1_DrmRawContent_JNI_DRM_UNKNOWN_DATA_LEN /**< Indicates the data length is unknown */
-
-/**
- * DRM MIME type defines
- */
-#define JNI_DRM_MIMETYPE_MESSAGE \
- android_drm_mobile1_DrmRawContent_DRM_MIMETYPE_MESSAGE /**< The "application/vnd.oma.drm.message" MIME type */
-#define JNI_DRM_MIMETYPE_CONTENT \
- android_drm_mobile1_DrmRawContent_DRM_MIMETYPE_CONTENT /**< The "application/vnd.oma.drm.content" MIME type */
-#define JNI_DRM_MIMETYPE_RIGHTS_XML \
- android_drm_mobile1_DrmRightsManager_DRM_MIMETYPE_RIGHTS_XML /**< The "application/vnd.oma.drm.rights+xml" MIME type */
-#define JNI_DRM_MIMETYPE_RIGHTS_WBXML \
- android_drm_mobile1_DrmRightsManager_DRM_MIMETYPE_RIGHTS_WBXML /**< The "application/vnd.oma.drm.rights+wbxml" MIME type */
-
-/**
- * DRM permission defines
- */
-#define JNI_DRM_PERMISSION_PLAY \
- android_drm_mobile1_DrmRights_DRM_PERMISSION_PLAY /**< The permission to play */
-#define JNI_DRM_PERMISSION_DISPLAY \
- android_drm_mobile1_DrmRights_DRM_PERMISSION_DISPLAY /**< The permission to display */
-#define JNI_DRM_PERMISSION_EXECUTE \
- android_drm_mobile1_DrmRights_DRM_PERMISSION_EXECUTE /**< The permission to execute */
-#define JNI_DRM_PERMISSION_PRINT \
- android_drm_mobile1_DrmRights_DRM_PERMISSION_PRINT /**< The permission to print */
-
-/**
- * DRM delivery type defines
- */
-#define JNI_DRM_FORWARD_LOCK \
- android_drm_mobile1_DrmRawContent_DRM_FORWARD_LOCK /**< forward lock */
-#define JNI_DRM_COMBINED_DELIVERY \
- android_drm_mobile1_DrmRawContent_DRM_COMBINED_DELIVERY /**< combined delivery */
-#define JNI_DRM_SEPARATE_DELIVERY \
- android_drm_mobile1_DrmRawContent_DRM_SEPARATE_DELIVERY /**< separate delivery */
-#define JNI_DRM_SEPARATE_DELIVERY_DM \
- android_drm_mobile1_DrmRawContent_DRM_SEPARATE_DELIVERY_DM /**< separate delivery DRM message */
-#ifdef __cplusplus
-}
-#endif
-#endif /* __DRM1_JNI_H__ */
-
diff --git a/media/libdrm/mobile1/include/objmng/drm_decoder.h b/media/libdrm/mobile1/include/objmng/drm_decoder.h
deleted file mode 100644
index a769c81..0000000
--- a/media/libdrm/mobile1/include/objmng/drm_decoder.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * @file drm_decoder.h
- *
- * provide service to decode base64 data.
- *
- * <!-- #interface list begin -->
- * \section drm decoder interface
- * - drm_decodeBase64()
- * <!-- #interface list end -->
- */
-
-#ifndef __DRM_DECODER_H__
-#define __DRM_DECODER_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-/**
- * Decode base64
- * \param dest dest buffer to save decode base64 data
- * \param destLen dest buffer length
- * \param src source data to be decoded
- * \param srcLen source buffer length, and when return, give out how many bytes has been decoded
- * \return
- * -when success, return a positive integer of dest buffer length,
- * if input dest buffer is NULL or destLen is 0,
- * return dest buffer length that user should allocate to save decoding data
- * -when failed, return -1
- */
-int32_t drm_decodeBase64(uint8_t * dest, int32_t destLen, uint8_t * src, int32_t * srcLen);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __DRM_DECODER_H__ */
diff --git a/media/libdrm/mobile1/include/objmng/drm_file.h b/media/libdrm/mobile1/include/objmng/drm_file.h
deleted file mode 100644
index b94ddd0..0000000
--- a/media/libdrm/mobile1/include/objmng/drm_file.h
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * 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.
- */
-
-
-/**
- * File Porting Layer.
- */
-#ifndef __DRM_FILE_H__
-#define __DRM_FILE_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-/** Type value of a regular file or file name. */
-#define DRM_FILE_ISREG 1
-/** Type value of a directory or directory name. */
-#define DRM_FILE_ISDIR 2
-/** Type value of a filter name */
-#define DRM_FILE_ISFILTER 3
-
-
-/** Return code that indicates successful completion of an operation. */
-#define DRM_FILE_SUCCESS 0
-/** Indicates that an operation failed. */
-#define DRM_FILE_FAILURE -1
-/** Indicates that the a DRM_file_read() call reached the end of the file. */
-#define DRM_FILE_EOF -2
-
-
-/** Open for read access. */
-#define DRM_FILE_MODE_READ 1
-/** Open for write access. */
-#define DRM_FILE_MODE_WRITE 2
-
-
-#ifndef MAX_FILENAME_LEN
-/** Maximum number of characters that a filename may have. By default assumes
- * that the entry results of DRM_file_listNextEntry() are returned in the async state
- * buffer, after the #DRM_file_result_s, and calculates the maximum name
- * from that.
- */
-#define MAX_FILENAME_LEN 1024
-#endif
-
-
-/**
- * Performs one-time initialization of the File System (FS).
- * This function is called once during the lifetime of an application,
- * and before any call to <code>DRM_file_*</code> functions by this application.
- * When several applications are using the file interface, this function may be called
- * several times, once per application.
- *
- * @return #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE.
- */
-int32_t DRM_file_startup(void);
-
-/**
- * Returns the length of a file (by name, opened or unopened).
- *
- * @param name Name of the file, UCS-2 encoded.
- * @param nameChars Number characters encoded in name.
- * asynchronous operation returns #DRM_FILE_WOULDBLOCK.
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_FAILURE or the file length.
- */
-int32_t DRM_file_getFileLength(const uint16_t* name,
- int32_t nameChars);
-
-/**
- * Initializes a list iteration session.
- *
- * @param prefix Prefix that must be matched, UCS-2 encoded. *
- * @param prefixChars Number characters encoded in prefix.
- * @param session List session identifier.
- * @param iteration List iteration identifier.
- *
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE.
- */
-int32_t DRM_file_listOpen(const uint16_t* prefix,
- int32_t prefixChars,
- int32_t* session,
- int32_t* iteration);
-
-/**
- * Used to fetch a list of file names that match a given name prefix.
- *
- * @param prefix See DRM_file_listOpen(). This does not change during the
- * iteration session.
- * @param prefixChars See DRM_file_listOpen(). This does not change during
- * the iteration session.
- * @param entry Buffer parameter to return the next file name that matches the
- * #prefix parameter, if any, when the function returns a positive number of
- * characters.
- * @param entryBytes Size of entry in bytes.
- * @param session See DRM_file_listOpen().
- * @param iteration See DRM_file_listOpen().
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_FAILURE or the number of
- * characters encoded in entry. Returns 0 when the end of the list is reached.
- */
-int32_t DRM_file_listNextEntry(const uint16_t* prefix,
- int32_t prefixChars,
- uint16_t* entry,
- int32_t entryBytes,
- int32_t* session,
- int32_t* iteration);
-
-/**
- * Ends a list iteration session. Notifies the implementation
- * that the list session is over and that any session resources
- * can be released.
- *
- * @param session See DRM_file_listOpen().
- * @param iteration See DRM_file_listOpen().
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE.
- */
-int32_t DRM_file_listClose(int32_t session, int32_t iteration);
-
-/**
- * Renames a file, given its old name. The file or directory is renamed
- * immediately on the actual file system upon invocation of this method.
- * Any open handles on the file specified by oldName become invalid after
- * this method has been called.
- *
- * @param oldName Current file name (unopened), UCS-2 encoded.
- * @param oldNameChars Number of characters encoded on oldName.
- * @param newName New name for the file (unopened), UCS-2 encoded.
- * @param newNameChars Number of characters encoded on newName.
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE. In particular,
- * #DRM_FILE_FAILURE if a file or directory already exists with the new name.
- */
-int32_t DRM_file_rename(const uint16_t* oldName,
- int32_t oldNameChars,
- const uint16_t* newName,
- int32_t newNameChars);
-
-/**
- * Tests if a file exists given its name.
- *
- * @param name Name of the file, UCS-2 encoded.
- * @param nameChars Number of characters encoded in name.
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_ISREG, #DRM_FILE_ISDIR, #DRM_FILE_FAILURE. If name
- * exists, returns #DRM_FILE_ISREG if it is a regular file and #DRM_FILE_ISDIR if it is a directory.
- * Returns #DRM_FILE_FAILURE in all other cases, including those where name exists but is neither
- * a regular file nor a directory. Platforms that do not support directories MUST NOT return
- * #DRM_FILE_ISDIR.
- */
-int32_t DRM_file_exists(const uint16_t* name,
- int32_t nameChars);
-
-/**
- * Opens a file with the given name and returns its file handle.
- *
- * @param name Name of the file, UCS-2 encoded.
- * @param nameChars Number of characters encoded in name.
- * @param mode Any combination of the #DRM_FILE_MODE_READ and
- * #DRM_FILE_MODE_WRITE flags. If the file does not exist and mode contains the
- * #DRM_FILE_MODE_WRITE flag, then the file is automatically created. If the
- * file exists and the mode contains the #DRM_FILE_MODE_WRITE flag, the file is
- * opened so it can be modified, but the data is not modified by the open call.
- * In all cases the current position is set to the start of the file.
- * The following table shows how to map the mode semantics above to UNIX
- * fopen-style modes. For brevity in the table, R=#DRM_FILE_MODE_READ,
- * W=#DRM_FILE_MODE_WRITE, E=File exists:
- * <table>
- * <tr><td>RW</td><td>E</td><td>Maps-to</td></tr>
- * <tr><td>00</td><td>0</td><td>Return #DRM_FILE_FAILURE</td></tr>
- * <tr><td>00</td><td>1</td><td>Return #DRM_FILE_FAILURE</td></tr>
- * <tr><td>01</td><td>0</td><td>Use fopen mode "w"</td></tr>
- * <tr><td>01</td><td>1</td><td>Use fopen mode "a" and fseek to the start</td></tr>
- * <tr><td>10</td><td>0</td><td>Return #DRM_FILE_FAILURE</td></tr>
- * <tr><td>10</td><td>1</td><td>Use fopen mode "r"</td></tr>
- * <tr><td>11</td><td>0</td><td>Use fopen mode "w+"</td></tr>
- * <tr><td>11</td><td>1</td><td>Use fopen mode "r+"</td></tr>
- * </table>
- * @param handle Pointer where the result handle value is placed when the function
- * is called synchronously.
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE.
- */
-int32_t DRM_file_open(const uint16_t* name,
- int32_t nameChars,
- int32_t mode,
- int32_t* handle);
-
-/**
- * Deletes a file given its name, UCS-2 encoded. The file or directory is
- * deleted immediately on the actual file system upon invocation of this
- * method. Any open handles on the file specified by name become invalid
- * after this method has been called.
- *
- * If the port needs to ensure that a specific application does not exceed a given storage
- * space quota, then the bytes freed by the deletion must be added to the available space for
- * that application.
- *
- * @param name Name of the file, UCS-2 encoded.
- * @param nameChars Number of characters encoded in name.
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE.
- */
-int32_t DRM_file_delete(const uint16_t* name,
- int32_t nameChars);
-
-/**
- * Read bytes from a file at the current position to a buffer. Afterwards the
- * new file position is the byte after the last byte read.
- * DRM_FILE_FAILURE is returned if the handle is invalid (e.g., as a
- * consquence of DRM_file_delete, DRM_file_rename, or DRM_file_close).
- *
- * @param handle File handle as returned by DRM_file_open().
- * @param dst Buffer where the data is to be copied.
- * @param length Number of bytes to be copied.
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE, #DRM_FILE_EOF
- * or the number of bytes that were read, i.e. in the range 0..length.
- */
-int32_t DRM_file_read(int32_t handle,
- uint8_t* dst,
- int32_t length);
-
-/**
- * Write bytes from a buffer to the file at the current position. If the
- * current position + number of bytes written > current size of the file,
- * then the file is grown. Afterwards the new file position is the byte
- * after the last byte written.
- * DRM_FILE_FAILURE is returned if the handle is invalid (e.g., as a
- * consquence of DRM_file_delete, DRM_file_rename, or DRM_file_close).
- *
- * @param handle File handle as returned by DRM_file_open().
- * @param src Buffer that contains the bytes to be written.
- * @param length Number of bytes to be written.
- * If the port needs to ensure that a specific application does not exceed a given storage
- * space quota, the implementation must make sure the call does not violate that invariant.
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_FAILURE or the number of bytes
- * that were written. This number must be in the range 0..length.
- * Returns #DRM_FILE_FAILURE when storage is full or exceeds quota.
- */
-int32_t DRM_file_write(int32_t handle,
- const uint8_t* src,
- int32_t length);
-
-/**
- * Closes a file.
- * DRM_FILE_SUCCESS is returned if the handle is invalid (e.g., as a
- * consquence of DRM_file_delete or DRM_file_rename).
- *
- * @param handle File handle as returned by DRM_file_open().
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE.
- */
-int32_t DRM_file_close(int32_t handle);
-
-/**
- * Sets the current position in an opened file.
- * DRM_FILE_FAILURE is returned if the handle is invalid (e.g., as a
- * consquence of DRM_file_delete, DRM_file_rename, or DRM_file_close).
- *
- * @param handle File handle as returned by DRM_file_open().
- * @param value The new current position of the file. If value is greater
- * than the length of the file then the file should be extended. The contents
- * of the newly extended portion of the file is undefined.
- * If the port needs to ensure that a specific application does not exceed a given storage
- * space quota, the implementation must make sure the call does not violate that invariant.
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE.
- * Returns #DRM_FILE_FAILURE when storage is full or exceeds quota.
- */
-int32_t DRM_file_setPosition(int32_t handle, int32_t value);
-
-/**
- * Creates a directory with the assigned name and full file permissions on
- * the file system. The full path to the new directory must already exist.
- * The directory is created immediately on the actual file system upon
- * invocation of this method.
- *
- * @param name Name of the directory, UCS-2 encoded.
- * @param nameChars Number of characters encoded in name.
- * @return #DRM_FILE_WOULDBLOCK, #DRM_FILE_SUCCESS, #DRM_FILE_FAILURE.
- */
-int32_t DRM_file_mkdir(const uint16_t* name,
- int32_t nameChars);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __DRM_FILE_H__ */
diff --git a/media/libdrm/mobile1/include/objmng/drm_i18n.h b/media/libdrm/mobile1/include/objmng/drm_i18n.h
deleted file mode 100644
index 7487e9b..0000000
--- a/media/libdrm/mobile1/include/objmng/drm_i18n.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.
- */
-
-
-#ifndef __DRM_I18N_H__
-#define __DRM_I18N_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-/**
- * @name Charset value defines
- * @ingroup i18n
- *
- * Charset value defines
- * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/intl/unicode_81rn.asp
- */
-typedef enum {
- DRM_CHARSET_GBK = 936, /** Simplified Chinese GBK (CP936) */
- DRM_CHARSET_GB2312 = 20936, /** Simplified Chinese GB2312 (CP936) */
- DRM_CHARSET_BIG5 = 950, /** BIG5 (CP950) */
- DRM_CHARSET_LATIN1 = 28591, /** ISO 8859-1, Latin 1 */
- DRM_CHARSET_LATIN2 = 28592, /** ISO 8859-2, Latin 2 */
- DRM_CHARSET_LATIN3 = 28593, /** ISO 8859-3, Latin 3 */
- DRM_CHARSET_LATIN4 = 28594, /** ISO 8859-4, Latin 4 */
- DRM_CHARSET_CYRILLIC = 28595, /** ISO 8859-5, Cyrillic */
- DRM_CHARSET_ARABIC = 28596, /** ISO 8859-6, Arabic */
- DRM_CHARSET_GREEK = 28597, /** ISO 8859-7, Greek */
- DRM_CHARSET_HEBREW = 28598, /** ISO 8859-8, Hebrew */
- DRM_CHARSET_LATIN5 = 28599, /** ISO 8859-9, Latin 5 */
- DRM_CHARSET_LATIN6 = 865, /** ISO 8859-10, Latin 6 (not sure here) */
- DRM_CHARSET_THAI = 874, /** ISO 8859-11, Thai */
- DRM_CHARSET_LATIN7 = 1257, /** ISO 8859-13, Latin 7 (not sure here) */
- DRM_CHARSET_LATIN8 = 38598, /** ISO 8859-14, Latin 8 (not sure here) */
- DRM_CHARSET_LATIN9 = 28605, /** ISO 8859-15, Latin 9 */
- DRM_CHARSET_LATIN10 = 28606, /** ISO 8859-16, Latin 10 */
- DRM_CHARSET_UTF8 = 65001, /** UTF-8 */
- DRM_CHARSET_UTF16LE = 1200, /** UTF-16 LE */
- DRM_CHARSET_UTF16BE = 1201, /** UTF-16 BE */
- DRM_CHARSET_HINDI = 57002, /** Hindi/Mac Devanagari */
- DRM_CHARSET_UNSUPPORTED = -1
-} DRM_Charset_t;
-
-/**
- * Convert multibyte string of specified charset to unicode string.
- * Note NO terminating '\0' will be appended to the output unicode string.
- *
- * @param charset Charset of the multibyte string.
- * @param mbs Multibyte string to be converted.
- * @param mbsLen Number of the bytes (in mbs) to be converted.
- * @param wcsBuf Buffer for the converted unicode characters.
- * If wcsBuf is NULL, the function returns the number of unicode
- * characters required for the buffer.
- * @param bufSizeInWideChar The size (in wide char) of wcsBuf
- * @param bytesConsumed The number of bytes in mbs that have been successfully
- * converted. The value of *bytesConsumed is undefined
- * if wcsBuf is NULL.
- *
- * @return Number of the successfully converted unicode characters if wcsBuf
- * is not NULL. If wcsBuf is NULL, returns required unicode buffer
- * size. -1 for unrecoverable errors.
- */
-int32_t DRM_i18n_mbsToWcs(DRM_Charset_t charset,
- const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed);
-
-/**
- * Convert unicode string to multibyte string with specified charset.
- * Note NO terminating '\0' will be appended to the output multibyte string.
- *
- * @param charset Charset of the multibyte string to be converted to.
- * @param wcs Unicode string to be converted.
- * @param wcsLen Number of the unicode characters (in wcs) to be converted.
- * @param mbsBuf Buffer for converted multibyte characters.
- * If mbsBuf is NULL, the function returns the number of bytes
- * required for the buffer.
- * @param bufSizeInByte The size (in byte) of mbsBuf.
- *
- * @return Number of the successfully converted bytes.
- */
-int32_t DRM_i18n_wcsToMbs(DRM_Charset_t charset,
- const uint16_t *wcs, int32_t wcsLen,
- uint8_t *mbsBuf, int32_t bufSizeInByte);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
diff --git a/media/libdrm/mobile1/include/objmng/drm_inner.h b/media/libdrm/mobile1/include/objmng/drm_inner.h
deleted file mode 100644
index 55234f8..0000000
--- a/media/libdrm/mobile1/include/objmng/drm_inner.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __DRM_INNER_H__
-#define __DRM_INNER_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-#define INT_2_YMD_HMS(year, mon, day, date, hour, min, sec, time) do{\
- year = date / 10000;\
- mon = date % 10000 / 100;\
- day = date %100;\
- hour = time / 10000;\
- min = time % 10000 / 100;\
- sec = time % 100;\
-}while(0)
-
-/**
- * Define the max malloc length for a DRM.
- */
-#define DRM_MAX_MALLOC_LEN (50 * 1024) /* 50K */
-
-#define DRM_ONE_AES_BLOCK_LEN 16
-#define DRM_TWO_AES_BLOCK_LEN 32
-
-typedef struct _T_DRM_DM_Binary_Node {
- uint8_t boundary[256];
-} T_DRM_DM_Binary_Node;
-
-typedef struct _T_DRM_DM_Base64_Node {
- uint8_t boundary[256];
- uint8_t b64DecodeData[4];
- int32_t b64DecodeDataLen;
-} T_DRM_DM_Base64_Node;
-
-typedef struct _T_DRM_Dcf_Node {
- uint8_t rightsIssuer[256];
- int32_t encContentLength;
- uint8_t aesDecData[16];
- int32_t aesDecDataLen;
- int32_t aesDecDataOff;
- uint8_t aesBackupBuf[16];
- int32_t bAesBackupBuf;
-} T_DRM_Dcf_Node;
-
-typedef struct _T_DRM_Session_Node {
- int32_t sessionId;
- int32_t inputHandle;
- int32_t mimeType;
- int32_t (*getInputDataLengthFunc)(int32_t inputHandle);
- int32_t (*readInputDataFunc)(int32_t inputHandle, uint8_t* buf, int32_t bufLen);
- int32_t (*seekInputDataFunc)(int32_t inputHandle, int32_t offset);
- int32_t deliveryMethod;
- int32_t transferEncoding;
- uint8_t contentType[64];
- int32_t contentLength;
- int32_t contentOffset;
- uint8_t contentID[256];
- uint8_t* rawContent;
- int32_t rawContentLen;
- int32_t bEndData;
- uint8_t* readBuf;
- int32_t readBufLen;
- int32_t readBufOff;
- void* infoStruct;
- struct _T_DRM_Session_Node* next;
-} T_DRM_Session_Node;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __DRM_INNER_H__ */
diff --git a/media/libdrm/mobile1/include/objmng/drm_rights_manager.h b/media/libdrm/mobile1/include/objmng/drm_rights_manager.h
deleted file mode 100644
index d81e7a1..0000000
--- a/media/libdrm/mobile1/include/objmng/drm_rights_manager.h
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __DRM_RIGHTS_MANAGER_H__
-#define __DRM_RIGHTS_MANAGER_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <openssl/aes.h>
-#include <drm_common_types.h>
-#include <parser_rel.h>
-
-#ifdef DRM_DEVICE_ARCH_ARM
-#define ANDROID_DRM_CORE_PATH "/data/drm/rights/"
-#define DRM_UID_FILE_PATH "/data/drm/rights/uid.txt"
-#else
-#define ANDROID_DRM_CORE_PATH "/home/user/golf/esmertec/device/out/debug/host/linux-x86/product/sim/data/data/com.android.drm.mobile1/"
-#define DRM_UID_FILE_PATH "/home/user/golf/esmertec/device/out/debug/host/linux-x86/product/sim/data/data/com.android.drm.mobile1/uid.txt"
-#endif
-
-#define EXTENSION_NAME_INFO ".info"
-
-#define GET_ID 1
-#define GET_UID 2
-
-#define GET_ROAMOUNT 1
-#define GET_ALL_RO 2
-#define SAVE_ALL_RO 3
-#define GET_A_RO 4
-#define SAVE_A_RO 5
-
-/**
- * Get the id or uid from the "uid.txt" file.
- *
- * \param Uid The content id for a specially DRM object.
- * \param id The id number managed by DRM engine for a specially DRM object.
- * \param option The option to get id or uid, the value includes: GET_ID, GET_UID.
- *
- * \return
- * -TRUE, if the operation successfully.
- * -FALSE, if the operation failed.
- */
-int32_t drm_readFromUidTxt(uint8_t* Uid, int32_t* id, int32_t option);
-
-/**
- * Save or read the rights information on the "id.info" file.
- *
- * \param id The id number managed by DRM engine for a specially DRM object.
- * \param Ro The rights structure to save the rights information.
- * \param RoAmount The number of rights for this DRM object.
- * \param option The option include: GET_ROAMOUNT, GET_ALL_RO, SAVE_ALL_RO, GET_A_RO, SAVE_A_RO.
- *
- * \return
- * -TRUE, if the operation successfully.
- * -FALSE, if the operation failed.
- */
-int32_t drm_writeOrReadInfo(int32_t id, T_DRM_Rights* Ro, int32_t* RoAmount, int32_t option);
-
-/**
- * Append a rights information to DRM engine storage.
- *
- * \param Ro The rights structure to save the rights information.
- *
- * return
- * -TRUE, if the operation successfully.
- * -FALSE, if the operation failed.
- */
-int32_t drm_appendRightsInfo(T_DRM_Rights* rights);
-
-/**
- * Get the mex id number from the "uid.txt" file.
- *
- * \return
- * -an integer to indicate the max id number.
- * -(-1), if the operation failed.
- */
-int32_t drm_getMaxIdFromUidTxt();
-
-/**
- * Remove the "id.info" file if all the rights for this DRM object has been deleted.
- *
- * \param id The id number managed by DRM engine for a specially DRM object.
- *
- * \return
- * -TRUE, if the operation successfully.
- * -FALSE, if the operation failed.
- */
-int32_t drm_removeIdInfoFile(int32_t id);
-
-/**
- * Update the "uid.txt" file when delete the rights object.
- *
- * \param id The id number managed by DRM engine for a specially DRM object.
- *
- * \return
- * -TRUE, if the operation successfully.
- * -FALSE, if the operation failed.
- */
-int32_t drm_updateUidTxtWhenDelete(int32_t id);
-
-/**
- * Get the CEK according the given content id.
- *
- * \param uid The content id for a specially DRM object.
- * \param KeyValue The buffer to save the CEK.
- *
- * \return
- * -TRUE, if the operation successfully.
- * -FALSE, if the operation failed.
- */
-int32_t drm_getKey(uint8_t* uid, uint8_t* KeyValue);
-
-/**
- * Discard the padding bytes in DCF decrypted data.
- *
- * \param decryptedBuf The aes decrypted data buffer to be scanned.
- * \param decryptedBufLen The length of the buffer. And save the output result.
- *
- * \return
- * -0
- */
-void drm_discardPaddingByte(uint8_t *decryptedBuf, int32_t *decryptedBufLen);
-
-/**
- * Decrypt the media data according the CEK.
- *
- * \param Buffer The buffer to decrypted and also used to save the output data.
- * \param BufferLen The length of the buffer data and also save the output data length.
- * \param key The structure of the CEK.
- *
- * \return
- * -0
- */
-int32_t drm_aesDecBuffer(uint8_t * Buffer, int32_t * BufferLen, AES_KEY *key);
-
-/**
- * Update the DCF data length according the CEK.
- *
- * \param pDcfLastData The last several byte for the DCF.
- * \param keyValue The CEK of the DRM content.
- * \param moreBytes Output the more bytes for discarded.
- *
- * \return
- * -TRUE, if the operation successfully.
- * -FALSE, if the operation failed.
- */
-int32_t drm_updateDcfDataLen(uint8_t* pDcfLastData, uint8_t* keyValue, int32_t* moreBytes);
-
-/**
- * Check and update the rights for a specially DRM content.
- *
- * \param id The id number managed by DRM engine for a specially DRM object.
- * \param permission The permission to be check and updated.
- *
- * \return
- * -DRM_SUCCESS, if there is a valid rights and update it successfully.
- * -DRM_NO_RIGHTS, if there is no rights for this content.
- * -DRM_RIGHTS_PENDING, if the rights is pending.
- * -DRM_RIGHTS_EXPIRED, if the rights has expired.
- * -DRM_RIGHTS_FAILURE, if there is some other error occur.
- */
-int32_t drm_checkRoAndUpdate(int32_t id, int32_t permission);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __DRM_RIGHTS_MANAGER_H__ */
diff --git a/media/libdrm/mobile1/include/objmng/drm_time.h b/media/libdrm/mobile1/include/objmng/drm_time.h
deleted file mode 100644
index 9b013e6..0000000
--- a/media/libdrm/mobile1/include/objmng/drm_time.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.
- */
-
-
-/**
- * @file
- * Time Porting Layer
- *
- * Basic support functions that are needed by time.
- *
- * <!-- #interface list begin -->
- * \section drm_time Interface
- * - DRM_time_getElapsedSecondsFrom1970()
- * - DRM_time_sleep()
- * - DRM_time_getSysTime()
- * <!-- #interface list end -->
- */
-
-#ifndef __DRM_TIME_H__
-#define __DRM_TIME_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <time.h>
-#include <drm_common_types.h>
-
-/** the time format */
-typedef struct __db_system_time_
-{
- uint16_t year;
- uint16_t month;
- uint16_t day;
- uint16_t hour;
- uint16_t min;
- uint16_t sec;
-} T_DB_TIME_SysTime;
-
-/**
- * Get the system time.it's up to UTC
- * \return Return the time in elapsed seconds.
- */
-uint32_t DRM_time_getElapsedSecondsFrom1970(void);
-
-/**
- * Suspend the execution of the current thread for a specified interval
- * \param ms suspended time by millisecond
- */
-void DRM_time_sleep(uint32_t ms);
-
-/**
- * function: get current system time
- * \param time_ptr[OUT] the system time got
- * \attention
- * time_ptr must not be NULL
- */
-void DRM_time_getSysTime(T_DB_TIME_SysTime *time_ptr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __DRM_TIME_H__ */
diff --git a/media/libdrm/mobile1/include/objmng/svc_drm.h b/media/libdrm/mobile1/include/objmng/svc_drm.h
deleted file mode 100644
index 789343f..0000000
--- a/media/libdrm/mobile1/include/objmng/svc_drm.h
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __SVC_DRM_NEW_H__
-#define __SVC_DRM_NEW_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-/**
- * Define the mime type of DRM data.
- */
-#define TYPE_DRM_MESSAGE 0x48 /**< The mime type is "application/vnd.oma.drm.message" */
-#define TYPE_DRM_CONTENT 0x49 /**< The mime type is "application/vnd.oma.drm.content" */
-#define TYPE_DRM_RIGHTS_XML 0x4a /**< The mime type is "application/vnd.oma.drm.rights+xml" */
-#define TYPE_DRM_RIGHTS_WBXML 0x4b /**< The mime type is "application/vnd.oma.drm.rights+wbxml" */
-#define TYPE_DRM_UNKNOWN 0xff /**< The mime type is unknown */
-
-/**
- * Define the delivery methods.
- */
-#define FORWARD_LOCK 1 /**< Forward_lock */
-#define COMBINED_DELIVERY 2 /**< Combined delivery */
-#define SEPARATE_DELIVERY 3 /**< Separate delivery */
-#define SEPARATE_DELIVERY_FL 4 /**< Separate delivery but DCF is forward-lock */
-
-/**
- * Define the permissions.
- */
-#define DRM_PERMISSION_PLAY 0x01 /**< Play */
-#define DRM_PERMISSION_DISPLAY 0x02 /**< Display */
-#define DRM_PERMISSION_EXECUTE 0x04 /**< Execute */
-#define DRM_PERMISSION_PRINT 0x08 /**< Print */
-#define DRM_PERMISSION_FORWARD 0x10 /**< Forward */
-
-/**
- * Define the constraints.
- */
-#define DRM_NO_CONSTRAINT 0x80 /**< Indicate have no constraint, it can use freely */
-#define DRM_END_TIME_CONSTRAINT 0x08 /**< Indicate have end time constraint */
-#define DRM_INTERVAL_CONSTRAINT 0x04 /**< Indicate have interval constraint */
-#define DRM_COUNT_CONSTRAINT 0x02 /**< Indicate have count constraint */
-#define DRM_START_TIME_CONSTRAINT 0x01 /**< Indicate have start time constraint */
-#define DRM_NO_PERMISSION 0x00 /**< Indicate no rights */
-
-/**
- * Define the return values for those interface.
- */
-#define DRM_SUCCESS 0
-#define DRM_FAILURE -1
-#define DRM_MEDIA_EOF -2
-#define DRM_RIGHTS_DATA_INVALID -3
-#define DRM_MEDIA_DATA_INVALID -4
-#define DRM_SESSION_NOT_OPENED -5
-#define DRM_NO_RIGHTS -6
-#define DRM_NOT_SD_METHOD -7
-#define DRM_RIGHTS_PENDING -8
-#define DRM_RIGHTS_EXPIRED -9
-#define DRM_UNKNOWN_DATA_LEN -10
-
-/**
- * The input DRM data structure, include DM, DCF, DR, DRC.
- */
-typedef struct _T_DRM_Input_Data {
- /**
- * The handle of the input DRM data.
- */
- int32_t inputHandle;
-
- /**
- * The mime type of the DRM data, if the mime type set to unknown, DRM engine
- * will try to scan the input data to confirm the mime type, but we must say that
- * the scan and check of mime type is not strictly precise.
- */
- int32_t mimeType;
-
- /**
- * The function to get input data length, this function should be implement by out module,
- * and DRM engine will call-back it.
- *
- * \param inputHandle The handle of the DRM data.
- *
- * \return
- * -A positive integer indicate the length of input data.
- * -0, if some error occurred.
- */
- int32_t (*getInputDataLength)(int32_t inputHandle);
-
- /**
- * The function to read the input data, this function should be implement by out module,
- * and DRM engine will call-back it.
- *
- * \param inputHandle The handle of the DRM data.
- * \param buf The buffer mallocced by DRM engine to save the data.
- * \param bufLen The length of the buffer.
- *
- * \return
- * -A positive integer indicate the actually length of byte has been read.
- * -0, if some error occurred.
- * -(-1), if reach to the end of the data.
- */
- int32_t (*readInputData)(int32_t inputHandle, uint8_t* buf, int32_t bufLen);
-
- /**
- * The function to seek the current file pointer, this function should be implement by out module,
- * and DRM engine will call-back it.
- *
- * \param inputHandle The handle of the DRM data.
- * \param offset The offset from the start position to be seek.
- *
- * \return
- * -0, if seek operation success.
- * -(-1), if seek operation fail.
- */
- int32_t (*seekInputData)(int32_t inputHandle, int32_t offset);
-} T_DRM_Input_Data;
-
-/**
- * The constraint structure.
- */
-typedef struct _T_DRM_Constraint_Info {
- uint8_t indicator; /**< Whether there is a right */
- uint8_t unUsed[3];
- int32_t count; /**< The constraint of count */
- int32_t startDate; /**< The constraint of start date */
- int32_t startTime; /**< The constraint of start time */
- int32_t endDate; /**< The constraint of end date */
- int32_t endTime; /**< The constraint of end time */
- int32_t intervalDate; /**< The constraint of interval date */
- int32_t intervalTime; /**< The constraint of interval time */
-} T_DRM_Constraint_Info;
-
-/**
- * The rights permission and constraint information structure.
- */
-typedef struct _T_DRM_Rights_Info {
- uint8_t roId[256]; /**< The unique id for a specially rights object */
- T_DRM_Constraint_Info playRights; /**< Constraint of play */
- T_DRM_Constraint_Info displayRights; /**< Constraint of display */
- T_DRM_Constraint_Info executeRights; /**< Constraint of execute */
- T_DRM_Constraint_Info printRights; /**< Constraint of print */
-} T_DRM_Rights_Info;
-
-/**
- * The list node of the Rights information structure.
- */
-typedef struct _T_DRM_Rights_Info_Node {
- T_DRM_Rights_Info roInfo;
- struct _T_DRM_Rights_Info_Node *next;
-} T_DRM_Rights_Info_Node;
-
-/**
- * Install a rights object to DRM engine, include the rights in Combined Delivery cases.
- * Because all the rights object is managed by DRM engine, so every incoming rights object
- * must be install to the engine first, or the DRM engine will not recognize it.
- *
- * \param data The rights object data or Combined Delivery case data.
- * \param pRightsInfo The structure to save this rights information.
- *
- * \return
- * -DRM_SUCCESS, when install successfully.
- * -DRM_RIGHTS_DATA_INVALID, when the input rights data is invalid.
- * -DRM_FAILURE, when some other error occur.
- */
-int32_t SVC_drm_installRights(T_DRM_Input_Data data, T_DRM_Rights_Info* pRightsInfo);
-
-/**
- * Open a session for a special DRM object, it will parse the input DRM data, and then user
- * can try to get information for this DRM object, or try to use it if the rights is valid.
- *
- * \param data The DRM object data, DM or DCF.
- *
- * \return
- * -A handle for this opened DRM object session.
- * -DRM_MEDIA_DATA_INVALID, when the input DRM object data is invalid.
- * -DRM_FAILURE, when some other error occurred.
- */
-int32_t SVC_drm_openSession(T_DRM_Input_Data data);
-
-/**
- * Get the delivery method of the DRM object.
- *
- * \param session The handle for this DRM object session.
- *
- * \return
- * -The delivery method of this DRM object, include: FORWARD_LOCK, COMBINED_DELIVERY, SEPARATE_DELIVERY, SEPARATE_DELIVERY_FL.
- * -DRM_FAILURE, when some other error occurred.
- */
-int32_t SVC_drm_getDeliveryMethod(int32_t session);
-
-/**
- * Get DRM object media object content type.
- *
- * \param session The handle for this DRM object session.
- * \param mediaType The buffer to save the media type string, 64 bytes is enough.
- *
- * \return
- * -DRM_SUCCESS, when get the media object content type successfully.
- * -DRM_SESSION_NOT_OPENED, when the session is not opened or has been closed.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_getContentType(int32_t session, uint8_t* mediaType);
-
-/**
- * Check whether a specific DRM object has the specific permission rights or not.
- *
- * \param session The handle for this DRM object session.
- * \param permission Specify the permission to be checked.
- *
- * \return
- * -DRM_SUCCESS, when it has the rights for the permission.
- * -DRM_SESSION_NOT_OPENED, when the session is not opened or has been closed.
- * -DRM_NO_RIGHTS, when it has no rights.
- * -DRM_RIGHTS_PENDING, when it has the rights, but currently it is pending.
- * -DRM_RIGHTS_EXPIRED, when the rights has expired.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_checkRights(int32_t session, int32_t permission);
-
-/**
- * Consume the rights when try to use the DRM object.
- *
- * \param session The handle for this DRM object session.
- * \param permission Specify the permission to be checked.
- *
- * \return
- * -DRM_SUCCESS, when consume rights successfully.
- * -DRM_SESSION_NOT_OPENED, when the session is not opened or has been closed.
- * -DRM_NO_RIGHTS, when it has no rights.
- * -DRM_RIGHTS_PENDING, when it has the rights, but currently it is pending.
- * -DRM_RIGHTS_EXPIRED, when the rights has expired.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_consumeRights(int32_t session, int32_t permission);
-
-/**
- * Get DRM media object content data length.
- *
- * \param session The handle for this DRM object session.
- *
- * \return
- * -A positive integer indicate the length of the media object content data.
- * -DRM_SESSION_NOT_OPENED, when the session is not opened or has been closed.
- * -DRM_NO_RIGHTS, when the rights object is not existed.
- * -DRM_UNKNOWN_DATA_LEN, when DRM object media data length is unknown in case of DCF has no rights.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_getContentLength(int32_t session);
-
-/**
- * Get DRM media object content data. Support get the data piece by piece if the content is too large.
- *
- * \param session The handle for this DRM object session.
- * \param offset The offset to start to get content.
- * \param mediaBuf The buffer to save media object data.
- * \param mediaBufLen The length of the buffer.
- *
- * \return
- * -A positive integer indicate the actually length of the data has been got.
- * -DRM_SESSION_NOT_OPENED, when the session is not opened or has been closed.
- * -DRM_NO_RIGHTS, when the rights object is not existed.
- * -DRM_MEDIA_EOF, when reach to the end of the media data.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_getContent(int32_t session, int32_t offset, uint8_t* mediaBuf, int32_t mediaBufLen);
-
-/**
- * Get the rights issuer address, this interface is specially for Separate Delivery method.
- *
- * \param session The handle for this DRM object session.
- * \param rightsIssuer The buffer to save rights issuer, 256 bytes are enough.
- *
- * \return
- * -DRM_SUCCESS, when get the rights issuer successfully.
- * -DRM_SESSION_NOT_OPENED, when the session is not opened or has been closed.
- * -DRM_NOT_SD_METHOD, when it is not a Separate Delivery DRM object.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_getRightsIssuer(int32_t session, uint8_t* rightsIssuer);
-
-/**
- * Get DRM object constraint informations.
- *
- * \param session The handle for this DRM object session.
- * \param rights The structue to save the rights object information.
- *
- * \return
- * -DRM_SUCCESS, when get the rights information successfully.
- * -DRM_SESSION_NOT_OPENED, when the session is not opened or has been closed.
- * -DRM_NO_RIGHTS, when this DRM object has not rights.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_getRightsInfo(int32_t session, T_DRM_Rights_Info* rights);
-
-/**
- * Close the opened session, after closed, the handle become invalid.
- *
- * \param session The handle for this DRM object session.
- *
- * \return
- * -DRM_SUCCESS, when close operation success.
- * -DRM_SESSION_NOT_OPENED, when the session is not opened or has been closed.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_closeSession(int32_t session);
-
-/**
- * Check and update the given rights according the given permission.
- *
- * \param contentID The unique id of the rights object.
- * \param permission The permission to be updated.
- *
- * \return
- * -DRM_SUCCESS, when update operation success.
- * -DRM_NO_RIGHTS, when it has no rights.
- * -DRM_RIGHTS_PENDING, when it has the rights, but currently it is pending.
- * -DRM_RIGHTS_EXPIRED, when the rights has expired.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_updateRights(uint8_t* contentID, int32_t permission);
-
-/**
- * Scan all the rights object in current DRM engine, and get all their information.
- *
- * \param ppRightsInfo The pointer to the list structure to save rights info.
- *
- * \return
- * -DRM_SUCCESS, when get information successfully.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_viewAllRights(T_DRM_Rights_Info_Node **ppRightsInfo);
-
-/**
- * Free the allocated memory when call "SVC_drm_viewAllRights".
- *
- * \param pRightsHeader The header pointer of the list to be free.
- *
- * \return
- * -DRM_SUCCESS, when free operation successfully.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_freeRightsInfoList(T_DRM_Rights_Info_Node *pRightsHeader);
-
-/**
- * Delete a specify rights.
- *
- * \param roId The unique id of the rights.
- *
- * \return
- * -DRM_SUCCESS, when free operation successfully.
- * -DRM_NO_RIGHTS, when there is not this rights object.
- * -DRM_FAILURE, when some other error occured.
- */
-int32_t SVC_drm_deleteRights(uint8_t* roId);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __SVC_DRM_NEW_H__ */
diff --git a/media/libdrm/mobile1/include/parser/parser_dcf.h b/media/libdrm/mobile1/include/parser/parser_dcf.h
deleted file mode 100644
index c63a195..0000000
--- a/media/libdrm/mobile1/include/parser/parser_dcf.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __PARSER_DCF_H__
-#define __PARSER_DCF_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-#define MAX_ENCRYPTION_METHOD_LEN 64
-#define MAX_RIGHTS_ISSUER_LEN 256
-#define MAX_CONTENT_NAME_LEN 64
-#define MAX_CONTENT_DESCRIPTION_LEN 256
-#define MAX_CONTENT_VENDOR_LEN 256
-#define MAX_ICON_URI_LEN 256
-#define MAX_CONTENT_TYPE_LEN 64
-#define MAX_CONTENT_URI_LEN 256
-
-#define HEADER_ENCRYPTION_METHOD "Encryption-Method: "
-#define HEADER_RIGHTS_ISSUER "Rights-Issuer: "
-#define HEADER_CONTENT_NAME "Content-Name: "
-#define HEADER_CONTENT_DESCRIPTION "Content-Description: "
-#define HEADER_CONTENT_VENDOR "Content-Vendor: "
-#define HEADER_ICON_URI "Icon-Uri: "
-
-#define HEADER_ENCRYPTION_METHOD_LEN 19
-#define HEADER_RIGHTS_ISSUER_LEN 15
-#define HEADER_CONTENT_NAME_LEN 14
-#define HEADER_CONTENT_DESCRIPTION_LEN 21
-#define HEADER_CONTENT_VENDOR_LEN 16
-#define HEADER_ICON_URI_LEN 10
-
-#define UINT_VAR_FLAG 0x80
-#define UINT_VAR_DATA 0x7F
-#define MAX_UINT_VAR_BYTE 5
-#define DRM_UINT_VAR_ERR -1
-
-typedef struct _T_DRM_DCF_Info {
- uint8_t Version;
- uint8_t ContentTypeLen; /**< Length of the ContentType field */
- uint8_t ContentURILen; /**< Length of the ContentURI field */
- uint8_t unUsed;
- uint8_t ContentType[MAX_CONTENT_TYPE_LEN]; /**< The MIME media type of the plaintext data */
- uint8_t ContentURI[MAX_CONTENT_URI_LEN]; /**< The unique identifier of this content object */
- int32_t HeadersLen; /**< Length of the Headers field */
- int32_t EncryptedDataLen; /**< Length of the encrypted data field */
- int32_t DecryptedDataLen; /**< Length of the decrypted data field */
- uint8_t Encryption_Method[MAX_ENCRYPTION_METHOD_LEN]; /**< Encryption method */
- uint8_t Rights_Issuer[MAX_RIGHTS_ISSUER_LEN]; /**< Rights issuer */
- uint8_t Content_Name[MAX_CONTENT_NAME_LEN]; /**< Content name */
- uint8_t ContentDescription[MAX_CONTENT_DESCRIPTION_LEN]; /**< Content description */
- uint8_t ContentVendor[MAX_CONTENT_VENDOR_LEN]; /**< Content vendor */
- uint8_t Icon_URI[MAX_ICON_URI_LEN]; /**< Icon URI */
-} T_DRM_DCF_Info;
-
-/**
- * Parse the DRM content format data
- *
- * \param buffer (in)Input the DCF format data
- * \param bufferLen (in)The input buffer length
- * \param pDcfInfo (out)A structure pointer which contain information of DCF headers
- * \param ppEncryptedData (out)The location of encrypted data
- *
- * \return
- * -TRUE, when success
- * -FALSE, when failed
- */
-int32_t drm_dcfParser(uint8_t *buffer, int32_t bufferLen, T_DRM_DCF_Info *pDcfInfo,
- uint8_t **ppEncryptedData);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __PARSER_DCF_H__ */
diff --git a/media/libdrm/mobile1/include/parser/parser_dm.h b/media/libdrm/mobile1/include/parser/parser_dm.h
deleted file mode 100644
index ec8b6b2..0000000
--- a/media/libdrm/mobile1/include/parser/parser_dm.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __PARSER_DM_H__
-#define __PARSER_DM_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-#define MAX_CONTENT_TYPE_LEN 64
-#define MAX_CONTENT_ID 256
-#define MAX_CONTENT_BOUNDARY_LEN 256
-#define MAX_RIGHTS_ISSUER_LEN 256
-
-#define DRM_MIME_TYPE_RIGHTS_XML "application/vnd.oma.drm.rights+xml"
-#define DRM_MIME_TYPE_CONTENT "application/vnd.oma.drm.content"
-
-#define HEADERS_TRANSFER_CODING "Content-Transfer-Encoding:"
-#define HEADERS_CONTENT_TYPE "Content-Type:"
-#define HEADERS_CONTENT_ID "Content-ID:"
-
-#define TRANSFER_CODING_TYPE_7BIT "7bit"
-#define TRANSFER_CODING_TYPE_8BIT "8bit"
-#define TRANSFER_CODING_TYPE_BINARY "binary"
-#define TRANSFER_CODING_TYPE_BASE64 "base64"
-
-#define DRM_UID_TYPE_FORWORD_LOCK "forwardlock"
-#define DRM_NEW_LINE_CRLF "\r\n"
-
-#define HEADERS_TRANSFER_CODING_LEN 26
-#define HEADERS_CONTENT_TYPE_LEN 13
-#define HEADERS_CONTENT_ID_LEN 11
-
-#define DRM_MESSAGE_CODING_7BIT 0 /* default */
-#define DRM_MESSAGE_CODING_8BIT 1
-#define DRM_MESSAGE_CODING_BINARY 2
-#define DRM_MESSAGE_CODING_BASE64 3
-
-#define DRM_B64_DEC_BLOCK 3
-#define DRM_B64_ENC_BLOCK 4
-
-typedef struct _T_DRM_DM_Info {
- uint8_t contentType[MAX_CONTENT_TYPE_LEN]; /**< Content type */
- uint8_t contentID[MAX_CONTENT_ID]; /**< Content ID */
- uint8_t boundary[MAX_CONTENT_BOUNDARY_LEN]; /**< DRM message's boundary */
- uint8_t deliveryType; /**< The Delivery type */
- uint8_t transferEncoding; /**< Transfer encoding type */
- int32_t contentOffset; /**< The offset of the media content from the original DRM data */
- int32_t contentLen; /**< The length of the media content */
- int32_t rightsOffset; /**< The offset of the rights object in case of combined delivery */
- int32_t rightsLen; /**< The length of the rights object in case of combined delivery */
- uint8_t rightsIssuer[MAX_RIGHTS_ISSUER_LEN];/**< The rights issuer address in case of separate delivery */
-} T_DRM_DM_Info;
-
-/**
- * Search the string in a limited length.
- *
- * \param str The original string
- * \param strSearch The sub-string to be searched
- * \param len The length limited
- *
- * \return
- * -NULL, when there is not the searched string in length
- * -The pointer of this sub-string
- */
-const uint8_t* drm_strnstr(const uint8_t* str, const uint8_t* strSearch, int32_t len);
-
-/**
- * Parse the DRM message format data.
- *
- * \param buffer (in)Input the DRM message format data
- * \param bufferLen (in)The input buffer length
- * \param pDmInfo (out)A structure pointer which contain information of DRM message headers
- *
- * \return
- * -TRUE, when success
- * -FALSE, when failed
- */
-int32_t drm_parseDM(const uint8_t* buffer, int32_t bufferLen, T_DRM_DM_Info* pDmInfo);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __PARSER_DM_H__ */
diff --git a/media/libdrm/mobile1/include/parser/parser_rel.h b/media/libdrm/mobile1/include/parser/parser_rel.h
deleted file mode 100644
index 8def199..0000000
--- a/media/libdrm/mobile1/include/parser/parser_rel.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __PARSER_REL_H__
-#define __PARSER_REL_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-#define WRITE_RO_FLAG(whoIsAble, boolValue, Indicator, RIGHTS) do{\
- whoIsAble = boolValue;\
- Indicator |= RIGHTS;\
-}while(0)
-
-#define CHECK_VALIDITY(ret) do{\
- if(ret == NULL){\
- if(XML_ERROR_NO_SUCH_NODE != xml_errno)\
- return FALSE;\
- }\
- else\
- {\
- if(XML_ERROR_OK != xml_errno)\
- return FALSE;\
- }\
-}while(0)
-
-#define YMD_HMS_2_INT(year, mon, day, date, hour, min, sec, time) do{\
- date = year * 10000 + mon * 100 + day;\
- time = hour * 10000 + min * 100 + sec;\
-}while(0)
-
-#define DRM_UID_LEN 256
-#define DRM_KEY_LEN 16
-
-#define XML_DOM_PARSER
-
-typedef struct _T_DRM_DATETIME {
- int32_t date; /**< year * 10000 + mon *100 + day */
- int32_t time; /**< hour * 10000 + min *100 + sec */
-} T_DRM_DATETIME;
-
-typedef struct _T_DRM_Rights_Constraint {
- uint8_t Indicator; /**< Indicate which is constrainted, the first one indicate 0001, second one indicate 0010 */
- uint8_t unUsed[3];
- int32_t Count; /**< The times that can be used */
- T_DRM_DATETIME StartTime; /**< The starting time */
- T_DRM_DATETIME EndTime; /**< The ending time */
- T_DRM_DATETIME Interval; /**< The interval time */
-} T_DRM_Rights_Constraint;
-
-typedef struct _T_DRM_Rights {
- uint8_t Version[8]; /**< Version number */
- uint8_t uid[256]; /**< record the rights object name */
- uint8_t KeyValue[16]; /**< Decode base64 */
- int32_t bIsPlayable; /**< Is playable */
- int32_t bIsDisplayable; /**< Is displayable */
- int32_t bIsExecuteable; /**< Is executeable */
- int32_t bIsPrintable; /**< Is printable */
- T_DRM_Rights_Constraint PlayConstraint; /**< Play constraint */
- T_DRM_Rights_Constraint DisplayConstraint; /**< Display constraint */
- T_DRM_Rights_Constraint ExecuteConstraint; /**< Execute constraint */
- T_DRM_Rights_Constraint PrintConstraint; /**< Print constraint */
-} T_DRM_Rights;
-
-/**
- * Input year and month, return how many days that month have
- * \param year (in)Input the year
- * \param month (in)Input the month
- * \return
- * -A positive integer, which is how many days that month have
- * -When wrong input, return -1
- */
-int32_t drm_monthDays(int32_t year, int32_t month);
-
-/**
- * Check whether the date and time is valid.
- * \param year year of the date
- * \param month month of the date
- * \param day day of the date
- * \param hour hour of the time
- * \param min minute of the time
- * \param sec second of the time
- * \return
- * -when it is a valid time, return 0
- * -when it is a invalid time, return -1
- */
-int32_t drm_checkDate(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t min, int32_t sec);
-
-/**
- * Parse the rights object include xml format and wbxml format data
- *
- * \param buffer (in)Input the DRM rights object data
- * \param bufferLen (in)The buffer length
- * \param format (in)Which format, xml or wbxml
- * \param pRights (out)A structure pointer which save the rights information
- *
- * \return
- * -TRUE, when success
- * -FALSE, when failed
- */
-int32_t drm_relParser(uint8_t* buffer, int32_t bufferLen, int32_t Format, T_DRM_Rights* pRights);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __PARSER_REL_H__ */
diff --git a/media/libdrm/mobile1/include/xml/wbxml_tinyparser.h b/media/libdrm/mobile1/include/xml/wbxml_tinyparser.h
deleted file mode 100644
index 1c40467..0000000
--- a/media/libdrm/mobile1/include/xml/wbxml_tinyparser.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __WBXML_TINYPARSER_H__
-#define __WBXML_TINYPARSER_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-#define REL_TAG_RIGHTS 0x05
-#define REL_TAG_CONTEXT 0x06
-#define REL_TAG_VERSION 0x07
-#define REL_TAG_UID 0x08
-#define REL_TAG_AGREEMENT 0x09
-#define REL_TAG_ASSET 0x0A
-#define REL_TAG_KEYINFO 0x0B
-#define REL_TAG_KEYVALUE 0x0C
-#define REL_TAG_PERMISSION 0x0D
-#define REL_TAG_PLAY 0x0E
-#define REL_TAG_DISPLAY 0x0F
-#define REL_TAG_EXECUTE 0x10
-#define REL_TAG_PRINT 0x11
-#define REL_TAG_CONSTRAINT 0x12
-#define REL_TAG_COUNT 0x13
-#define REL_TAG_DATETIME 0x14
-#define REL_TAG_START 0x15
-#define REL_TAG_END 0x16
-#define REL_TAG_INTERVAL 0x17
-
-#define REL_CHECK_WBXML_HEADER(x) ((x != NULL) && (x[0] == 0x03) && (x[1] == 0x0E) && (x[2] == 0x6A))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __WBXML_TINYPARSER_H__ */
diff --git a/media/libdrm/mobile1/include/xml/xml_tinyParser.h b/media/libdrm/mobile1/include/xml/xml_tinyParser.h
deleted file mode 100644
index 4ad65b8..0000000
--- a/media/libdrm/mobile1/include/xml/xml_tinyParser.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __XML_TINYPARSER_H__
-#define __XML_TINYPARSER_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <drm_common_types.h>
-
-#define XML_DOM_PARSER
-#define WBXML_DOM_PARSER
-#define XML_DOM_CHECK_ENDTAG
-#define XML_ENABLE_ERRNO
-#define WBXML_OLD_VERSION /* for drm only */
-
-#ifdef DEBUG_MODE
-void XML_PrintMallocInfo();
-#endif /* DEBUG_MODE */
-
-#define XML_TRUE 1
-#define XML_FALSE 0
-#define XML_EOF 0
-#define XML_TAG_START 0
-#define XML_TAG_END 1
-#define XML_TAG_SELF 2
-
-#define XML_MAX_PROPERTY_LEN 256
-#define XML_MAX_ATTR_NAME_LEN 256
-#define XML_MAX_ATTR_VALUE_LEN 256
-#define XML_MAX_VALUE_LEN 256
-
-#define XML_ERROR_OK 0
-#define XML_ERROR_BUFFER_NULL -1
-#define XML_ERROR_ATTR_NAME -2
-#define XML_ERROR_ATTR_MISSED_EQUAL -3
-#define XML_ERROR_PROPERTY_NAME -4
-#define XML_ERROR_ATTR_VALUE -5
-#define XML_ERROR_ENDTAG -6
-#define XML_ERROR_NO_SUCH_NODE -7
-#define XML_ERROR_PROPERTY_END -8
-#define XML_ERROR_VALUE -9
-#define XML_ERROR_NO_START_TAG -14
-#define XML_ERROR_NOVALUE -15
-
-#define WBXML_ERROR_MISSED_CONTENT -10
-#define WBXML_ERROR_MBUINT32 -11
-#define WBXML_ERROR_MISSED_STARTTAG -12
-#define WBXML_ERROR_MISSED_ENDTAG -13
-
-#ifdef XML_ENABLE_ERRNO
-extern int32_t xml_errno;
-#define XML_ERROR(x) do { xml_errno = x; } while (0)
-#else /* XML_ENABLE_ERRNO */
-#define XML_ERROR
-#endif /* XML_ENABLE_ERRNO */
-
-#ifdef XML_DOM_PARSER
-uint8_t *XML_DOM_getNode(uint8_t *buffer, const uint8_t *const node);
-uint8_t *XML_DOM_getNodeValue(uint8_t *buffer, uint8_t *node,
- uint8_t **value, int32_t *valueLen);
-
-uint8_t *XML_DOM_getValue(uint8_t *buffer, uint8_t **pValue, int32_t *valueLen);
-uint8_t *XML_DOM_getAttr(uint8_t *buffer, uint8_t **pName, int32_t *nameLen,
- uint8_t **pValue, int32_t *valueLen);
-
-uint8_t *XML_DOM_getNextNode(uint8_t *buffer, uint8_t **pNodeName,
- int32_t *nodenameLen);
-
-uint8_t *XML_DOM_getTag(uint8_t *buffer, int32_t *tagLen, int32_t *tagType);
-#endif /* XML_DOM_PARSER */
-
-#ifdef WBXML_DOM_PARSER
-
-#define WBXML_WITH_ATTR 0x80
-#define WBXML_WITH_CONTENT 0x40
-#define WBXML_ATTR_END 0x01
-#define WBXML_CONTENT_END 0x01
-
-#define WBXML_SWITCH_PAGE 0x00
-#define WBXML_STR_I 0x03
-#define WBXML_END 0x00
-#define WBXML_OPAUE 0xC3
-#define WBXML_STR_T 0x83
-#define WBXML_OPAQUE 0xC3
-
-#define WBXML_GET_TAG(x) ((x) & 0x3F) /* get 6-digits */
-#define WBXML_HAS_ATTR(x) ((x) & WBXML_WITH_ATTR)
-#define WBXML_HAS_CONTENT(x) ((x) & WBXML_WITH_CONTENT)
-
-typedef struct _WBXML {
- uint8_t version;
- uint8_t unUsed[3];
- uint32_t publicid;
- uint32_t charset;
- int32_t strTableLen;
- uint8_t *strTable;
- uint8_t *Content;
- uint8_t *End;
- uint8_t *curPtr;
- int32_t depth;
-} WBXML;
-
-typedef int32_t XML_BOOL;
-
-#ifdef WBXML_OLD_VERSION
-uint8_t *WBXML_DOM_getNode(uint8_t *buffer, int32_t bufferLen,
- uint8_t *node);
-uint8_t *WBXML_DOM_getNodeValue(uint8_t *buffer, int32_t bufferLen,
- uint8_t *node,
- uint8_t **value,
- int32_t *valueLen);
-#endif /* WBXML_OLD_VERSION */
-
-XML_BOOL WBXML_DOM_Init(WBXML * pWbxml, uint8_t *buffer,
- int32_t bufferLen);
-XML_BOOL WBXML_DOM_Eof(WBXML * pWbxml);
-uint8_t WBXML_DOM_GetTag(WBXML * pWbxml);
-uint8_t WBXML_DOM_GetChar(WBXML * pWbxml);
-uint8_t WBXML_DOM_GetUIntVar(WBXML * pWbxml);
-void WBXML_DOM_Rewind(WBXML * pWbxml);
-void WBXML_DOM_Seek(WBXML * pWbxml, int32_t offset);
-int32_t WBXML_GetUintVar(const uint8_t *const buffer, int32_t *len);
-
-#endif /* WBXML_DOM_PARSER */
-
-#ifdef XML_TREE_STRUCTURE
-
-typedef struct _XML_TREE_ATTR XML_TREE_ATTR;
-struct _XML_TREE_ATTR {
- uint8_t name[XML_MAX_ATTR_VALUE_LEN];
- uint8_t value[XML_MAX_ATTR_VALUE_LEN];
- XML_TREE_ATTR *next;
-};
-
-typedef struct _XML_TREE XML_TREE;
-struct _XML_TREE {
- uint8_t tag[XML_MAX_PROPERTY_LEN];
- uint8_t value[XML_MAX_VALUE_LEN];
- XML_TREE_ATTR *attr;
- XML_TREE_ATTR *last_attr;
- XML_TREE *brother;
- XML_TREE *last_brother;
- XML_TREE *child;
-};
-
-XML_TREE *XML_makeTree(uint8_t **buf);
-void XML_freeTree(XML_TREE * pTree);
-
-#endif /* XML_TREE_STRUCTURE */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __XML_TINYPARSER_H__ */
diff --git a/media/libdrm/mobile1/src/jni/drm1_jni.c b/media/libdrm/mobile1/src/jni/drm1_jni.c
deleted file mode 100644
index 11353a7..0000000
--- a/media/libdrm/mobile1/src/jni/drm1_jni.c
+++ /dev/null
@@ -1,1166 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * @file drm1_jni.c
- *
- * This file implement the Java Native Interface
- * for supporting OMA DRM 1.0
- */
-
-#include <jni/drm1_jni.h>
-#include <objmng/svc_drm.h>
-#include "log.h"
-#include "JNIHelp.h"
-
-
-#define MS_PER_SECOND 1000 /* Milliseconds per second */
-#define MS_PER_MINUTE 60 * MS_PER_SECOND /* Milliseconds per minute */
-#define MS_PER_HOUR 60 * MS_PER_MINUTE /* Milliseconds per hour */
-#define MS_PER_DAY 24 * MS_PER_HOUR /* Milliseconds per day */
-
-#define SECONDS_PER_MINUTE 60 /* Seconds per minute*/
-#define SECONDS_PER_HOUR 60 * SECONDS_PER_MINUTE /* Seconds per hour */
-#define SECONDS_PER_DAY 24 * SECONDS_PER_HOUR /* Seconds per day */
-
-#define DAY_PER_MONTH 30 /* Days per month */
-#define DAY_PER_YEAR 365 /* Days per year */
-
-/** Nonzero if 'y' is a leap year, else zero. */
-#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
-
-/** Number of leap years from 1970 to 'y' (not including 'y' itself). */
-#define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
-
-/** Accumulated number of days from 01-Jan up to start of current month. */
-static const int32_t ydays[] = {
- 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
-};
-
-#define int64_const(s) (s)
-#define int64_add(dst, s1, s2) ((void)((dst) = (s1) + (s2)))
-#define int64_mul(dst, s1, s2) ((void)((dst) = (int64_t)(s1) * (int64_t)(s2)))
-
-/**
- * DRM data structure
- */
-typedef struct _DrmData {
- /**
- * The id of the DRM content.
- */
- int32_t id;
-
- /**
- * The pointer of JNI interface.
- */
- JNIEnv* env;
-
- /**
- * The pointer of DRM raw content InputStream object.
- */
- jobject* pInData;
-
- /**
- * The len of the InputStream object.
- */
- int32_t len;
-
- /**
- * The next DRM data.
- */
- struct _DrmData *next;
-} DrmData;
-
-/** The table to hold all the DRM data. */
-static DrmData *drmTable = NULL;
-
-/**
- * Allocate a new item of DrmData.
- *
- * \return a pointer to a DrmData item if allocate successfully,
- * otherwise return NULL
- */
-static DrmData * newItem(void)
-{
- DrmData *d = (DrmData *)malloc(sizeof(DrmData));
-
- if (d != NULL) {
- d->id = -1;
- d->next = NULL;
- }
-
- return d;
-}
-
-/**
- * Free the memory of the specified DrmData item <code>d</code>.
- *
- * \param d - a pointer to DrmData
- */
-static void freeItem(DrmData *d)
-{
- assert(d != NULL);
-
- free(d);
-}
-
-/**
- * Insert a DrmData item with given <code>name</code> into the head of
- * the DrmData list.
- *
- * @param d - the pointer of the JNI interface
- * @param pInData - the pointer of the DRM content InputStream object.
- *
- * @return <code>JNI_DRM_SUCCESS</code> if insert successfully, otherwise
- * return <code>JNI_DRM_FAILURE</code>
- */
-static int32_t addItem(DrmData* d)
-{
- if (NULL == d)
- return JNI_DRM_FAILURE;
-
- if (NULL == drmTable) {
- drmTable = d;
- return JNI_DRM_SUCCESS;
- }
-
- d->next = drmTable;
- drmTable = d;
-
- return JNI_DRM_SUCCESS;
-}
-
-/**
- * Get the item from the DrmData list by the specified <code>
- * id</code>.
- *
- * @param p - the pointer of the DRM content InputStream object.
- *
- * @return a pointer to the DrmData item if find it successfuly,
- * otherwise return NULL
- */
-static DrmData * getItem(int32_t id)
-{
- DrmData *d;
-
- if (NULL == drmTable)
- return NULL;
-
- for (d = drmTable; d != NULL; d = d->next) {
- if (id == d->id)
- return d;
- }
-
- return NULL;
-}
-
-/**
- * Remove the specified DrmData item <code>d</code>.
- *
- * @param p - the pointer of the DRM content InputStream object.
- *
- * @return <code>JNI_DRM_SUCCESS</code> if remove successfuly,
- * otherwise return <code>JNI_DRM_FAILURE</code>
- */
-static int32_t removeItem(int32_t id)
-{
- DrmData *curItem, *preItem, *dstItem;
-
- if (NULL == drmTable)
- return JNI_DRM_FAILURE;
-
- preItem = NULL;
- for (curItem = drmTable; curItem != NULL; curItem = curItem->next) {
- if (id == curItem->id) {
- if (curItem == drmTable)
- drmTable = curItem->next;
- else
- preItem->next = curItem->next;
-
- freeItem(curItem);
-
- return JNI_DRM_SUCCESS;
- }
-
- preItem = curItem;
- }
-
- return JNI_DRM_FAILURE;
-}
-
-
-static int32_t getInputStreamDataLength(int32_t handle)
-{
- JNIEnv* env;
- jobject* pInputStream;
- int32_t len;
- DrmData* p;
- jclass cls;
- jmethodID mid;
-
- p = (DrmData *)handle;
-
- if (NULL == p)
- return 0;
-
- env = p->env;
- pInputStream = p->pInData;
- len = p->len;
-
- if (NULL == env || p->len <= 0 || NULL == pInputStream)
- return 0;
-
- /* check the original InputStream is available or not */
- cls = (*env)->GetObjectClass(env, *pInputStream);
- mid = (*env)->GetMethodID(env, cls, "available", "()I");
- (*env)->DeleteLocalRef(env, cls);
-
- if (NULL == mid)
- return 0;
-
- if (0 > (*env)->CallIntMethod(env, *pInputStream, mid))
- return 0;
-
- return len;
-}
-
-static int32_t readInputStreamData(int32_t handle, uint8_t* buf, int32_t bufLen)
-{
- JNIEnv* env;
- jobject* pInputStream;
- int32_t len;
- DrmData* p;
- jclass cls;
- jmethodID mid;
- jbyteArray tmp;
- int tmpLen;
- jbyte* pNativeBuf;
-
- p = (DrmData *)handle;
-
- if (NULL == p || NULL == buf || bufLen <- 0)
- return 0;
-
- env = p->env;
- pInputStream = p->pInData;
- len = p->len;
-
- if (NULL == env || p->len <= 0 || NULL == pInputStream)
- return 0;
-
- cls = (*env)->GetObjectClass(env, *pInputStream);
- mid = (*env)->GetMethodID(env, cls, "read", "([BII)I");
- tmp = (*env)->NewByteArray(env, bufLen);
- bufLen = (*env)->CallIntMethod(env, *pInputStream, mid, tmp, 0, bufLen);
-
- (*env)->DeleteLocalRef(env, cls);
-
- if (-1 == bufLen)
- return -1;
-
- pNativeBuf = (*env)->GetByteArrayElements(env, tmp, NULL);
- memcpy(buf, pNativeBuf, bufLen);
- (*env)->ReleaseByteArrayElements(env, tmp, pNativeBuf, 0);
- (*env)->DeleteLocalRef(env, tmp);
-
- return bufLen;
-}
-
-static const T_DRM_Rights_Info_Node *searchRightsObject(const jbyte* roId, const T_DRM_Rights_Info_Node* pRightsList)
-{
- const T_DRM_Rights_Info_Node *pTmp;
-
- if (NULL == roId || NULL == pRightsList)
- return NULL;
-
- pTmp = pRightsList;
-
- while (NULL != pTmp) {
- if(0 == strcmp((char *)roId, (char *)pTmp->roInfo.roId))
- break;
- pTmp = pTmp->next;
- }
-
- return pTmp;
-}
-
-/**
- * Returns the difference in seconds between the given GMT time
- * and 1970-01-01 00:00:00 GMT.
- *
- * \param year the year (since 1970)
- * \param month the month (1 - 12)
- * \param day the day (1 - 31)
- * \param hour the hour (0 - 23)
- * \param minute the minute (0 - 59)
- * \param second the second (0 - 59)
- *
- * \return the difference in seconds between the given GMT time
- * and 1970-01-01 00:00:00 GMT.
- */
-static int64_t mkgmtime(
- uint32_t year, uint32_t month, uint32_t day,
- uint32_t hour, uint32_t minute, uint32_t second)
-{
- int64_t result;
-
- /*
- * FIXME: It does not check whether the specified days
- * is valid based on the specified months.
- */
- assert(year >= 1970
- && month > 0 && month <= 12
- && day > 0 && day <= 31
- && hour < 24 && minute < 60
- && second < 60);
-
- /* Set 'day' to the number of days into the year. */
- day += ydays[month - 1] + (month > 2 && leap (year)) - 1;
-
- /* Now calculate 'day' to the number of days since Jan 1, 1970. */
- day = day + 365 * (year - 1970) + nleap(year);
-
- int64_mul(result, int64_const(day), int64_const(SECONDS_PER_DAY));
- int64_add(result, result, int64_const(
- SECONDS_PER_HOUR * hour + SECONDS_PER_MINUTE * minute + second));
-
- return result;
-}
-
-/**
- * Compute the milliseconds by the specified <code>date</code>
- * and <code>time</code>.
- *
- * @param date - the specified date,
- * <code>date = year * 10000 + month * 100 + day</code>
- * @param time - the specified time,
- * <code>time = hour * 10000 + minute * 100 + second</code>
- *
- * @return the related milliseconds
- */
-static int64_t computeTime(int32_t date, int32_t time)
-{
- int32_t year, month, day, hour, minute, second;
-
- year = date / 10000;
- month = (date / 100) % 100;
- day = date % 100;
- hour = time / 10000;
- minute = (time / 100) % 100;
- second = time % 100;
-
- /* Adjust the invalid parameters. */
- if (year < 1970) year = 1970;
- if (month < 1) month = 1;
- if (month > 12) month = 12;
- if (day < 1) day = 1;
- if (day > 31) day = 31;
- if (hour < 0) hour = 0;
- if (hour > 23) hour = 23;
- if (minute < 0) minute = 0;
- if (minute > 59) minute = 59;
- if (second < 0) second = 0;
- if (second > 59) second = 59;
-
- return mkgmtime(year, month, day, hour, minute, second) * 1000;
-}
-
-/**
- * Compute the milliseconds by the specified <code>date</code>
- * and <code>time</code>.
- * Note that here we always treat 1 year as 365 days and 1 month as 30 days
- * that is not precise. But it should not be a problem since OMA DRM 2.0
- * already restricts the interval representation to be day-based,
- * i.e. there will not be an interval with year or month any more in the
- * future.
- *
- * @param date - the specified date,
- * <code>date = year * 10000 + month * 100 + day</code>
- * @param time - the specified time,
- * <code>time = hour * 10000 + minute * 100 + second</code>
- *
- * @return the related milliseconds
- */
-static int64_t computeInterval(int32_t date, int32_t time)
-{
- int32_t year, month, day, hour, minute, second;
- int64_t milliseconds;
-
- year = date / 10000;
- month = (date / 100) % 100;
- day = date % 100;
- hour = time / 10000;
- minute = (time / 100) % 100;
- second = time % 100;
-
- /* milliseconds = ((((year * 365 + month * 30 + day) * 24
- * + hour) * 60 + minute) * 60 + second) * 1000;
- */
- int64_mul(milliseconds,
- int64_const(year * DAY_PER_YEAR + month * DAY_PER_MONTH + day),
- int64_const(MS_PER_DAY));
- int64_add(milliseconds, milliseconds,
- int64_const(hour * MS_PER_HOUR + minute * MS_PER_MINUTE +
- second * MS_PER_SECOND));
-
- return milliseconds;
-}
-
-static jint getObjectIntField(JNIEnv * env, jobject obj, const char *name, jint * value)
-{
- jclass clazz;
- jfieldID field;
-
- clazz = (*env)->GetObjectClass(env, obj);
- if (NULL == clazz)
- return JNI_DRM_FAILURE;
-
- field = (*env)->GetFieldID(env, clazz, name, "I");
- (*env)->DeleteLocalRef(env, clazz);
-
- if (NULL == field)
- return JNI_DRM_FAILURE;
-
- *value = (*env)->GetIntField(env, obj, field);
-
- return JNI_DRM_SUCCESS;
-}
-
-static jint setObjectIntField(JNIEnv * env, jobject obj, const char *name, jint value)
-{
- jclass clazz;
- jfieldID field;
-
- clazz = (*env)->GetObjectClass(env, obj);
- if (NULL == clazz)
- return JNI_DRM_FAILURE;
-
- field = (*env)->GetFieldID(env, clazz, name, "I");
- (*env)->DeleteLocalRef(env, clazz);
-
- if (NULL == field)
- return JNI_DRM_FAILURE;
-
- (*env)->SetIntField(env, obj, field, value);
-
- return JNI_DRM_SUCCESS;
-}
-
-static jint setObjectLongField(JNIEnv * env, jobject obj, const char *name, jlong value)
-{
- jclass clazz;
- jfieldID field;
-
- clazz = (*env)->GetObjectClass(env, obj);
- if (NULL == clazz)
- return JNI_DRM_FAILURE;
-
- field = (*env)->GetFieldID(env, clazz, name, "J");
- (*env)->DeleteLocalRef(env, clazz);
-
- if (NULL == field)
- return JNI_DRM_FAILURE;
-
- (*env)->SetLongField(env, obj, field, value);
-
- return JNI_DRM_SUCCESS;
-}
-
-static jint setConstraintFields(JNIEnv * env, jobject constraint, T_DRM_Constraint_Info * pConstraint)
-{
- /* if no this permission */
- if (pConstraint->indicator == (uint8_t)DRM_NO_RIGHTS) {
- if (JNI_DRM_FAILURE == setObjectIntField(env, constraint, "count", 0))
- return JNI_DRM_FAILURE;
-
- return JNI_DRM_SUCCESS;
- }
-
- /* set count field */
- if (pConstraint->indicator & DRM_COUNT_CONSTRAINT) {
- if (JNI_DRM_FAILURE == setObjectIntField(env, constraint, "count", pConstraint->count))
- return JNI_DRM_FAILURE;
- }
-
- /* set start time field */
- if (pConstraint->indicator & DRM_START_TIME_CONSTRAINT) {
- int64_t startTime;
-
- startTime = computeTime(pConstraint->startDate, pConstraint->startTime);
-
- if (JNI_DRM_FAILURE == setObjectLongField(env, constraint, "startDate", startTime))
- return JNI_DRM_FAILURE;
- }
-
- /* set end time field */
- if (pConstraint->indicator & DRM_END_TIME_CONSTRAINT) {
- int64_t endTime;
-
- endTime = computeTime(pConstraint->endDate, pConstraint->endTime);
-
- if (JNI_DRM_FAILURE == setObjectLongField(env, constraint, "endDate", endTime))
- return JNI_DRM_FAILURE;
- }
-
- /* set interval field */
- if (pConstraint->indicator & DRM_INTERVAL_CONSTRAINT) {
- int64_t interval;
-
- interval = computeInterval(pConstraint->intervalDate, pConstraint->intervalTime);
-
- if (JNI_DRM_FAILURE == setObjectLongField(env, constraint, "interval", interval))
- return JNI_DRM_FAILURE;
- }
-
- return JNI_DRM_SUCCESS;
-}
-
-static jint setRightsFields(JNIEnv * env, jobject rights, T_DRM_Rights_Info* pRoInfo)
-{
- jclass clazz;
- jfieldID field;
- jstring str;
- jint index;
-
- clazz = (*env)->GetObjectClass(env, rights);
- if (NULL == clazz)
- return JNI_DRM_FAILURE;
-
- /* set roId field */
- field = (*env)->GetFieldID(env, clazz, "roId", "Ljava/lang/String;");
- (*env)->DeleteLocalRef(env, clazz);
-
- if (NULL == field)
- return JNI_DRM_FAILURE;
-
- str = (*env)->NewStringUTF(env, (char *)pRoInfo->roId);
- if (NULL == str)
- return JNI_DRM_FAILURE;
-
- (*env)->SetObjectField(env, rights, field, str);
- (*env)->DeleteLocalRef(env, str);
-
- return JNI_DRM_SUCCESS;
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRawContent_nativeConstructDrmContent
- (JNIEnv * env, jobject rawContent, jobject data, jint len, jint mimeType)
-{
- int32_t id;
- T_DRM_Input_Data inData;
- DrmData* drmInData;
-
- switch (mimeType) {
- case JNI_DRM_MIMETYPE_MESSAGE:
- mimeType = TYPE_DRM_MESSAGE;
- break;
- case JNI_DRM_MIMETYPE_CONTENT:
- mimeType = TYPE_DRM_CONTENT;
- break;
- default:
- return JNI_DRM_FAILURE;
- }
-
- drmInData = newItem();
- if (NULL == drmInData)
- return JNI_DRM_FAILURE;
-
- drmInData->env = env;
- drmInData->pInData = &data;
- drmInData->len = len;
-
- if (JNI_DRM_FAILURE == addItem(drmInData))
- return JNI_DRM_FAILURE;
-
- inData.inputHandle = (int32_t)drmInData;
- inData.mimeType = mimeType;
- inData.getInputDataLength = getInputStreamDataLength;
- inData.readInputData = readInputStreamData;
-
- id = SVC_drm_openSession(inData);
- if (id < 0)
- return JNI_DRM_FAILURE;
-
- drmInData->id = id;
-
- return id;
-}
-
-/* native interface */
-JNIEXPORT jstring JNICALL
-Java_android_drm_mobile1_DrmRawContent_nativeGetRightsAddress
- (JNIEnv * env, jobject rawContent)
-{
- jint id;
- uint8_t rightsIssuer[256] = {0};
- jstring str = NULL;
-
- if (JNI_DRM_FAILURE == getObjectIntField(env, rawContent, "id", &id))
- return NULL;
-
- if (DRM_SUCCESS == SVC_drm_getRightsIssuer(id, rightsIssuer))
- str = (*env)->NewStringUTF(env, (char *)rightsIssuer);
-
- return str;
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRawContent_nativeGetDeliveryMethod
- (JNIEnv * env, jobject rawContent)
-{
- jint id;
- int32_t res;
-
- if (JNI_DRM_FAILURE == getObjectIntField(env, rawContent, "id", &id))
- return JNI_DRM_FAILURE;
-
- res = SVC_drm_getDeliveryMethod(id);
-
- switch (res) {
- case FORWARD_LOCK:
- return JNI_DRM_FORWARD_LOCK;
- case COMBINED_DELIVERY:
- return JNI_DRM_COMBINED_DELIVERY;
- case SEPARATE_DELIVERY:
- return JNI_DRM_SEPARATE_DELIVERY;
- case SEPARATE_DELIVERY_FL:
- return JNI_DRM_SEPARATE_DELIVERY_DM;
- default:
- return JNI_DRM_FAILURE;
- }
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRawContent_nativeReadContent
- (JNIEnv * env, jobject rawContent, jbyteArray buf, jint bufOff, jint len, jint mediaOff)
-{
- jint id;
- jbyte *nativeBuf;
- jclass cls;
- jmethodID mid;
- DrmData* p;
- jobject inputStream;
- jfieldID field;
-
- if (NULL == buf) {
- jniThrowNullPointerException(env, "b == null");
- return JNI_DRM_FAILURE;
- }
-
- if (len < 0 || bufOff < 0 || len + bufOff > (*env)->GetArrayLength(env, buf)) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
- return JNI_DRM_FAILURE;
- }
-
- if (mediaOff < 0 || len == 0)
- return JNI_DRM_FAILURE;
-
- if (JNI_DRM_FAILURE == getObjectIntField(env, rawContent, "id", &id))
- return JNI_DRM_FAILURE;
-
- p = getItem(id);
- if (NULL == p)
- return JNI_DRM_FAILURE;
-
- cls = (*env)->GetObjectClass(env, rawContent);
- if (NULL == cls)
- return JNI_DRM_FAILURE;
-
- field = (*env)->GetFieldID(env, cls, "inData", "Ljava/io/BufferedInputStream;");
- (*env)->DeleteLocalRef(env, cls);
-
- if (NULL == field)
- return JNI_DRM_FAILURE;
-
- inputStream = (*env)->GetObjectField(env, rawContent, field);
-
- p->env = env;
- p->pInData = &inputStream;
-
- nativeBuf = (*env)->GetByteArrayElements(env, buf, NULL);
-
- len = SVC_drm_getContent(id, mediaOff, (uint8_t *)nativeBuf + bufOff, len);
-
- (*env)->ReleaseByteArrayElements(env, buf, nativeBuf, 0);
-
- if (DRM_MEDIA_EOF == len)
- return JNI_DRM_EOF;
- if (len <= 0)
- return JNI_DRM_FAILURE;
-
- return len;
-}
-
-/* native interface */
-JNIEXPORT jstring JNICALL
-Java_android_drm_mobile1_DrmRawContent_nativeGetContentType
- (JNIEnv * env, jobject rawContent)
-{
- jint id;
- uint8_t contentType[64] = {0};
- jstring str = NULL;
-
- if (JNI_DRM_FAILURE == getObjectIntField(env, rawContent, "id", &id))
- return NULL;
-
- if (DRM_SUCCESS == SVC_drm_getContentType(id, contentType))
- str = (*env)->NewStringUTF(env, (char *)contentType);
-
- return str;
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRawContent_nativeGetContentLength
- (JNIEnv * env, jobject rawContent)
-{
- jint id;
- int32_t len;
-
- if (JNI_DRM_FAILURE == getObjectIntField(env, rawContent, "id", &id))
- return JNI_DRM_FAILURE;
-
- len = SVC_drm_getContentLength(id);
-
- if (DRM_UNKNOWN_DATA_LEN == len)
- return JNI_DRM_UNKNOWN_DATA_LEN;
-
- if (0 > len)
- return JNI_DRM_FAILURE;
-
- return len;
-}
-
-/* native interface */
-JNIEXPORT void JNICALL
-Java_android_drm_mobile1_DrmRawContent_finalize
- (JNIEnv * env, jobject rawContent)
-{
- jint id;
-
- if (JNI_DRM_FAILURE == getObjectIntField(env, rawContent, "id", &id))
- return;
-
- removeItem(id);
-
- SVC_drm_closeSession(id);
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRights_nativeGetConstraintInfo
- (JNIEnv * env, jobject rights, jint permission, jobject constraint)
-{
- jclass clazz;
- jfieldID field;
- jstring str;
- uint8_t *nativeStr;
- T_DRM_Rights_Info_Node *pRightsList;
- T_DRM_Rights_Info_Node *pCurNode;
- T_DRM_Constraint_Info *pConstraint;
-
- clazz = (*env)->GetObjectClass(env, rights);
- if (NULL == clazz)
- return JNI_DRM_FAILURE;
-
- field = (*env)->GetFieldID(env, clazz, "roId", "Ljava/lang/String;");
- (*env)->DeleteLocalRef(env, clazz);
-
- if (NULL == field)
- return JNI_DRM_FAILURE;
-
- str = (*env)->GetObjectField(env, rights, field);
-
- nativeStr = (uint8_t *)(*env)->GetStringUTFChars(env, str, NULL);
- if (NULL == nativeStr)
- return JNI_DRM_FAILURE;
-
- /* this means forward-lock rights */
- if (0 == strcmp((char *)nativeStr, "ForwardLock")) {
- (*env)->ReleaseStringUTFChars(env, str, (char *)nativeStr);
- return JNI_DRM_SUCCESS;
- }
-
- if (DRM_FAILURE == SVC_drm_viewAllRights(&pRightsList)) {
- (*env)->ReleaseStringUTFChars(env, str, (char *)nativeStr);
- return JNI_DRM_FAILURE;
- }
-
- pCurNode = searchRightsObject((jbyte *)nativeStr, pRightsList);
- if (NULL == pCurNode) {
- (*env)->ReleaseStringUTFChars(env, str, (char *)nativeStr);
- SVC_drm_freeRightsInfoList(pRightsList);
- return JNI_DRM_FAILURE;
- }
- (*env)->ReleaseStringUTFChars(env, str, (char *)nativeStr);
-
- switch (permission) {
- case JNI_DRM_PERMISSION_PLAY:
- pConstraint = &(pCurNode->roInfo.playRights);
- break;
- case JNI_DRM_PERMISSION_DISPLAY:
- pConstraint = &(pCurNode->roInfo.displayRights);
- break;
- case JNI_DRM_PERMISSION_EXECUTE:
- pConstraint = &(pCurNode->roInfo.executeRights);
- break;
- case JNI_DRM_PERMISSION_PRINT:
- pConstraint = &(pCurNode->roInfo.printRights);
- break;
- default:
- SVC_drm_freeRightsInfoList(pRightsList);
- return JNI_DRM_FAILURE;
- }
-
- /* set constraint field */
- if (JNI_DRM_FAILURE == setConstraintFields(env, constraint, pConstraint)) {
- SVC_drm_freeRightsInfoList(pRightsList);
- return JNI_DRM_FAILURE;
- }
-
- SVC_drm_freeRightsInfoList(pRightsList);
-
- return JNI_DRM_SUCCESS;
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRights_nativeConsumeRights
- (JNIEnv * env, jobject rights, jint permission)
-{
- jclass clazz;
- jfieldID field;
- jstring str;
- uint8_t *nativeStr;
- int32_t id;
-
- switch (permission) {
- case JNI_DRM_PERMISSION_PLAY:
- permission = DRM_PERMISSION_PLAY;
- break;
- case JNI_DRM_PERMISSION_DISPLAY:
- permission = DRM_PERMISSION_DISPLAY;
- break;
- case JNI_DRM_PERMISSION_EXECUTE:
- permission = DRM_PERMISSION_EXECUTE;
- break;
- case JNI_DRM_PERMISSION_PRINT:
- permission = DRM_PERMISSION_PRINT;
- break;
- default:
- return JNI_DRM_FAILURE;
- }
-
- clazz = (*env)->GetObjectClass(env, rights);
- if (NULL == clazz)
- return JNI_DRM_FAILURE;
-
- field = (*env)->GetFieldID(env, clazz, "roId", "Ljava/lang/String;");
- (*env)->DeleteLocalRef(env, clazz);
-
- if (NULL == field)
- return JNI_DRM_FAILURE;
-
- str = (*env)->GetObjectField(env, rights, field);
-
- nativeStr = (uint8_t *)(*env)->GetStringUTFChars(env, str, NULL);
- if (NULL == nativeStr)
- return JNI_DRM_FAILURE;
-
- if (0 == strcmp("ForwardLock", (char *)nativeStr)) {
- (*env)->ReleaseStringUTFChars(env, str, (char *)nativeStr);
- return JNI_DRM_SUCCESS;
- }
-
- if (DRM_SUCCESS != SVC_drm_updateRights(nativeStr, permission)) {
- (*env)->ReleaseStringUTFChars(env, str, (char *)nativeStr);
- return JNI_DRM_FAILURE;
- }
-
- (*env)->ReleaseStringUTFChars(env, str, (char *)nativeStr);
-
- return JNI_DRM_SUCCESS;
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRightsManager_nativeInstallDrmRights
- (JNIEnv * env, jobject rightsManager, jobject data, jint len, jint mimeType, jobject rights)
-{
- int32_t id;
- T_DRM_Input_Data inData;
- DrmData* drmInData;
- jclass cls;
- jmethodID mid;
- T_DRM_Rights_Info rightsInfo;
-
- switch (mimeType) {
- case JNI_DRM_MIMETYPE_RIGHTS_XML:
- mimeType = TYPE_DRM_RIGHTS_XML;
- break;
- case JNI_DRM_MIMETYPE_RIGHTS_WBXML:
- mimeType = TYPE_DRM_RIGHTS_WBXML;
- break;
- case JNI_DRM_MIMETYPE_MESSAGE:
- mimeType = TYPE_DRM_MESSAGE;
- break;
- default:
- return JNI_DRM_FAILURE;
- }
-
- drmInData = newItem();
- if (NULL == drmInData)
- return JNI_DRM_FAILURE;
-
- drmInData->env = env;
- drmInData->pInData = &data;
- drmInData->len = len;
-
- inData.inputHandle = (int32_t)drmInData;
- inData.mimeType = mimeType;
- inData.getInputDataLength = getInputStreamDataLength;
- inData.readInputData = readInputStreamData;
-
- memset(&rightsInfo, 0, sizeof(T_DRM_Rights_Info));
- if (DRM_FAILURE == SVC_drm_installRights(inData, &rightsInfo))
- return JNI_DRM_FAILURE;
-
- freeItem(drmInData);
-
- return setRightsFields(env, rights, &rightsInfo);
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRightsManager_nativeQueryRights
- (JNIEnv * env, jobject rightsManager, jobject rawContent, jobject rights)
-{
- jint id;
- T_DRM_Rights_Info rightsInfo;
-
- if (JNI_DRM_FAILURE == getObjectIntField(env, rawContent, "id", &id))
- return JNI_DRM_FAILURE;
-
- memset(&rightsInfo, 0, sizeof(T_DRM_Rights_Info));
- if (DRM_SUCCESS != SVC_drm_getRightsInfo(id, &rightsInfo))
- return JNI_DRM_FAILURE;
-
- return setRightsFields(env, rights, &rightsInfo);
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRightsManager_nativeGetNumOfRights
- (JNIEnv * env, jobject rightsManager)
-{
- T_DRM_Rights_Info_Node *pRightsList;
- T_DRM_Rights_Info_Node *pCurNode;
- int32_t num = 0;
-
- if (DRM_FAILURE == SVC_drm_viewAllRights(&pRightsList))
- return JNI_DRM_FAILURE;
-
- pCurNode = pRightsList;
- while (pCurNode != NULL) {
- num++;
- pCurNode = pCurNode->next;
- }
-
- SVC_drm_freeRightsInfoList(pRightsList);
-
- return num;
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRightsManager_nativeGetRightsList
- (JNIEnv * env, jobject rightsManager, jobjectArray rightsArray, jint num)
-{
- T_DRM_Rights_Info_Node *pRightsList;
- T_DRM_Rights_Info_Node *pCurNode;
- int32_t index;
-
- if (DRM_FAILURE == SVC_drm_viewAllRights(&pRightsList))
- return JNI_DRM_FAILURE;
-
- pCurNode = pRightsList;
- for (index = 0; NULL != pCurNode; index++) {
- jobject rights = (*env)->GetObjectArrayElement(env, rightsArray, index);
- if (NULL == rights)
- break;
-
- if (JNI_DRM_FAILURE == setRightsFields(env, rights, &(pCurNode->roInfo)))
- break;
-
- (*env)->SetObjectArrayElement(env, rightsArray, index, rights);
-
- pCurNode = pCurNode->next;
- }
-
- SVC_drm_freeRightsInfoList(pRightsList);
-
- return index;
-}
-
-/* native interface */
-JNIEXPORT jint JNICALL
-Java_android_drm_mobile1_DrmRightsManager_nativeDeleteRights
- (JNIEnv * env, jobject rightsManager, jobject rights)
-{
- jclass clazz;
- jfieldID field;
- jstring str;
- uint8_t *nativeStr;
-
- clazz = (*env)->GetObjectClass(env, rights);
- if (NULL == clazz)
- return JNI_DRM_FAILURE;
-
- field = (*env)->GetFieldID(env, clazz, "roId", "Ljava/lang/String;");
- if (NULL == field)
- return JNI_DRM_FAILURE;
-
- str = (*env)->GetObjectField(env, rights, field);
-
- nativeStr = (uint8_t *)(*env)->GetStringUTFChars(env, str, NULL);
- if (NULL == nativeStr)
- return JNI_DRM_FAILURE;
-
- if (0 == strcmp("ForwardLock", (char *)nativeStr))
- return JNI_DRM_SUCCESS;
-
- if (DRM_SUCCESS != SVC_drm_deleteRights(nativeStr)) {
- (*env)->ReleaseStringUTFChars(env, str, (char *)nativeStr);
- return JNI_DRM_FAILURE;
- }
-
- (*env)->ReleaseStringUTFChars(env, str, (char *)nativeStr);
- return JNI_DRM_SUCCESS;
-}
-
-/*
- * Table of methods associated with the DrmRawContent class.
- */
-static JNINativeMethod gDrmRawContentMethods[] = {
- /* name, signature, funcPtr */
- {"nativeConstructDrmContent", "(Ljava/io/InputStream;II)I",
- (void*)Java_android_drm_mobile1_DrmRawContent_nativeConstructDrmContent},
- {"nativeGetRightsAddress", "()Ljava/lang/String;",
- (void*)Java_android_drm_mobile1_DrmRawContent_nativeGetRightsAddress},
- {"nativeGetDeliveryMethod", "()I",
- (void*)Java_android_drm_mobile1_DrmRawContent_nativeGetDeliveryMethod},
- {"nativeReadContent", "([BIII)I",
- (void*)Java_android_drm_mobile1_DrmRawContent_nativeReadContent},
- {"nativeGetContentType", "()Ljava/lang/String;",
- (void*)Java_android_drm_mobile1_DrmRawContent_nativeGetContentType},
- {"nativeGetContentLength", "()I",
- (void*)Java_android_drm_mobile1_DrmRawContent_nativeGetContentLength},
- {"finalize", "()V",
- (void*)Java_android_drm_mobile1_DrmRawContent_finalize},
-};
-
-/*
- * Table of methods associated with the DrmRights class.
- */
-static JNINativeMethod gDrmRightsMethods[] = {
- /* name, signature, funcPtr */
- {"nativeGetConstraintInfo", "(ILandroid/drm/mobile1/DrmConstraintInfo;)I",
- (void*)Java_android_drm_mobile1_DrmRights_nativeGetConstraintInfo},
- {"nativeConsumeRights", "(I)I",
- (void*)Java_android_drm_mobile1_DrmRights_nativeConsumeRights},
-};
-
-/*
- * Table of methods associated with the DrmRightsManager class.
- */
-static JNINativeMethod gDrmRightsManagerMethods[] = {
- /* name, signature, funcPtr */
- {"nativeInstallDrmRights", "(Ljava/io/InputStream;IILandroid/drm/mobile1/DrmRights;)I",
- (void*)Java_android_drm_mobile1_DrmRightsManager_nativeInstallDrmRights},
- {"nativeQueryRights", "(Landroid/drm/mobile1/DrmRawContent;Landroid/drm/mobile1/DrmRights;)I",
- (void*)Java_android_drm_mobile1_DrmRightsManager_nativeQueryRights},
- {"nativeGetNumOfRights", "()I",
- (void*)Java_android_drm_mobile1_DrmRightsManager_nativeGetNumOfRights},
- {"nativeGetRightsList", "([Landroid/drm/mobile1/DrmRights;I)I",
- (void*)Java_android_drm_mobile1_DrmRightsManager_nativeGetRightsList},
- {"nativeDeleteRights", "(Landroid/drm/mobile1/DrmRights;)I",
- (void*)Java_android_drm_mobile1_DrmRightsManager_nativeDeleteRights},
-};
-
-/*
- * Register several native methods for one class.
- */
-static int registerNativeMethods(JNIEnv* env, const char* className,
- JNINativeMethod* gMethods, int numMethods)
-{
- jclass clazz;
-
- clazz = (*env)->FindClass(env, className);
- if (clazz == NULL)
- return JNI_FALSE;
-
- if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)
- return JNI_FALSE;
-
- return JNI_TRUE;
-}
-
-/*
- * Register native methods for all classes we know about.
- */
-static int registerNatives(JNIEnv* env)
-{
- if (!registerNativeMethods(env, "android/drm/mobile1/DrmRawContent",
- gDrmRawContentMethods, sizeof(gDrmRawContentMethods) / sizeof(gDrmRawContentMethods[0])))
- return JNI_FALSE;
-
- if (!registerNativeMethods(env, "android/drm/mobile1/DrmRights",
- gDrmRightsMethods, sizeof(gDrmRightsMethods) / sizeof(gDrmRightsMethods[0])))
- return JNI_FALSE;
-
- if (!registerNativeMethods(env, "android/drm/mobile1/DrmRightsManager",
- gDrmRightsManagerMethods, sizeof(gDrmRightsManagerMethods) / sizeof(gDrmRightsManagerMethods[0])))
- return JNI_FALSE;
-
- return JNI_TRUE;
-}
-
-jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
- JNIEnv* env = NULL;
- jint result = -1;
-
- printf("Entering JNI_OnLoad\n");
-
- if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
- goto bail;
-
- assert(env != NULL);
-
- if (!registerNatives(env))
- goto bail;
-
- /* success -- return valid version number */
- result = JNI_VERSION_1_4;
-
-bail:
- printf("Leaving JNI_OnLoad (result=0x%x)\n", result);
- return result;
-}
diff --git a/media/libdrm/mobile1/src/objmng/drm_api.c b/media/libdrm/mobile1/src/objmng/drm_api.c
deleted file mode 100644
index 232d9f4..0000000
--- a/media/libdrm/mobile1/src/objmng/drm_api.c
+++ /dev/null
@@ -1,1943 +0,0 @@
-/*
- * 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.
- */
-
-#include <svc_drm.h>
-#include <drm_inner.h>
-#include <parser_dm.h>
-#include <parser_dcf.h>
-#include <parser_rel.h>
-#include <drm_rights_manager.h>
-#include <drm_time.h>
-#include <drm_decoder.h>
-#include "log.h"
-
-/**
- * Current id.
- */
-static int32_t curID = 0;
-
-/**
- * The header pointer for the session list.
- */
-static T_DRM_Session_Node* sessionTable = NULL;
-
-/**
- * New a session.
- */
-static T_DRM_Session_Node* newSession(T_DRM_Input_Data data)
-{
- T_DRM_Session_Node* s = (T_DRM_Session_Node *)malloc(sizeof(T_DRM_Session_Node));
-
- if (NULL != s) {
- memset(s, 0, sizeof(T_DRM_Session_Node));
-
- s->sessionId = curID++;
- s->inputHandle = data.inputHandle;
- s->mimeType = data.mimeType;
- s->getInputDataLengthFunc = data.getInputDataLength;
- s->readInputDataFunc = data.readInputData;
- s->seekInputDataFunc = data.seekInputData;
- }
-
- return s;
-}
-
-/**
- * Free a session.
- */
-static void freeSession(T_DRM_Session_Node* s)
-{
- if (NULL == s)
- return;
-
- if (NULL != s->rawContent)
- free(s->rawContent);
-
- if (NULL != s->readBuf)
- free(s->readBuf);
-
- if (NULL != s->infoStruct)
- free(s->infoStruct);
-
- free(s);
-}
-
-/**
- * Add a session to list.
- */
-static int32_t addSession(T_DRM_Session_Node* s)
-{
- if (NULL == s)
- return -1;
-
- s->next = sessionTable;
- sessionTable = s;
-
- return s->sessionId;
-}
-
-/**
- * Get a session from the list.
- */
-static T_DRM_Session_Node* getSession(int32_t sessionId)
-{
- T_DRM_Session_Node* s;
-
- if (sessionId < 0 || NULL == sessionTable)
- return NULL;
-
- for (s = sessionTable; s != NULL; s = s->next) {
- if (sessionId == s->sessionId)
- return s;
- }
-
- return NULL;
-}
-
-/**
- * Remove a session from the list.
- */
-static void removeSession(int32_t sessionId)
-{
- T_DRM_Session_Node *curS, *preS;
-
- if (sessionId < 0 || NULL == sessionTable)
- return;
-
- if (sessionId == sessionTable->sessionId) {
- curS = sessionTable;
- sessionTable = curS->next;
- freeSession(curS);
- return;
- }
-
- for (preS = sessionTable; preS->next != NULL; preS = preS->next) {
- if (preS->next->sessionId == sessionId)
- curS = preS->next;
- }
-
- if (NULL == preS->next)
- return;
-
- preS->next = curS->next;
- freeSession(curS);
-}
-
-/**
- * Try to identify the mimetype according the input DRM data.
- */
-static int32_t getMimeType(const uint8_t *buf, int32_t bufLen)
-{
- const uint8_t *p;
-
- if (NULL == buf || bufLen <= 0)
- return TYPE_DRM_UNKNOWN;
-
- p = buf;
-
- /* check if it is DRM Content Format, only check the first field of Version, it must be "0x01" */
- if (0x01 == *p)
- return TYPE_DRM_CONTENT;
-
- /* check if it is DRM Message, only check the first two bytes, it must be the start flag of boundary: "--" */
- if (bufLen >= 2 && '-' == *p && '-' == *(p + 1))
- return TYPE_DRM_MESSAGE;
-
- /* check if it is DRM Rights XML format, only check the first several bytes, it must be: "<o-ex:rights" */
- if (bufLen >= 12 && 0 == strncmp("<o-ex:rights", (char *)p, 12))
- return TYPE_DRM_RIGHTS_XML;
-
- /* check if it is DRM Rights WBXML format, only check the first two bytes, it must be: 0x03, 0x0e */
- if (bufLen >= 2 && 0x03 == *p && 0x0e == *(p + 1))
- return TYPE_DRM_RIGHTS_WBXML;
-
- return TYPE_DRM_UNKNOWN;
-}
-
-static int32_t drm_skipCRLFinB64(const uint8_t* b64Data, int32_t len)
-{
- const uint8_t* p;
- int32_t skipLen = 0;
-
- if (NULL == b64Data || len <= 0)
- return -1;
-
- p = b64Data;
- while (p - b64Data < len) {
- if ('\r' == *p || '\n'== *p)
- skipLen++;
- p++;
- }
-
- return skipLen;
-}
-
-static int32_t drm_scanEndBoundary(const uint8_t* pBuf, int32_t len, uint8_t* const boundary)
-{
- const uint8_t* p;
- int32_t leftLen;
- int32_t boundaryLen;
-
- if (NULL == pBuf || len <=0 || NULL == boundary)
- return -1;
-
- p = pBuf;
- boundaryLen = strlen((char *)boundary) + 2; /* 2 means: '\r' and '\n' */
- leftLen = len - (p - pBuf);
- while (leftLen > 0) {
- if (NULL == (p = memchr(p, '\r', leftLen)))
- break;
-
- leftLen = len - (p - pBuf);
- if (leftLen < boundaryLen)
- return -2; /* here means may be the boundary has been split */
-
- if (('\n' == *(p + 1)) && (0 == memcmp(p + 2, boundary, strlen((char *)boundary))))
- return p - pBuf; /* find the boundary here */
-
- p++;
- leftLen--;
- }
-
- return len; /* no boundary found */
-}
-
-static int32_t drm_getLicenseInfo(T_DRM_Rights* pRights, T_DRM_Rights_Info* licenseInfo)
-{
- if (NULL != licenseInfo && NULL != pRights) {
- strcpy((char *)licenseInfo->roId, (char *)pRights->uid);
-
- if (1 == pRights->bIsDisplayable) {
- licenseInfo->displayRights.indicator = pRights->DisplayConstraint.Indicator;
- licenseInfo->displayRights.count =
- pRights->DisplayConstraint.Count;
- licenseInfo->displayRights.startDate =
- pRights->DisplayConstraint.StartTime.date;
- licenseInfo->displayRights.startTime =
- pRights->DisplayConstraint.StartTime.time;
- licenseInfo->displayRights.endDate =
- pRights->DisplayConstraint.EndTime.date;
- licenseInfo->displayRights.endTime =
- pRights->DisplayConstraint.EndTime.time;
- licenseInfo->displayRights.intervalDate =
- pRights->DisplayConstraint.Interval.date;
- licenseInfo->displayRights.intervalTime =
- pRights->DisplayConstraint.Interval.time;
- }
- if (1 == pRights->bIsPlayable) {
- licenseInfo->playRights.indicator = pRights->PlayConstraint.Indicator;
- licenseInfo->playRights.count = pRights->PlayConstraint.Count;
- licenseInfo->playRights.startDate =
- pRights->PlayConstraint.StartTime.date;
- licenseInfo->playRights.startTime =
- pRights->PlayConstraint.StartTime.time;
- licenseInfo->playRights.endDate =
- pRights->PlayConstraint.EndTime.date;
- licenseInfo->playRights.endTime =
- pRights->PlayConstraint.EndTime.time;
- licenseInfo->playRights.intervalDate =
- pRights->PlayConstraint.Interval.date;
- licenseInfo->playRights.intervalTime =
- pRights->PlayConstraint.Interval.time;
- }
- if (1 == pRights->bIsExecuteable) {
- licenseInfo->executeRights.indicator = pRights->ExecuteConstraint.Indicator;
- licenseInfo->executeRights.count =
- pRights->ExecuteConstraint.Count;
- licenseInfo->executeRights.startDate =
- pRights->ExecuteConstraint.StartTime.date;
- licenseInfo->executeRights.startTime =
- pRights->ExecuteConstraint.StartTime.time;
- licenseInfo->executeRights.endDate =
- pRights->ExecuteConstraint.EndTime.date;
- licenseInfo->executeRights.endTime =
- pRights->ExecuteConstraint.EndTime.time;
- licenseInfo->executeRights.intervalDate =
- pRights->ExecuteConstraint.Interval.date;
- licenseInfo->executeRights.intervalTime =
- pRights->ExecuteConstraint.Interval.time;
- }
- if (1 == pRights->bIsPrintable) {
- licenseInfo->printRights.indicator = pRights->PrintConstraint.Indicator;
- licenseInfo->printRights.count =
- pRights->PrintConstraint.Count;
- licenseInfo->printRights.startDate =
- pRights->PrintConstraint.StartTime.date;
- licenseInfo->printRights.startTime =
- pRights->PrintConstraint.StartTime.time;
- licenseInfo->printRights.endDate =
- pRights->PrintConstraint.EndTime.date;
- licenseInfo->printRights.endTime =
- pRights->PrintConstraint.EndTime.time;
- licenseInfo->printRights.intervalDate =
- pRights->PrintConstraint.Interval.date;
- licenseInfo->printRights.intervalTime =
- pRights->PrintConstraint.Interval.time;
- }
- return TRUE;
- }
- return FALSE;
-}
-
-static int32_t drm_addRightsNodeToList(T_DRM_Rights_Info_Node **ppRightsHeader,
- T_DRM_Rights_Info_Node *pInputRightsNode)
-{
- T_DRM_Rights_Info_Node *pRightsNode;
-
- if (NULL == ppRightsHeader || NULL == pInputRightsNode)
- return FALSE;
-
- pRightsNode = (T_DRM_Rights_Info_Node *)malloc(sizeof(T_DRM_Rights_Info_Node));
- if (NULL == pRightsNode)
- return FALSE;
-
- memcpy(pRightsNode, pInputRightsNode, sizeof(T_DRM_Rights_Info_Node));
- pRightsNode->next = NULL;
-
- /* this means it is the first node */
- if (NULL == *ppRightsHeader)
- *ppRightsHeader = pRightsNode;
- else {
- T_DRM_Rights_Info_Node *pTmp;
-
- pTmp = *ppRightsHeader;
- while (NULL != pTmp->next)
- pTmp = pTmp->next;
-
- pTmp->next = pRightsNode;
- }
- return TRUE;
-}
-
-static int32_t drm_startConsumeRights(int32_t * bIsXXable,
- T_DRM_Rights_Constraint * XXConstraint,
- int32_t * writeFlag)
-{
- T_DB_TIME_SysTime curDateTime;
- T_DRM_DATETIME CurrentTime;
- uint8_t countFlag = 0;
-
- memset(&CurrentTime, 0, sizeof(T_DRM_DATETIME));
-
- if (NULL == bIsXXable || 0 == *bIsXXable || NULL == XXConstraint || NULL == writeFlag)
- return DRM_FAILURE;
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_NO_CONSTRAINT)) /* Have utter right? */
- return DRM_SUCCESS;
-
- *bIsXXable = 0; /* Assume have invalid rights at first */
- *writeFlag = 0;
-
- if (0 != (XXConstraint->Indicator & (DRM_START_TIME_CONSTRAINT | DRM_END_TIME_CONSTRAINT | DRM_INTERVAL_CONSTRAINT))) {
- DRM_time_getSysTime(&curDateTime);
-
- if (-1 == drm_checkDate(curDateTime.year, curDateTime.month, curDateTime.day,
- curDateTime.hour, curDateTime.min, curDateTime.sec))
- return DRM_FAILURE;
-
- YMD_HMS_2_INT(curDateTime.year, curDateTime.month, curDateTime.day,
- CurrentTime.date, curDateTime.hour, curDateTime.min,
- curDateTime.sec, CurrentTime.time);
- }
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_COUNT_CONSTRAINT)) { /* Have count restrict? */
- *writeFlag = 1;
- /* If it has only one time for use, after use this function, we will delete this rights */
- if (XXConstraint->Count <= 0) {
- XXConstraint->Indicator &= ~DRM_COUNT_CONSTRAINT;
- return DRM_RIGHTS_EXPIRED;
- }
-
- if (XXConstraint->Count-- <= 1) {
- XXConstraint->Indicator &= ~DRM_COUNT_CONSTRAINT;
- countFlag = 1;
- }
- }
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_START_TIME_CONSTRAINT)) {
- if (XXConstraint->StartTime.date > CurrentTime.date ||
- (XXConstraint->StartTime.date == CurrentTime.date &&
- XXConstraint->StartTime.time >= CurrentTime.time)) {
- *bIsXXable = 1;
- return DRM_RIGHTS_PENDING;
- }
- }
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_END_TIME_CONSTRAINT)) { /* Have end time restrict? */
- if (XXConstraint->EndTime.date < CurrentTime.date ||
- (XXConstraint->EndTime.date == CurrentTime.date &&
- XXConstraint->EndTime.time <= CurrentTime.time)) {
- *writeFlag = 1;
- XXConstraint->Indicator &= ~DRM_END_TIME_CONSTRAINT;
- return DRM_RIGHTS_EXPIRED;
- }
- }
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_INTERVAL_CONSTRAINT)) { /* Have interval time restrict? */
- int32_t year, mon, day, hour, min, sec, date, time;
- int32_t ret;
-
- XXConstraint->Indicator |= DRM_END_TIME_CONSTRAINT;
- XXConstraint->Indicator &= ~DRM_INTERVAL_CONSTRAINT; /* Write off interval right */
- *writeFlag = 1;
-
- if (XXConstraint->Interval.date == 0
- && XXConstraint->Interval.time == 0) {
- return DRM_RIGHTS_EXPIRED;
- }
- date = CurrentTime.date + XXConstraint->Interval.date;
- time = CurrentTime.time + XXConstraint->Interval.time;
- INT_2_YMD_HMS(year, mon, day, date, hour, min, sec, time);
-
- if (sec > 59) {
- min += sec / 60;
- sec %= 60;
- }
- if (min > 59) {
- hour += min / 60;
- min %= 60;
- }
- if (hour > 23) {
- day += hour / 24;
- hour %= 24;
- }
- if (day > 31) {
- mon += day / 31;
- day %= 31;
- }
- if (mon > 12) {
- year += mon / 12;
- mon %= 12;
- }
- if (day > (ret = drm_monthDays(year, mon))) {
- day -= ret;
- mon++;
- if (mon > 12) {
- mon -= 12;
- year++;
- }
- }
- YMD_HMS_2_INT(year, mon, day, XXConstraint->EndTime.date, hour,
- min, sec, XXConstraint->EndTime.time);
- }
-
- if (1 != countFlag)
- *bIsXXable = 1; /* Can go here ,so right must be valid */
- return DRM_SUCCESS;
-}
-
-static int32_t drm_startCheckRights(int32_t * bIsXXable,
- T_DRM_Rights_Constraint * XXConstraint)
-{
- T_DB_TIME_SysTime curDateTime;
- T_DRM_DATETIME CurrentTime;
-
- memset(&CurrentTime, 0, sizeof(T_DRM_DATETIME));
-
- if (NULL == bIsXXable || 0 == *bIsXXable || NULL == XXConstraint)
- return DRM_FAILURE;
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_NO_CONSTRAINT)) /* Have utter right? */
- return DRM_SUCCESS;
-
- *bIsXXable = 0; /* Assume have invalid rights at first */
-
- if (0 != (XXConstraint->Indicator & (DRM_START_TIME_CONSTRAINT | DRM_END_TIME_CONSTRAINT))) {
- DRM_time_getSysTime(&curDateTime);
-
- if (-1 == drm_checkDate(curDateTime.year, curDateTime.month, curDateTime.day,
- curDateTime.hour, curDateTime.min, curDateTime.sec))
- return DRM_FAILURE;
-
- YMD_HMS_2_INT(curDateTime.year, curDateTime.month, curDateTime.day,
- CurrentTime.date, curDateTime.hour, curDateTime.min,
- curDateTime.sec, CurrentTime.time);
- }
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_COUNT_CONSTRAINT)) { /* Have count restrict? */
- if (XXConstraint->Count <= 0) {
- XXConstraint->Indicator &= ~DRM_COUNT_CONSTRAINT;
- return DRM_RIGHTS_EXPIRED;
- }
- }
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_START_TIME_CONSTRAINT)) {
- if (XXConstraint->StartTime.date > CurrentTime.date ||
- (XXConstraint->StartTime.date == CurrentTime.date &&
- XXConstraint->StartTime.time >= CurrentTime.time)) {
- *bIsXXable = 1;
- return DRM_RIGHTS_PENDING;
- }
- }
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_END_TIME_CONSTRAINT)) { /* Have end time restrict? */
- if (XXConstraint->EndTime.date < CurrentTime.date ||
- (XXConstraint->EndTime.date == CurrentTime.date &&
- XXConstraint->EndTime.time <= CurrentTime.time)) {
- XXConstraint->Indicator &= ~DRM_END_TIME_CONSTRAINT;
- return DRM_RIGHTS_EXPIRED;
- }
- }
-
- if (0 != (uint8_t)(XXConstraint->Indicator & DRM_INTERVAL_CONSTRAINT)) { /* Have interval time restrict? */
- if (XXConstraint->Interval.date == 0 && XXConstraint->Interval.time == 0) {
- XXConstraint->Indicator &= ~DRM_INTERVAL_CONSTRAINT;
- return DRM_RIGHTS_EXPIRED;
- }
- }
-
- *bIsXXable = 1;
- return DRM_SUCCESS;
-}
-
-int32_t drm_checkRoAndUpdate(int32_t id, int32_t permission)
-{
- int32_t writeFlag = 0;
- int32_t roAmount;
- int32_t validRoAmount = 0;
- int32_t flag = DRM_FAILURE;
- int32_t i, j;
- T_DRM_Rights *pRo;
- T_DRM_Rights *pCurRo;
- int32_t * pNumOfPriority;
- int32_t iNum;
- T_DRM_Rights_Constraint * pCurConstraint;
- T_DRM_Rights_Constraint * pCompareConstraint;
- int priority[8] = {1, 2, 4, 3, 8, 6, 7, 5};
-
- if (FALSE == drm_writeOrReadInfo(id, NULL, &roAmount, GET_ROAMOUNT))
- return DRM_FAILURE;
-
- validRoAmount = roAmount;
- if (roAmount < 1)
- return DRM_NO_RIGHTS;
-
- pRo = malloc(roAmount * sizeof(T_DRM_Rights));
- pCurRo = pRo;
- if (NULL == pRo)
- return DRM_FAILURE;
-
- if (FALSE == drm_writeOrReadInfo(id, pRo, &roAmount, GET_ALL_RO)) {
- free(pRo);
- return DRM_FAILURE;
- }
-
- /** check the right priority */
- pNumOfPriority = malloc(sizeof(int32_t) * roAmount);
- for(i = 0; i < roAmount; i++) {
- iNum = roAmount - 1;
- for(j = 0; j < roAmount; j++) {
- if(i == j)
- continue;
- switch(permission) {
- case DRM_PERMISSION_PLAY:
- pCurConstraint = &pRo[i].PlayConstraint;
- pCompareConstraint = &pRo[j].PlayConstraint;
- break;
- case DRM_PERMISSION_DISPLAY:
- pCurConstraint = &pRo[i].DisplayConstraint;
- pCompareConstraint = &pRo[j].DisplayConstraint;
- break;
- case DRM_PERMISSION_EXECUTE:
- pCurConstraint = &pRo[i].ExecuteConstraint;
- pCompareConstraint = &pRo[j].ExecuteConstraint;
- break;
- case DRM_PERMISSION_PRINT:
- pCurConstraint = &pRo[i].PrintConstraint;
- pCompareConstraint = &pRo[j].PrintConstraint;
- break;
- default:
- free(pRo);
- free(pNumOfPriority);
- return DRM_FAILURE;
- }
-
- /**get priority by Indicator*/
- if(0 == (pCurConstraint->Indicator & DRM_NO_CONSTRAINT) &&
- 0 == (pCompareConstraint->Indicator & DRM_NO_CONSTRAINT)) {
- int num1, num2;
- num1 = (pCurConstraint->Indicator & 0x0e) >> 1;
- num2 = (pCompareConstraint->Indicator & 0x0e) >> 1;
- if(priority[num1] > priority[num2]) {
- iNum--;
- continue;
- } else if(priority[pCurConstraint->Indicator] < priority[pCompareConstraint->Indicator])
- continue;
- } else if(pCurConstraint->Indicator > pCompareConstraint->Indicator) {
- iNum--;
- continue;
- } else if(pCurConstraint->Indicator < pCompareConstraint->Indicator)
- continue;
-
- if(0 != (pCurConstraint->Indicator & DRM_END_TIME_CONSTRAINT)) {
- if(pCurConstraint->EndTime.date < pCompareConstraint->EndTime.date) {
- iNum--;
- continue;
- } else if(pCurConstraint->EndTime.date > pCompareConstraint->EndTime.date)
- continue;
-
- if(pCurConstraint->EndTime.time < pCompareConstraint->EndTime.time) {
- iNum--;
- continue;
- } else if(pCurConstraint->EndTime.date > pCompareConstraint->EndTime.date)
- continue;
- }
-
- if(0 != (pCurConstraint->Indicator & DRM_INTERVAL_CONSTRAINT)) {
- if(pCurConstraint->Interval.date < pCompareConstraint->Interval.date) {
- iNum--;
- continue;
- } else if(pCurConstraint->Interval.date > pCompareConstraint->Interval.date)
- continue;
-
- if(pCurConstraint->Interval.time < pCompareConstraint->Interval.time) {
- iNum--;
- continue;
- } else if(pCurConstraint->Interval.time > pCompareConstraint->Interval.time)
- continue;
- }
-
- if(0 != (pCurConstraint->Indicator & DRM_COUNT_CONSTRAINT)) {
- if(pCurConstraint->Count < pCompareConstraint->Count) {
- iNum--;
- continue;
- } else if(pCurConstraint->Count > pCompareConstraint->Count)
- continue;
- }
-
- if(i < j)
- iNum--;
- }
- pNumOfPriority[iNum] = i;
- }
-
- for (i = 0; i < validRoAmount; i++) {
- /** check the right priority */
- if (pNumOfPriority[i] >= validRoAmount)
- break;
-
- pCurRo = pRo + pNumOfPriority[i];
-
- switch (permission) {
- case DRM_PERMISSION_PLAY:
- flag =
- drm_startConsumeRights(&pCurRo->bIsPlayable,
- &pCurRo->PlayConstraint, &writeFlag);
- break;
- case DRM_PERMISSION_DISPLAY:
- flag =
- drm_startConsumeRights(&pCurRo->bIsDisplayable,
- &pCurRo->DisplayConstraint,
- &writeFlag);
- break;
- case DRM_PERMISSION_EXECUTE:
- flag =
- drm_startConsumeRights(&pCurRo->bIsExecuteable,
- &pCurRo->ExecuteConstraint,
- &writeFlag);
- break;
- case DRM_PERMISSION_PRINT:
- flag =
- drm_startConsumeRights(&pCurRo->bIsPrintable,
- &pCurRo->PrintConstraint, &writeFlag);
- break;
- default:
- free(pNumOfPriority);
- free(pRo);
- return DRM_FAILURE;
- }
-
- /* Here confirm the valid RO amount and set the writeFlag */
- if (0 == pCurRo->bIsPlayable && 0 == pCurRo->bIsDisplayable &&
- 0 == pCurRo->bIsExecuteable && 0 == pCurRo->bIsPrintable) {
- int32_t iCurPri;
-
- /** refresh the right priority */
- iCurPri = pNumOfPriority[i];
- for(j = i; j < validRoAmount - 1; j++)
- pNumOfPriority[j] = pNumOfPriority[j + 1];
-
- if(iCurPri != validRoAmount - 1) {
- memcpy(pCurRo, pRo + validRoAmount - 1,
- sizeof(T_DRM_Rights));
- for(j = 0; j < validRoAmount -1; j++) {
- if(validRoAmount - 1 == pNumOfPriority[j])
- pNumOfPriority[j] = iCurPri;
- }
- }
-
- /* Here means it is not the last one RO, so the invalid RO should be deleted */
- writeFlag = 1;
- validRoAmount--; /* If current right is invalid */
- i--;
- }
-
- /* If the flag is TRUE, this means: we have found a valid RO, so break, no need to check other RO */
- if (DRM_SUCCESS == flag)
- break;
- }
-
- if (1 == writeFlag) {
- /* Delete the *.info first */
- //drm_removeIdInfoFile(id);
-
- if (FALSE == drm_writeOrReadInfo(id, pRo, &validRoAmount, SAVE_ALL_RO))
- flag = DRM_FAILURE;
- }
-
- free(pNumOfPriority);
- free(pRo);
- return flag;
-}
-
-
-/* see svc_drm.h */
-int32_t SVC_drm_installRights(T_DRM_Input_Data data, T_DRM_Rights_Info* pRightsInfo)
-{
- uint8_t *buf;
- int32_t dataLen, bufLen;
- T_DRM_Rights rights;
-
- if (0 == data.inputHandle)
- return DRM_RIGHTS_DATA_INVALID;
-
- /* Get input rights data length */
- dataLen = data.getInputDataLength(data.inputHandle);
- if (dataLen <= 0)
- return DRM_RIGHTS_DATA_INVALID;
-
- /* Check if the length is larger than DRM max malloc length */
- if (dataLen > DRM_MAX_MALLOC_LEN)
- bufLen = DRM_MAX_MALLOC_LEN;
- else
- bufLen = dataLen;
-
- buf = (uint8_t *)malloc(bufLen);
- if (NULL == buf)
- return DRM_FAILURE;
-
- /* Read input data to buffer */
- if (0 >= data.readInputData(data.inputHandle, buf, bufLen)) {
- free(buf);
- return DRM_RIGHTS_DATA_INVALID;
- }
-
- /* if the input mime type is unknown, DRM engine will try to recognize it. */
- if (TYPE_DRM_UNKNOWN == data.mimeType)
- data.mimeType = getMimeType(buf, bufLen);
-
- switch(data.mimeType) {
- case TYPE_DRM_MESSAGE: /* in case of Combined Delivery, extract the rights part to install */
- {
- T_DRM_DM_Info dmInfo;
-
- memset(&dmInfo, 0, sizeof(T_DRM_DM_Info));
- if (FALSE == drm_parseDM(buf, bufLen, &dmInfo)) {
- free(buf);
- return DRM_RIGHTS_DATA_INVALID;
- }
-
- /* if it is not Combined Delivery, it can not use to "SVC_drm_installRights" */
- if (COMBINED_DELIVERY != dmInfo.deliveryType || dmInfo.rightsOffset <= 0 || dmInfo.rightsLen <= 0) {
- free(buf);
- return DRM_RIGHTS_DATA_INVALID;
- }
-
- memset(&rights, 0, sizeof(T_DRM_Rights));
- if (FALSE == drm_relParser(buf + dmInfo.rightsOffset, dmInfo.rightsLen, TYPE_DRM_RIGHTS_XML, &rights)) {
- free(buf);
- return DRM_RIGHTS_DATA_INVALID;
- }
- }
- break;
- case TYPE_DRM_RIGHTS_XML:
- case TYPE_DRM_RIGHTS_WBXML:
- memset(&rights, 0, sizeof(T_DRM_Rights));
- if (FALSE == drm_relParser(buf, bufLen, data.mimeType, &rights)) {
- free(buf);
- return DRM_RIGHTS_DATA_INVALID;
- }
- break;
- case TYPE_DRM_CONTENT: /* DCF should not using "SVC_drm_installRights", it should be used to open a session. */
- case TYPE_DRM_UNKNOWN:
- default:
- free(buf);
- return DRM_MEDIA_DATA_INVALID;
- }
-
- free(buf);
-
- /* append the rights information to DRM engine storage */
- if (FALSE == drm_appendRightsInfo(&rights))
- return DRM_FAILURE;
-
- memset(pRightsInfo, 0, sizeof(T_DRM_Rights_Info));
- drm_getLicenseInfo(&rights, pRightsInfo);
-
- return DRM_SUCCESS;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_openSession(T_DRM_Input_Data data)
-{
- int32_t session;
- int32_t dataLen;
- T_DRM_Session_Node* s;
-
- if (0 == data.inputHandle)
- return DRM_MEDIA_DATA_INVALID;
-
- /* Get input data length */
- dataLen = data.getInputDataLength(data.inputHandle);
- if (dataLen <= 0)
- return DRM_MEDIA_DATA_INVALID;
-
- s = newSession(data);
- if (NULL == s)
- return DRM_FAILURE;
-
- /* Check if the length is larger than DRM max malloc length */
- if (dataLen > DRM_MAX_MALLOC_LEN)
- s->rawContentLen = DRM_MAX_MALLOC_LEN;
- else
- s->rawContentLen = dataLen;
-
- s->rawContent = (uint8_t *)malloc(s->rawContentLen);
- if (NULL == s->rawContent)
- return DRM_FAILURE;
-
- /* Read input data to buffer */
- if (0 >= data.readInputData(data.inputHandle, s->rawContent, s->rawContentLen)) {
- freeSession(s);
- return DRM_MEDIA_DATA_INVALID;
- }
-
- /* if the input mime type is unknown, DRM engine will try to recognize it. */
- if (TYPE_DRM_UNKNOWN == data.mimeType)
- data.mimeType = getMimeType(s->rawContent, s->rawContentLen);
-
- switch(data.mimeType) {
- case TYPE_DRM_MESSAGE:
- {
- T_DRM_DM_Info dmInfo;
-
- memset(&dmInfo, 0, sizeof(T_DRM_DM_Info));
- if (FALSE == drm_parseDM(s->rawContent, s->rawContentLen, &dmInfo)) {
- freeSession(s);
- return DRM_MEDIA_DATA_INVALID;
- }
-
- s->deliveryMethod = dmInfo.deliveryType;
-
- if (SEPARATE_DELIVERY_FL == s->deliveryMethod)
- s->contentLength = DRM_UNKNOWN_DATA_LEN;
- else
- s->contentLength = dmInfo.contentLen;
-
- s->transferEncoding = dmInfo.transferEncoding;
- s->contentOffset = dmInfo.contentOffset;
- s->bEndData = FALSE;
- strcpy((char *)s->contentType, (char *)dmInfo.contentType);
- strcpy((char *)s->contentID, (char *)dmInfo.contentID);
-
- if (SEPARATE_DELIVERY_FL == s->deliveryMethod) {
- s->infoStruct = (T_DRM_Dcf_Node *)malloc(sizeof(T_DRM_Dcf_Node));
- if (NULL == s->infoStruct)
- return DRM_FAILURE;
- memset(s->infoStruct, 0, sizeof(T_DRM_Dcf_Node));
-
- ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength = dmInfo.contentLen;
- strcpy((char *)((T_DRM_Dcf_Node *)(s->infoStruct))->rightsIssuer, (char *)dmInfo.rightsIssuer);
- break;
- }
-
- if (DRM_MESSAGE_CODING_BASE64 == s->transferEncoding) {
- s->infoStruct = (T_DRM_DM_Base64_Node *)malloc(sizeof(T_DRM_DM_Base64_Node));
- if (NULL == s->infoStruct)
- return DRM_FAILURE;
- memset(s->infoStruct, 0, sizeof(T_DRM_DM_Base64_Node));
-
- strcpy((char *)((T_DRM_DM_Base64_Node *)(s->infoStruct))->boundary, (char *)dmInfo.boundary);
- } else {
- s->infoStruct = (T_DRM_DM_Binary_Node *)malloc(sizeof(T_DRM_DM_Binary_Node));
- if (NULL == s->infoStruct)
- return DRM_FAILURE;
- memset(s->infoStruct, 0, sizeof(T_DRM_DM_Binary_Node));
-
- strcpy((char *)((T_DRM_DM_Binary_Node *)(s->infoStruct))->boundary, (char *)dmInfo.boundary);
- }
-
-
- if (DRM_MESSAGE_CODING_BASE64 == s->transferEncoding) {
- if (s->contentLength > 0) {
- int32_t encLen, decLen;
-
- encLen = s->contentLength;
- decLen = encLen / DRM_B64_ENC_BLOCK * DRM_B64_DEC_BLOCK;
-
- decLen = drm_decodeBase64(s->rawContent, decLen, s->rawContent + s->contentOffset, &encLen);
- s->contentLength = decLen;
- } else {
- int32_t encLen = DRM_MAX_MALLOC_LEN - s->contentOffset, decLen;
- int32_t skipLen, needBytes, i;
- uint8_t *pStart;
- int32_t res, bFoundBoundary = FALSE;
-
- pStart = s->rawContent + s->contentOffset;
- if (-1 == (skipLen = drm_skipCRLFinB64(pStart, encLen))) {
- freeSession(s);
- return DRM_FAILURE;
- }
-
- needBytes = DRM_B64_ENC_BLOCK - ((encLen - skipLen) % DRM_B64_ENC_BLOCK);
- if (needBytes < DRM_B64_ENC_BLOCK) {
- s->rawContent = (uint8_t *)realloc(s->rawContent, DRM_MAX_MALLOC_LEN + needBytes);
- if (NULL == s->rawContent) {
- freeSession(s);
- return DRM_FAILURE;
- }
-
- i = 0;
- while (i < needBytes) {
- if (-1 != data.readInputData(data.inputHandle, s->rawContent + DRM_MAX_MALLOC_LEN + i, 1)) {
- if ('\r' == *(s->rawContent + DRM_MAX_MALLOC_LEN + i) || '\n' == *(s->rawContent + DRM_MAX_MALLOC_LEN + i))
- continue;
- i++;
- } else
- break;
- }
- encLen += i;
- }
-
- res = drm_scanEndBoundary(pStart, encLen, ((T_DRM_DM_Base64_Node *)(s->infoStruct))->boundary);
- if (-1 == res) {
- freeSession(s);
- return DRM_FAILURE;
- }
- if (-2 == res) { /* may be there is a boundary */
- int32_t boundaryLen, leftLen, readBytes;
- char* pTmp = memrchr(pStart, '\r', encLen);
-
- if (NULL == pTmp) {
- freeSession(s);
- return DRM_FAILURE; /* conflict */
- }
- boundaryLen = strlen((char *)((T_DRM_DM_Base64_Node *)(s->infoStruct))->boundary) + 2; /* 2 means: '\r''\n' */
- s->readBuf = (uint8_t *)malloc(boundaryLen);
- if (NULL == s->readBuf) {
- freeSession(s);
- return DRM_FAILURE;
- }
- s->readBufOff = encLen - ((uint8_t *)pTmp - pStart);
- s->readBufLen = boundaryLen - s->readBufOff;
- memcpy(s->readBuf, pTmp, s->readBufOff);
- readBytes = data.readInputData(data.inputHandle, s->readBuf + s->readBufOff, s->readBufLen);
- if (-1 == readBytes || readBytes < s->readBufLen) {
- freeSession(s);
- return DRM_MEDIA_DATA_INVALID;
- }
-
- if (0 == drm_scanEndBoundary(s->readBuf, boundaryLen, ((T_DRM_DM_Base64_Node *)(s->infoStruct))->boundary)) {
- encLen = (uint8_t *)pTmp - pStart; /* yes, it is the end boundary */
- bFoundBoundary = TRUE;
- }
- } else {
- if (res >= 0 && res < encLen) {
- encLen = res;
- bFoundBoundary = TRUE;
- }
- }
-
- decLen = encLen / DRM_B64_ENC_BLOCK * DRM_B64_DEC_BLOCK;
- decLen = drm_decodeBase64(s->rawContent, decLen, s->rawContent + s->contentOffset, &encLen);
- ((T_DRM_DM_Base64_Node *)(s->infoStruct))->b64DecodeDataLen = decLen;
- if (bFoundBoundary)
- s->contentLength = decLen;
- }
- } else {
- /* binary data */
- if (DRM_UNKNOWN_DATA_LEN == s->contentLength) {
- /* try to check whether there is boundary may be split */
- int32_t res, binContentLen;
- uint8_t* pStart;
- int32_t bFoundBoundary = FALSE;
-
- pStart = s->rawContent + s->contentOffset;
- binContentLen = s->rawContentLen - s->contentOffset;
- res = drm_scanEndBoundary(pStart, binContentLen, ((T_DRM_DM_Binary_Node *)(s->infoStruct))->boundary);
-
- if (-1 == res) {
- freeSession(s);
- return DRM_FAILURE;
- }
-
- if (-2 == res) { /* may be the boundary is split */
- int32_t boundaryLen, leftLen, readBytes;
- char* pTmp = memrchr(pStart, '\r', binContentLen);
-
- if (NULL == pTmp) {
- freeSession(s);
- return DRM_FAILURE; /* conflict */
- }
-
- boundaryLen = strlen((char *)((T_DRM_DM_Binary_Node *)(s->infoStruct))->boundary) + 2; /* 2 means: '\r''\n' */
- s->readBuf = (uint8_t *)malloc(boundaryLen);
- if (NULL == s->readBuf) {
- freeSession(s);
- return DRM_FAILURE;
- }
- s->readBufOff = binContentLen - ((uint8_t *)pTmp - pStart);
- s->readBufLen = boundaryLen - s->readBufOff;
- memcpy(s->readBuf, pTmp, s->readBufOff);
- readBytes = data.readInputData(data.inputHandle, s->readBuf + s->readBufOff, s->readBufLen);
- if (-1 == readBytes || readBytes < s->readBufLen) {
- freeSession(s);
- return DRM_MEDIA_DATA_INVALID;
- }
-
- if (0 == drm_scanEndBoundary(s->readBuf, boundaryLen, ((T_DRM_DM_Binary_Node *)(s->infoStruct))->boundary)) {
- binContentLen = (uint8_t *)pTmp - pStart; /* yes, it is the end boundary */
- bFoundBoundary = TRUE;
- }
- } else {
- if (res >= 0 && res < binContentLen) {
- binContentLen = res;
- bFoundBoundary = TRUE;
- }
- }
-
- if (bFoundBoundary)
- s->contentLength = binContentLen;
- }
- }
- }
- break;
- case TYPE_DRM_CONTENT:
- {
- T_DRM_DCF_Info dcfInfo;
- uint8_t* pEncData = NULL;
-
- memset(&dcfInfo, 0, sizeof(T_DRM_DCF_Info));
- if (FALSE == drm_dcfParser(s->rawContent, s->rawContentLen, &dcfInfo, &pEncData)) {
- freeSession(s);
- return DRM_MEDIA_DATA_INVALID;
- }
-
- s->infoStruct = (T_DRM_Dcf_Node *)malloc(sizeof(T_DRM_Dcf_Node));
- if (NULL == s->infoStruct)
- return DRM_FAILURE;
- memset(s->infoStruct, 0, sizeof(T_DRM_Dcf_Node));
-
- s->deliveryMethod = SEPARATE_DELIVERY;
- s->contentLength = dcfInfo.DecryptedDataLen;
- ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength = dcfInfo.EncryptedDataLen;
- s->contentOffset = pEncData - s->rawContent;
- strcpy((char *)s->contentType, (char *)dcfInfo.ContentType);
- strcpy((char *)s->contentID, (char *)dcfInfo.ContentURI);
- strcpy((char *)((T_DRM_Dcf_Node *)(s->infoStruct))->rightsIssuer, (char *)dcfInfo.Rights_Issuer);
- }
- break;
- case TYPE_DRM_RIGHTS_XML: /* rights object should using "SVC_drm_installRights", it can not open a session */
- case TYPE_DRM_RIGHTS_WBXML: /* rights object should using "SVC_drm_installRights", it can not open a session */
- case TYPE_DRM_UNKNOWN:
- default:
- freeSession(s);
- return DRM_MEDIA_DATA_INVALID;
- }
-
- if ((SEPARATE_DELIVERY_FL == s->deliveryMethod || SEPARATE_DELIVERY == s->deliveryMethod) &&
- s->contentOffset + ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength <= DRM_MAX_MALLOC_LEN) {
- uint8_t keyValue[DRM_KEY_LEN];
- uint8_t lastDcfBuf[DRM_TWO_AES_BLOCK_LEN];
- int32_t seekPos, moreBytes;
-
- if (TRUE == drm_getKey(s->contentID, keyValue)) {
- seekPos = s->contentOffset + ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength - DRM_TWO_AES_BLOCK_LEN;
- memcpy(lastDcfBuf, s->rawContent + seekPos, DRM_TWO_AES_BLOCK_LEN);
-
- if (TRUE == drm_updateDcfDataLen(lastDcfBuf, keyValue, &moreBytes)) {
- s->contentLength = ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength;
- s->contentLength -= moreBytes;
- }
- }
- }
-
- session = addSession(s);
- if (-1 == session)
- return DRM_FAILURE;
-
- return session;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_getDeliveryMethod(int32_t session)
-{
- T_DRM_Session_Node* s;
-
- if (session < 0)
- return DRM_FAILURE;
-
- s = getSession(session);
- if (NULL == s)
- return DRM_SESSION_NOT_OPENED;
-
- return s->deliveryMethod;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_getContentType(int32_t session, uint8_t* mediaType)
-{
- T_DRM_Session_Node* s;
-
- if (session < 0 || NULL == mediaType)
- return DRM_FAILURE;
-
- s = getSession(session);
- if (NULL == s)
- return DRM_SESSION_NOT_OPENED;
-
- strcpy((char *)mediaType, (char *)s->contentType);
-
- return DRM_SUCCESS;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_checkRights(int32_t session, int32_t permission)
-{
- T_DRM_Session_Node* s;
- int32_t id;
- T_DRM_Rights *pRo, *pCurRo;
- int32_t roAmount;
- int32_t i;
- int32_t res = DRM_FAILURE;
-
- if (session < 0)
- return DRM_FAILURE;
-
- s = getSession(session);
- if (NULL == s)
- return DRM_SESSION_NOT_OPENED;
-
- /* if it is Forward-Lock cases, check it and return directly */
- if (FORWARD_LOCK == s->deliveryMethod) {
- if (DRM_PERMISSION_PLAY == permission ||
- DRM_PERMISSION_DISPLAY == permission ||
- DRM_PERMISSION_EXECUTE == permission ||
- DRM_PERMISSION_PRINT == permission)
- return DRM_SUCCESS;
-
- return DRM_FAILURE;
- }
-
- /* if try to forward, only DCF can be forwarded */
- if (DRM_PERMISSION_FORWARD == permission) {
- if (SEPARATE_DELIVERY == s->deliveryMethod)
- return DRM_SUCCESS;
-
- return DRM_FAILURE;
- }
-
- /* The following will check CD or SD other permissions */
- if (FALSE == drm_readFromUidTxt(s->contentID, &id, GET_ID))
- return DRM_FAILURE;
-
- drm_writeOrReadInfo(id, NULL, &roAmount, GET_ROAMOUNT);
- if (roAmount <= 0)
- return DRM_FAILURE;
-
- pRo = malloc(roAmount * sizeof(T_DRM_Rights));
- if (NULL == pRo)
- return DRM_FAILURE;
-
- drm_writeOrReadInfo(id, pRo, &roAmount, GET_ALL_RO);
-
- pCurRo = pRo;
- for (i = 0; i < roAmount; i++) {
- switch (permission) {
- case DRM_PERMISSION_PLAY:
- res = drm_startCheckRights(&(pCurRo->bIsPlayable), &(pCurRo->PlayConstraint));
- break;
- case DRM_PERMISSION_DISPLAY:
- res = drm_startCheckRights(&(pCurRo->bIsDisplayable), &(pCurRo->DisplayConstraint));
- break;
- case DRM_PERMISSION_EXECUTE:
- res = drm_startCheckRights(&(pCurRo->bIsExecuteable), &(pCurRo->ExecuteConstraint));
- break;
- case DRM_PERMISSION_PRINT:
- res = drm_startCheckRights(&(pCurRo->bIsPrintable), &(pCurRo->PrintConstraint));
- break;
- default:
- free(pRo);
- return DRM_FAILURE;
- }
-
- if (DRM_SUCCESS == res) {
- free(pRo);
- return DRM_SUCCESS;
- }
- pCurRo++;
- }
-
- free(pRo);
- return res;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_consumeRights(int32_t session, int32_t permission)
-{
- T_DRM_Session_Node* s;
- int32_t id;
-
- if (session < 0)
- return DRM_FAILURE;
-
- s = getSession(session);
- if (NULL == s)
- return DRM_SESSION_NOT_OPENED;
-
- if (DRM_PERMISSION_FORWARD == permission) {
- if (SEPARATE_DELIVERY == s->deliveryMethod)
- return DRM_SUCCESS;
-
- return DRM_FAILURE;
- }
-
- if (FORWARD_LOCK == s->deliveryMethod) /* Forwardlock type have utter rights */
- return DRM_SUCCESS;
-
- if (FALSE == drm_readFromUidTxt(s->contentID, &id, GET_ID))
- return DRM_FAILURE;
-
- return drm_checkRoAndUpdate(id, permission);
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_getContentLength(int32_t session)
-{
- T_DRM_Session_Node* s;
-
- if (session < 0)
- return DRM_FAILURE;
-
- s = getSession(session);
- if (NULL == s)
- return DRM_SESSION_NOT_OPENED;
-
- if (DRM_UNKNOWN_DATA_LEN == s->contentLength && s->contentOffset + ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength <= DRM_MAX_MALLOC_LEN &&
- (SEPARATE_DELIVERY == s->deliveryMethod || SEPARATE_DELIVERY_FL == s->deliveryMethod)) {
- uint8_t keyValue[DRM_KEY_LEN];
- uint8_t lastDcfBuf[DRM_TWO_AES_BLOCK_LEN];
- int32_t seekPos, moreBytes;
-
- if (TRUE == drm_getKey(s->contentID, keyValue)) {
- seekPos = s->contentOffset + ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength - DRM_TWO_AES_BLOCK_LEN;
- memcpy(lastDcfBuf, s->rawContent + seekPos, DRM_TWO_AES_BLOCK_LEN);
-
- if (TRUE == drm_updateDcfDataLen(lastDcfBuf, keyValue, &moreBytes)) {
- s->contentLength = ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength;
- s->contentLength -= moreBytes;
- }
- }
- }
-
- return s->contentLength;
-}
-
-static int32_t drm_readAesData(uint8_t* buf, T_DRM_Session_Node* s, int32_t aesStart, int32_t bufLen)
-{
- if (NULL == buf || NULL == s || aesStart < 0 || bufLen < 0)
- return -1;
-
- if (aesStart - s->contentOffset + bufLen > ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength)
- return -2;
-
- if (aesStart < DRM_MAX_MALLOC_LEN) {
- if (aesStart + bufLen <= DRM_MAX_MALLOC_LEN) { /* read from buffer */
- memcpy(buf, s->rawContent + aesStart, bufLen);
- return bufLen;
- } else { /* first read from buffer and then from InputStream */
- int32_t point = DRM_MAX_MALLOC_LEN - aesStart;
- int32_t res;
-
- if (((T_DRM_Dcf_Node *)(s->infoStruct))->bAesBackupBuf) {
- memcpy(buf, ((T_DRM_Dcf_Node *)(s->infoStruct))->aesBackupBuf, DRM_ONE_AES_BLOCK_LEN);
- res = s->readInputDataFunc(s->inputHandle, buf + DRM_ONE_AES_BLOCK_LEN, DRM_ONE_AES_BLOCK_LEN);
- if (0 == res || -1 == res)
- return -1;
-
- res += DRM_ONE_AES_BLOCK_LEN;
- } else {
- memcpy(buf, s->rawContent + aesStart, point);
- res = s->readInputDataFunc(s->inputHandle, buf + point, bufLen - point);
- if (0 == res || -1 == res)
- return -1;
-
- res += point;
- }
-
- memcpy(((T_DRM_Dcf_Node *)(s->infoStruct))->aesBackupBuf, buf + DRM_ONE_AES_BLOCK_LEN, DRM_ONE_AES_BLOCK_LEN);
- ((T_DRM_Dcf_Node *)(s->infoStruct))->bAesBackupBuf = TRUE;
-
- return res;
- }
- } else { /* read from InputStream */
- int32_t res;
-
- memcpy(buf, ((T_DRM_Dcf_Node *)(s->infoStruct))->aesBackupBuf, DRM_ONE_AES_BLOCK_LEN);
- res = s->readInputDataFunc(s->inputHandle, buf + DRM_ONE_AES_BLOCK_LEN, DRM_ONE_AES_BLOCK_LEN);
-
- if (0 == res || -1 == res)
- return -1;
-
- memcpy(((T_DRM_Dcf_Node *)(s->infoStruct))->aesBackupBuf, buf + DRM_ONE_AES_BLOCK_LEN, DRM_ONE_AES_BLOCK_LEN);
-
- return DRM_ONE_AES_BLOCK_LEN + res;
- }
-}
-
-static int32_t drm_readContentFromBuf(T_DRM_Session_Node* s, int32_t offset, uint8_t* mediaBuf, int32_t mediaBufLen)
-{
- int32_t readBytes;
-
- if (offset > s->contentLength)
- return DRM_FAILURE;
-
- if (offset == s->contentLength)
- return DRM_MEDIA_EOF;
-
- if (offset + mediaBufLen > s->contentLength)
- readBytes = s->contentLength - offset;
- else
- readBytes = mediaBufLen;
-
- if (DRM_MESSAGE_CODING_BASE64 == s->transferEncoding)
- memcpy(mediaBuf, s->rawContent + offset, readBytes);
- else
- memcpy(mediaBuf, s->rawContent + s->contentOffset + offset, readBytes);
-
- return readBytes;
-}
-
-static int32_t drm_readB64ContentFromInputStream(T_DRM_Session_Node* s, int32_t offset, uint8_t* mediaBuf, int32_t mediaBufLen)
-{
- uint8_t encBuf[DRM_B64_ENC_BLOCK], decBuf[DRM_B64_DEC_BLOCK];
- int32_t encLen, decLen;
- int32_t i, j, piece, leftLen, firstBytes;
- int32_t readBytes = 0;
-
- if (offset < ((T_DRM_DM_Base64_Node *)(s->infoStruct))->b64DecodeDataLen) {
- readBytes = ((T_DRM_DM_Base64_Node *)(s->infoStruct))->b64DecodeDataLen - offset;
- memcpy(mediaBuf, s->rawContent + offset, readBytes);
- } else {
- if (s->bEndData)
- return DRM_MEDIA_EOF;
-
- firstBytes = offset % DRM_B64_DEC_BLOCK;
- if (firstBytes > 0) {
- if (DRM_B64_DEC_BLOCK - firstBytes >= mediaBufLen) {
- readBytes = mediaBufLen;
- memcpy(mediaBuf, ((T_DRM_DM_Base64_Node *)(s->infoStruct))->b64DecodeData + firstBytes, readBytes);
- return readBytes;
- }
-
- readBytes = DRM_B64_DEC_BLOCK - firstBytes;
- memcpy(mediaBuf, ((T_DRM_DM_Base64_Node *)(s->infoStruct))->b64DecodeData + firstBytes, readBytes);
- }
- }
-
- leftLen = mediaBufLen - readBytes;
- encLen = (leftLen - 1) / DRM_B64_DEC_BLOCK * DRM_B64_ENC_BLOCK + DRM_B64_ENC_BLOCK;
- piece = encLen / DRM_B64_ENC_BLOCK;
-
- for (i = 0; i < piece; i++) {
- j = 0;
- while (j < DRM_B64_ENC_BLOCK) {
- if (NULL != s->readBuf && s->readBufLen > 0) { /* read from backup buffer */
- *(encBuf + j) = s->readBuf[s->readBufOff];
- s->readBufOff++;
- s->readBufLen--;
- } else { /* read from InputStream */
- if (0 == s->readInputDataFunc(s->inputHandle, encBuf + j, 1))
- return DRM_MEDIA_DATA_INVALID;
- }
-
- if ('\r' == *(encBuf + j) || '\n' == *(encBuf + j))
- continue; /* skip CRLF */
-
- if ('-' == *(encBuf + j)) {
- int32_t k, len;
-
- /* invalid base64 data, it comes to end boundary */
- if (0 != j)
- return DRM_MEDIA_DATA_INVALID;
-
- /* check whether it is really the boundary */
- len = strlen((char *)((T_DRM_DM_Base64_Node *)(s->infoStruct))->boundary);
- if (NULL == s->readBuf) {
- s->readBuf = (uint8_t *)malloc(len);
- if (NULL == s->readBuf)
- return DRM_FAILURE;
- }
-
- s->readBuf[0] = '-';
- for (k = 0; k < len - 1; k++) {
- if (NULL != s->readBuf && s->readBufLen > 0) { /* read from backup buffer */
- *(s->readBuf + k + 1) = s->readBuf[s->readBufOff];
- s->readBufOff++;
- s->readBufLen--;
- } else { /* read from InputStream */
- if (-1 == s->readInputDataFunc(s->inputHandle, s->readBuf + k + 1, 1))
- return DRM_MEDIA_DATA_INVALID;
- }
- }
- if (0 == memcmp(s->readBuf, ((T_DRM_DM_Base64_Node *)(s->infoStruct))->boundary, len))
- s->bEndData = TRUE;
- else
- return DRM_MEDIA_DATA_INVALID;
-
- break;
- }
- j++;
- }
-
- if (TRUE == s->bEndData) { /* it means come to the end of base64 data */
- if (0 == readBytes)
- return DRM_MEDIA_EOF;
-
- break;
- }
-
- encLen = DRM_B64_ENC_BLOCK;
- decLen = DRM_B64_DEC_BLOCK;
- if (-1 == (decLen = drm_decodeBase64(decBuf, decLen, encBuf, &encLen)))
- return DRM_MEDIA_DATA_INVALID;
-
- if (leftLen >= decLen) {
- memcpy(mediaBuf + readBytes, decBuf, decLen);
- readBytes += decLen;
- leftLen -= decLen;
- } else {
- if (leftLen > 0) {
- memcpy(mediaBuf + readBytes, decBuf, leftLen);
- readBytes += leftLen;
- }
- break;
- }
- }
- memcpy(((T_DRM_DM_Base64_Node *)(s->infoStruct))->b64DecodeData, decBuf, DRM_B64_DEC_BLOCK);
-
- return readBytes;
-}
-
-static int32_t drm_readBase64Content(T_DRM_Session_Node* s, int32_t offset, uint8_t* mediaBuf, int32_t mediaBufLen)
-{
- int32_t readBytes;
-
- /* when the content length has been well-known */
- if (s->contentLength >= 0)
- readBytes = drm_readContentFromBuf(s, offset, mediaBuf, mediaBufLen);
- else /* else when the content length has not been well-known yet */
- if (offset < ((T_DRM_DM_Base64_Node *)(s->infoStruct))->b64DecodeDataLen)
- if (offset + mediaBufLen <= ((T_DRM_DM_Base64_Node *)(s->infoStruct))->b64DecodeDataLen) {
- readBytes = mediaBufLen;
- memcpy(mediaBuf, s->rawContent + offset, readBytes);
- } else
- readBytes = drm_readB64ContentFromInputStream(s, offset, mediaBuf, mediaBufLen);
- else
- readBytes = drm_readB64ContentFromInputStream(s, offset, mediaBuf, mediaBufLen);
-
- return readBytes;
-}
-
-static int32_t drm_readBinaryContentFromInputStream(T_DRM_Session_Node* s, int32_t offset, uint8_t* mediaBuf, int32_t mediaBufLen)
-{
- int32_t res = 0, readBytes = 0;
- int32_t leftLen;
-
- if (s->contentOffset + offset < DRM_MAX_MALLOC_LEN) {
- readBytes = DRM_MAX_MALLOC_LEN - s->contentOffset - offset;
- memcpy(mediaBuf, s->rawContent + s->contentOffset + offset, readBytes);
- } else
- if (s->bEndData)
- return DRM_MEDIA_EOF;
-
- leftLen = mediaBufLen - readBytes;
-
- if (NULL != s->readBuf && s->readBufLen > 0) { /* read from backup buffer */
- if (leftLen <= s->readBufLen) {
- memcpy(mediaBuf + readBytes, s->readBuf + s->readBufOff, leftLen);
- s->readBufOff += leftLen;
- s->readBufLen -= leftLen;
- readBytes += leftLen;
- leftLen = 0;
- } else {
- memcpy(mediaBuf + readBytes, s->readBuf + s->readBufOff, s->readBufLen);
- s->readBufOff += s->readBufLen;
- leftLen -= s->readBufLen;
- readBytes += s->readBufLen;
- s->readBufLen = 0;
- }
- }
-
- if (leftLen > 0) {
- res = s->readInputDataFunc(s->inputHandle, mediaBuf + readBytes, mediaBufLen - readBytes);
- if (-1 == res)
- return DRM_MEDIA_DATA_INVALID;
- }
-
- readBytes += res;
- res = drm_scanEndBoundary(mediaBuf, readBytes, ((T_DRM_DM_Binary_Node *)(s->infoStruct))->boundary);
- if (-1 == res)
- return DRM_MEDIA_DATA_INVALID;
- if (-2 == res) { /* may be the boundary is split */
- int32_t boundaryLen, len, off, k;
- char* pTmp = memrchr(mediaBuf, '\r', readBytes);
-
- if (NULL == pTmp)
- return DRM_FAILURE; /* conflict */
-
- boundaryLen = strlen((char *)((T_DRM_DM_Binary_Node *)(s->infoStruct))->boundary) + 2; /* 2 means: '\r''\n' */
- if (NULL == s->readBuf) {
- s->readBuf = (uint8_t *)malloc(boundaryLen);
- if (NULL == s->readBuf)
- return DRM_FAILURE;
- }
-
- off = readBytes - ((uint8_t *)pTmp - mediaBuf);
- len = boundaryLen - off;
- memcpy(s->readBuf, pTmp, off);
- for (k = 0; k < boundaryLen - off; k++) {
- if (NULL != s->readBuf && s->readBufLen > 0) { /* read from backup buffer */
- *(s->readBuf + k + off) = s->readBuf[s->readBufOff];
- s->readBufOff++;
- s->readBufLen--;
- } else { /* read from InputStream */
- if (-1 == s->readInputDataFunc(s->inputHandle, s->readBuf + k + off, 1))
- return DRM_MEDIA_DATA_INVALID;
- }
- }
- s->readBufOff = off;
- s->readBufLen = len;
-
- if (0 == drm_scanEndBoundary(s->readBuf, boundaryLen, ((T_DRM_DM_Binary_Node *)(s->infoStruct))->boundary)) {
- readBytes = (uint8_t *)pTmp - mediaBuf; /* yes, it is the end boundary */
- s->bEndData = TRUE;
- }
- } else {
- if (res >= 0 && res < readBytes) {
- readBytes = res;
- s->bEndData = TRUE;
- }
- }
-
- if (s->bEndData) {
- if (0 == readBytes)
- return DRM_MEDIA_EOF;
- }
-
- return readBytes;
-}
-
-static int32_t drm_readBinaryContent(T_DRM_Session_Node* s, int32_t offset, uint8_t* mediaBuf, int32_t mediaBufLen)
-{
- int32_t readBytes;
-
- if (s->contentLength >= 0)
- readBytes = drm_readContentFromBuf(s, offset, mediaBuf, mediaBufLen);
- else /* else when the content length has not been well-known yet */
- if (s->contentOffset + offset < DRM_MAX_MALLOC_LEN)
- if (s->contentOffset + offset + mediaBufLen <= DRM_MAX_MALLOC_LEN) {
- readBytes = mediaBufLen;
- memcpy(mediaBuf, s->rawContent + s->contentOffset + offset, readBytes);
- } else
- readBytes = drm_readBinaryContentFromInputStream(s, offset, mediaBuf, mediaBufLen);
- else
- readBytes = drm_readBinaryContentFromInputStream(s, offset, mediaBuf, mediaBufLen);
-
- return readBytes;
-}
-
-static int32_t drm_readAesContent(T_DRM_Session_Node* s, int32_t offset, uint8_t* mediaBuf, int32_t mediaBufLen)
-{
- uint8_t keyValue[DRM_KEY_LEN];
- uint8_t buf[DRM_TWO_AES_BLOCK_LEN];
- int32_t readBytes = 0;
- int32_t bufLen, piece, i, copyBytes, leftBytes;
- int32_t aesStart, mediaStart, mediaBufOff;
- AES_KEY key;
-
- if (FALSE == drm_getKey(s->contentID, keyValue))
- return DRM_NO_RIGHTS;
-
- /* when the content length has been well-known */
- if (s->contentLength > 0) {
- if (offset > s->contentLength)
- return DRM_FAILURE;
-
- if (offset == s->contentLength)
- return DRM_MEDIA_EOF;
-
- if (offset + mediaBufLen > s->contentLength)
- readBytes = s->contentLength - offset;
- else
- readBytes = mediaBufLen;
-
- aesStart = s->contentOffset + (offset / DRM_ONE_AES_BLOCK_LEN * DRM_ONE_AES_BLOCK_LEN);
- piece = (offset + readBytes - 1) / DRM_ONE_AES_BLOCK_LEN - offset / DRM_ONE_AES_BLOCK_LEN + 2;
- mediaStart = offset % DRM_ONE_AES_BLOCK_LEN;
-
- AES_set_decrypt_key(keyValue, DRM_KEY_LEN * 8, &key);
- mediaBufOff = 0;
- leftBytes = readBytes;
-
- for (i = 0; i < piece - 1; i++) {
- memcpy(buf, s->rawContent + aesStart + i * DRM_ONE_AES_BLOCK_LEN, DRM_TWO_AES_BLOCK_LEN);
- bufLen = DRM_TWO_AES_BLOCK_LEN;
-
- if (drm_aesDecBuffer(buf, &bufLen, &key) < 0)
- return DRM_MEDIA_DATA_INVALID;
-
- if (0 != i)
- mediaStart = 0;
-
- if (bufLen - mediaStart <= leftBytes)
- copyBytes = bufLen - mediaStart;
- else
- copyBytes = leftBytes;
-
- memcpy(mediaBuf + mediaBufOff, buf + mediaStart, copyBytes);
- leftBytes -= copyBytes;
- mediaBufOff += copyBytes;
- }
- } else {
- int32_t res;
-
- if (s->bEndData)
- return DRM_MEDIA_EOF;
-
- if (((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataLen > ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataOff) {
- if (mediaBufLen < ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataLen - ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataOff)
- copyBytes = mediaBufLen;
- else
- copyBytes = ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataLen - ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataOff;
-
- memcpy(mediaBuf, ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecData + ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataOff, copyBytes);
- ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataOff += copyBytes;
- readBytes += copyBytes;
- }
-
- leftBytes = mediaBufLen - readBytes;
- if (0 == leftBytes)
- return readBytes;
- if (leftBytes < 0)
- return DRM_FAILURE;
-
- offset += readBytes;
- aesStart = s->contentOffset + (offset / DRM_ONE_AES_BLOCK_LEN * DRM_ONE_AES_BLOCK_LEN);
- piece = (offset + leftBytes - 1) / DRM_ONE_AES_BLOCK_LEN - offset / DRM_ONE_AES_BLOCK_LEN + 2;
- mediaBufOff = readBytes;
-
- AES_set_decrypt_key(keyValue, DRM_KEY_LEN * 8, &key);
-
- for (i = 0; i < piece - 1; i++) {
- if (-1 == (res = drm_readAesData(buf, s, aesStart, DRM_TWO_AES_BLOCK_LEN)))
- return DRM_MEDIA_DATA_INVALID;
-
- if (-2 == res)
- break;
-
- bufLen = DRM_TWO_AES_BLOCK_LEN;
- aesStart += DRM_ONE_AES_BLOCK_LEN;
-
- if (drm_aesDecBuffer(buf, &bufLen, &key) < 0)
- return DRM_MEDIA_DATA_INVALID;
-
- drm_discardPaddingByte(buf, &bufLen);
-
- if (bufLen <= leftBytes)
- copyBytes = bufLen;
- else
- copyBytes = leftBytes;
-
- memcpy(mediaBuf + mediaBufOff, buf, copyBytes);
- leftBytes -= copyBytes;
- mediaBufOff += copyBytes;
- readBytes += copyBytes;
- }
-
- memcpy(((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecData, buf, DRM_ONE_AES_BLOCK_LEN);
- ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataLen = bufLen;
- ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataOff = copyBytes;
-
- if (aesStart - s->contentOffset > ((T_DRM_Dcf_Node *)(s->infoStruct))->encContentLength - DRM_TWO_AES_BLOCK_LEN && ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataOff == ((T_DRM_Dcf_Node *)(s->infoStruct))->aesDecDataLen) {
- s->bEndData = TRUE;
- if (0 == readBytes)
- return DRM_MEDIA_EOF;
- }
- }
-
- return readBytes;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_getContent(int32_t session, int32_t offset, uint8_t* mediaBuf, int32_t mediaBufLen)
-{
- T_DRM_Session_Node* s;
- int32_t readBytes;
-
- if (session < 0 || offset < 0 || NULL == mediaBuf || mediaBufLen <= 0)
- return DRM_FAILURE;
-
- s = getSession(session);
- if (NULL == s)
- return DRM_SESSION_NOT_OPENED;
-
- if (0 >= s->getInputDataLengthFunc(s->inputHandle))
- return DRM_MEDIA_DATA_INVALID;
-
- switch(s->deliveryMethod) {
- case FORWARD_LOCK:
- case COMBINED_DELIVERY:
- if (DRM_MESSAGE_CODING_BASE64 == s->transferEncoding)
- readBytes = drm_readBase64Content(s, offset, mediaBuf, mediaBufLen);
- else /* binary */
- readBytes = drm_readBinaryContent(s, offset, mediaBuf, mediaBufLen);
- break;
- case SEPARATE_DELIVERY:
- case SEPARATE_DELIVERY_FL:
- readBytes = drm_readAesContent(s, offset, mediaBuf, mediaBufLen);
- break;
- default:
- return DRM_FAILURE;
- }
-
- return readBytes;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_getRightsIssuer(int32_t session, uint8_t* rightsIssuer)
-{
- T_DRM_Session_Node* s;
-
- if (session < 0 || NULL == rightsIssuer)
- return DRM_FAILURE;
-
- s = getSession(session);
- if (NULL == s)
- return DRM_SESSION_NOT_OPENED;
-
- if (SEPARATE_DELIVERY == s->deliveryMethod || SEPARATE_DELIVERY_FL == s->deliveryMethod) {
- strcpy((char *)rightsIssuer, (char *)((T_DRM_Dcf_Node *)(s->infoStruct))->rightsIssuer);
- return DRM_SUCCESS;
- }
-
- return DRM_NOT_SD_METHOD;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_getRightsInfo(int32_t session, T_DRM_Rights_Info* rights)
-{
- T_DRM_Session_Node* s;
- T_DRM_Rights rightsInfo;
- int32_t roAmount, id;
-
- if (session < 0 || NULL == rights)
- return DRM_FAILURE;
-
- s = getSession(session);
- if (NULL == s)
- return DRM_SESSION_NOT_OPENED;
-
- if (FORWARD_LOCK == s->deliveryMethod) {
- strcpy((char *)rights->roId, "ForwardLock");
- rights->displayRights.indicator = DRM_NO_CONSTRAINT;
- rights->playRights.indicator = DRM_NO_CONSTRAINT;
- rights->executeRights.indicator = DRM_NO_CONSTRAINT;
- rights->printRights.indicator = DRM_NO_CONSTRAINT;
- return DRM_SUCCESS;
- }
-
- if (FALSE == drm_readFromUidTxt(s->contentID, &id, GET_ID))
- return DRM_NO_RIGHTS;
-
- if (FALSE == drm_writeOrReadInfo(id, NULL, &roAmount, GET_ROAMOUNT))
- return DRM_FAILURE;
-
- if (roAmount < 0)
- return DRM_NO_RIGHTS;
-
- /* some rights has been installed, but now there is no valid rights */
- if (0 == roAmount) {
- strcpy((char *)rights->roId, s->contentID);
- rights->displayRights.indicator = DRM_NO_PERMISSION;
- rights->playRights.indicator = DRM_NO_PERMISSION;
- rights->executeRights.indicator = DRM_NO_PERMISSION;
- rights->printRights.indicator = DRM_NO_PERMISSION;
- return DRM_SUCCESS;
- }
-
- roAmount = 1;
- memset(&rightsInfo, 0, sizeof(T_DRM_Rights));
- if (FALSE == drm_writeOrReadInfo(id, &rightsInfo, &roAmount, GET_A_RO))
- return DRM_FAILURE;
-
- memset(rights, 0, sizeof(T_DRM_Rights_Info));
- drm_getLicenseInfo(&rightsInfo, rights);
- return DRM_SUCCESS;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_closeSession(int32_t session)
-{
- if (session < 0)
- return DRM_FAILURE;
-
- if (NULL == getSession(session))
- return DRM_SESSION_NOT_OPENED;
-
- removeSession(session);
-
- return DRM_SUCCESS;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_updateRights(uint8_t* contentID, int32_t permission)
-{
- int32_t id;
-
- if (NULL == contentID)
- return DRM_FAILURE;
-
- if (FALSE == drm_readFromUidTxt(contentID, &id, GET_ID))
- return DRM_FAILURE;
-
- return drm_checkRoAndUpdate(id, permission);
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_viewAllRights(T_DRM_Rights_Info_Node **ppRightsInfo)
-{
- T_DRM_Rights_Info_Node rightsNode;
- int32_t maxId, id, roAmount, j;
- T_DRM_Rights rights;
-
- memset(&rights, 0, sizeof(T_DRM_Rights));
-
- if (NULL == ppRightsInfo)
- return DRM_FAILURE;
-
- *ppRightsInfo = NULL;
-
- maxId = drm_getMaxIdFromUidTxt();
- if (-1 == maxId)
- return DRM_FAILURE;
-
- for (id = 1; id <= maxId; id++) {
- drm_writeOrReadInfo(id, NULL, &roAmount, GET_ROAMOUNT);
- if (roAmount <= 0) /* this means there is not any rights */
- continue;
-
- for (j = 1; j <= roAmount; j++) {
- if (FALSE == drm_writeOrReadInfo(id, &rights, &j, GET_A_RO))
- continue;
-
- memset(&rightsNode, 0, sizeof(T_DRM_Rights_Info_Node));
-
- drm_getLicenseInfo(&rights, &(rightsNode.roInfo));
-
- if (FALSE == drm_addRightsNodeToList(ppRightsInfo, &rightsNode))
- continue;
- }
- }
- return DRM_SUCCESS;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_freeRightsInfoList(T_DRM_Rights_Info_Node *pRightsHeader)
-{
- T_DRM_Rights_Info_Node *pNode, *pTmp;
-
- if (NULL == pRightsHeader)
- return DRM_FAILURE;
-
- pNode = pRightsHeader;
-
- while (NULL != pNode) {
- pTmp = pNode;
- pNode = pNode->next;
- free(pTmp);
- }
- return DRM_SUCCESS;
-}
-
-/* see svc_drm.h */
-int32_t SVC_drm_deleteRights(uint8_t* roId)
-{
- int32_t maxId, id, roAmount, j;
- T_DRM_Rights rights;
-
- memset(&rights, 0, sizeof(T_DRM_Rights));
-
- if (NULL == roId)
- return DRM_FAILURE;
-
- maxId = drm_getMaxIdFromUidTxt();
- if (-1 == maxId)
- return DRM_NO_RIGHTS;
-
- for (id = 1; id <= maxId; id++) {
- drm_writeOrReadInfo(id, NULL, &roAmount, GET_ROAMOUNT);
- if (roAmount <= 0) /* this means there is not any rights */
- continue;
-
- for (j = 1; j <= roAmount; j++) {
- if (FALSE == drm_writeOrReadInfo(id, &rights, &j, GET_A_RO))
- continue;
-
- /* here find the RO which will be deleted */
- if (0 == strcmp((char *)rights.uid, (char *)roId)) {
- T_DRM_Rights *pAllRights;
-
- pAllRights = (T_DRM_Rights *)malloc(roAmount * sizeof(T_DRM_Rights));
- if (NULL == pAllRights)
- return DRM_FAILURE;
-
- drm_writeOrReadInfo(id, pAllRights, &roAmount, GET_ALL_RO);
- roAmount--;
- if (0 == roAmount) { /* this means it is the last one rights */
- drm_removeIdInfoFile(id); /* delete the id.info file first */
- drm_updateUidTxtWhenDelete(id); /* update uid.txt file */
- free(pAllRights);
- return DRM_SUCCESS;
- } else /* using the last one rights instead of the deleted one */
- memcpy(pAllRights + (j - 1), pAllRights + roAmount, sizeof(T_DRM_Rights));
-
- /* delete the id.info file first */
-// drm_removeIdInfoFile(id);
-
- if (FALSE == drm_writeOrReadInfo(id, pAllRights, &roAmount, SAVE_ALL_RO)) {
- free(pAllRights);
- return DRM_FAILURE;
- }
-
- free(pAllRights);
- return DRM_SUCCESS;
- }
- }
- }
-
- return DRM_FAILURE;
-}
diff --git a/media/libdrm/mobile1/src/objmng/drm_decoder.c b/media/libdrm/mobile1/src/objmng/drm_decoder.c
deleted file mode 100644
index 82c7efb..0000000
--- a/media/libdrm/mobile1/src/objmng/drm_decoder.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.
- */
-
-#include <objmng/drm_decoder.h>
-
-/* global variables */
-static const uint8_t * base64_alphabet = (const uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-#define SKIP_CRLF(p) while('\r' == *(p) || '\n' == *(p)) \
- p++
-
-static int8_t get_alphabet_index(int8_t ch)
-{
- uint8_t * tmp;
-
- if ('=' == ch)
- return 64;
-
- tmp = (uint8_t *)strchr((const char *)base64_alphabet, ch);
- if (NULL == tmp)
- return -1;
-
- return (int8_t)(tmp - base64_alphabet);
-}
-
-/* See drm_decoder.h */
-int32_t drm_decodeBase64(uint8_t * dest, int32_t destLen, uint8_t * src, int32_t * srcLen)
-{
- int32_t maxDestSize, i, maxGroup;
- uint8_t *pDest, *pSrc;
- int8_t tpChar;
-
- if (NULL == src || NULL == srcLen || *srcLen <= 0 || destLen < 0)
- return -1;
-
- maxDestSize = (*srcLen) * 3/4;
- if (NULL == dest || 0 == destLen)
- return maxDestSize;
-
- if (destLen < maxDestSize)
- maxDestSize = destLen;
- maxGroup = maxDestSize/3;
-
- pDest = dest; /* start to decode src to dest */
- pSrc = src;
- for (i = 0; i < maxGroup && *srcLen - (pSrc - src) >= 4; i++) {
- SKIP_CRLF(pSrc);
- if (pSrc - src >= *srcLen)
- break;
- tpChar = get_alphabet_index(*pSrc); /* to first byte */
- if (-1 == tpChar || 64 == tpChar)
- return -1;
- pDest[0] = tpChar << 2;
- pSrc++;
- SKIP_CRLF(pSrc);
- tpChar = get_alphabet_index(*pSrc);
- if (-1 == tpChar || 64 == tpChar)
- return -1;
- pDest[0] |= (tpChar >> 4);
- pDest[1] = tpChar << 4; /* to second byte */
- pSrc++;
- SKIP_CRLF(pSrc);
- tpChar = get_alphabet_index(*pSrc);
- if (-1 == tpChar)
- return -1;
- if (64 == tpChar) /* end */
- return pDest - dest + 1;
- pDest[1] |= (tpChar >> 2);
- pDest[2] = tpChar << 6; /* to third byte */
- pSrc++;
- SKIP_CRLF(pSrc);
- tpChar = get_alphabet_index(*pSrc);
- if (-1 == tpChar)
- return -1;
- if (64 == tpChar) /* end */
- return pDest - dest + 2;
- pDest[2] |= tpChar;
- pDest += 3;
- pSrc++;
- }
- *srcLen = pSrc - src;
- return pDest - dest;
-}
diff --git a/media/libdrm/mobile1/src/objmng/drm_file.c b/media/libdrm/mobile1/src/objmng/drm_file.c
deleted file mode 100644
index e6c303e..0000000
--- a/media/libdrm/mobile1/src/objmng/drm_file.c
+++ /dev/null
@@ -1,694 +0,0 @@
-/*
- * 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.
- */
-
-#include <objmng/drm_file.h>
-
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <errno.h>
-#include <string.h>
-
-/**
- * Fails on zaurus?
- #define DEVICE_FILESYSTEM
-*/
-#define DEFAULT_TOTAL_SPACE (4L * 1024L * 1024L) /* 4 Meg. */
-
-#ifndef DEVICE_FILESYSTEM
-/* Store the total space on FS VM can use. */
-static int32_t totalSpace;
-/* how many remain space can VM use. */
-static int32_t availableSize;
-#endif
-
-extern char* getStorageRoot(void);
-
-static char tmpPathBuf1[MAX_FILENAME_LEN];
-static char tmpPathBuf2[MAX_FILENAME_LEN];
-
-static int32_t
-convertFilename(const uint16_t *strData, int32_t strLength, char *buffer);
-
-static int calcDirSize(char *path, int len, uint8_t includeSubdirs);
-
-#ifndef DEVICE_FILESYSTEM
-static void initFsVariables(void);
-#endif
-
-/**
- * Convert a Java string into a nul terminated ascii string to pass to posix
- * @param strData first character of name
- * @param strLength number of characters in name
- * @param buffer Buffer to store terminated string in (at least MAXPATHLEN)
- * @return Length of filename in characters (excl. nul), or -1 on failure.
- */
-static int32_t
-convertFilename(const uint16_t *strData, int32_t strLength, char *buffer)
-{
- int idx;
-
- if (strLength >= (MAXPATHLEN-1))
- {
- Trace("convertFilename '%.*S' too long", strLength, strData);
- return -1;
- }
-
- for (idx = 0; idx < strLength; ++idx)
- *buffer++ = (char)*strData++;
-
- *buffer = 0;
- return strLength;
-}
-
-
-/**
- * Perform a stat() call on the given filename.
- * Helper for getFileLength and exists
- * @param name unicode name
- * @param nameLen number of unicode characters in name
- * @param sbuf stat buffer
- * @return TRUE on success, FALSE on failure
- */
-static int32_t
-getFileStat(const uint16_t *name, int32_t nameLen, struct stat *sbuf)
-{
- Trace("getFileStat: %.*S", nameLen, name);
-
- if (convertFilename(name, nameLen, tmpPathBuf1) <= 0)
- {
- Trace("getFileStat: bad filename");
- }
- else if (stat(tmpPathBuf1, sbuf) != 0)
- {
- Trace("getFileStat %s: stat() errno=%d", tmpPathBuf1, errno);
- }
- else /* Successful */
- {
- return TRUE;
- }
-
- return FALSE;
-}
-
-#ifndef DEVICE_FILESYSTEM
-/**
- * initial the variables like totalSpace, availableSize...
- */
-static void initFsVariables(void)
-{
- totalSpace = DEFAULT_TOTAL_SPACE;
-
- availableSize = totalSpace;
-}
-#endif /* DEVICE_FILESYSTEM */
-
-/**
- * calculate the size of everything inside path pointed directory
- * this function will use path pointed buffer to store some extra info
- * so param len is needed.
- * @param path the directory path need to calculate
- * @param len length of the path buffer, not the path string length
- * @param includeSubdirs also calculate all the subdirs in path holds?
- * @return the calculated size, DRM_FILE_FAILURE on failure.
- */
-static int calcDirSize(char *path, int len, uint8_t includeSubdirs)
-{
- struct dirent *ent;
- struct stat stat_buf;
-
- DIR *dir = NULL;
- int size = 0;
- int exists = -1;
- int dirPathLen = strlen(path);
-
- /* Ensure space for wildcard */
- if((dirPathLen + 2) >= MAXPATHLEN || (dirPathLen + 2) >= len)
- {
- return DRM_FILE_FAILURE;
- }
-
- if(path[dirPathLen - 1] != '/')
- {
- path[dirPathLen++] = '/';
- path[dirPathLen] = '\0';
- }
-
- dir = opendir(path);
- if (dir == NULL)
- {
- return DRM_FILE_FAILURE;
- }
-
- while ((ent = readdir(dir)) != NULL )
- {
- if (strcmp(ent->d_name, ".") == 0 ||
- strcmp(ent->d_name, "..") == 0)
- {
- continue;
- }
-
- path[dirPathLen] = '\0';
- if ((int)(strlen(ent->d_name) + dirPathLen + 1) < len)
- {
- strcat(path, ent->d_name);
- }
- else
- {
- continue;
- }
-
- exists = stat(path, &stat_buf);
- if (exists != -1)
- {
- /* exclude the storage occupied by directory itself */
- if (stat_buf.st_mode & S_IFDIR)
- {
- if(includeSubdirs)
- {
- /* calculate the size recursively */
- int ret;
- ret = calcDirSize(path, len, includeSubdirs);
- /* ignore failure in subdirs */
- if( DRM_FILE_FAILURE != ret )
- {
- size += ret;
- }
- }
- }
- else
- {
- size += stat_buf.st_size;
- }
- }
- }
-
- closedir(dir);
- return size;
-}
-
-/* see drm_file.h */
-int32_t DRM_file_startup(void)
-{
- Trace("DRM_file_startup");
-
-#ifndef DEVICE_FILESYSTEM
- availableSize = -1;
-
- initFsVariables();
-#endif
-
- return DRM_FILE_SUCCESS; /* Nothing to do */
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_listOpen(const uint16_t *prefix,
- int32_t prefixLen,
- int32_t* session,
- int32_t* iteration)
-{
- Trace("DRM_file_listOpen: %.*S", prefixLen, prefix);
-
- if (convertFilename(prefix, prefixLen, tmpPathBuf1) <= 0)
- {
- Trace("DRM_file_listOpen: bad filename");
- }
- else
- {
- DIR *dir;
-
- /* find the last /, and store the offset to the leaf prefix in
- * *iteration
- */
-
- char *sep = strrchr(tmpPathBuf1, '/');
- /* Root "/" is a leaf */
- if (sep == NULL || ((sep != NULL) && (sep == tmpPathBuf1)))
- {
- *iteration = prefixLen;
-
-#ifdef TRACE_ON
- sep = " <empty>"; /* trace will show sep+1 */
-#endif
- }
- else
- {
- *iteration = sep - tmpPathBuf1 + 1;
- *sep = 0;
- }
-
- dir = opendir(tmpPathBuf1);
-
- if (dir == NULL)
- {
- Trace("DRM_file_listOpen: opendir %s: errno=%d", tmpPathBuf1, errno);
- }
- else
- {
- Trace("DRM_file_listOpen: dir %s, filter %s", tmpPathBuf1, sep+1);
- *session = (int32_t)dir;
- return DRM_FILE_SUCCESS;
- }
- }
-
- return DRM_FILE_FAILURE;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_listNextEntry(const uint16_t *prefix, int32_t prefixLen,
- uint16_t* entry, int32_t entrySize,
- int32_t *session, int32_t* iteration)
-{
- struct dirent *ent;
-
- /* We stored the offset of the leaf part of the prefix (if any)
- * in *iteration
- */
- const uint16_t* strData = prefix + *iteration;
- int32_t strLength = prefixLen - *iteration;
-
- /* entrySize is bytes for some reason. Convert to ucs chars */
- entrySize /= 2;
-
- /* Now we want to filter for files which start with the (possibly empty)
- * sequence at strData. We have to return fully-qualified filenames,
- * which means *iteration characters from prefix, plus the
- * leaf name.
- */
-
- while ( (ent = readdir((DIR *)*session)) != NULL)
- {
- int len = strlen(ent->d_name);
-
- if ( (len + *iteration) > entrySize)
- {
- Trace("DRM_file_listNextEntry: %s too long", ent->d_name);
- }
- else if (strcmp(ent->d_name, ".") != 0 &&
- strcmp(ent->d_name, "..") != 0)
- {
- int idx;
- struct stat sinfo;
-
- /* check against the filter */
-
- for (idx = 0; idx < strLength; ++idx)
- {
- if (ent->d_name[idx] != strData[idx])
- goto next_name;
- }
-
- Trace("DRM_file_listNextEntry: matched %s", ent->d_name);
-
- /* Now generate the fully-qualified name */
-
- for (idx = 0; idx < *iteration; ++idx)
- entry[idx] = prefix[idx];
-
- for (idx = 0; idx < len; ++idx)
- entry[*iteration + idx] = (unsigned char)ent->d_name[idx];
-
- /*add "/" at the end of a DIR file entry*/
- if (getFileStat(entry, idx + *iteration, &sinfo)){
- if (S_ISDIR(sinfo.st_mode) &&
- (idx + 1 + *iteration) < entrySize) {
- entry[*iteration + idx] = '/';
- ++idx;
- }
- }
- else
- {
- Trace("DRM_file_listNextEntry: stat FAILURE on %.*S",
- idx + *iteration, entry);
- }
- Trace("DRM_file_listNextEntry: got %.*S", idx + *iteration, entry);
-
- return idx + *iteration;
- }
-
- next_name:
- Trace("DRM_file_listNextEntry: rejected %s", ent->d_name);
- }
-
- Trace("DRM_file_listNextEntry: end of list");
- return 0;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_listClose(int32_t session, int32_t iteration)
-{
- closedir( (DIR *)session);
- return DRM_FILE_SUCCESS;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_getFileLength(const uint16_t *name, int32_t nameLen)
-{
- struct stat sbuf;
-
- if (getFileStat(name, nameLen, &sbuf))
- {
- if (sbuf.st_size >= INT32_MAX)
- {
- Trace("DRM_file_getFileLength: file too big");
- }
- else /* Successful */
- {
- Trace("DRM_file_getFileLength: %.*S -> %d",
- nameLen, name, (int32_t)sbuf.st_size);
- return (int32_t)sbuf.st_size;
- }
- }
-
- return DRM_FILE_FAILURE;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_delete(const uint16_t *name, int32_t nameLen)
-{
- Trace("DRM_file_delete: %.*S", nameLen, name);
-
- if (convertFilename(name, nameLen, tmpPathBuf1) <= 0)
- {
- Trace("DRM_file_delete: bad filename");
- return DRM_FILE_FAILURE;
- }
- else
- {
- struct stat sinfo;
- if (stat(tmpPathBuf1, &sinfo) != 0){
- Trace("DRM_file_delete: stat failed, errno=%d", errno);
- return DRM_FILE_FAILURE;
- }
-#ifndef DEVICE_FILESYSTEM
- if (S_ISDIR(sinfo.st_mode)){
- /* it's a dir */
- if (rmdir(tmpPathBuf1) != 0){
- Trace("DRM_file_delete: dir remove failed, errno=%d", errno);
- return DRM_FILE_FAILURE;
- }
- else
- {
- return DRM_FILE_SUCCESS;
- }
- }
-#endif
- /* it's a file */
- if (unlink(tmpPathBuf1) != 0)
- {
- Trace("DRM_file_delete: file remove failed, errno=%d", errno);
- return DRM_FILE_FAILURE;
- }
- else
- {
-#ifndef DEVICE_FILESYSTEM
- availableSize += sinfo.st_size;
-#endif
- return DRM_FILE_SUCCESS;
- }
- }
- return DRM_FILE_FAILURE;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_rename(const uint16_t *oldName, int32_t oldNameLen,
- const uint16_t *newName, int32_t newNameLen)
-{
- Trace("DRM_file_rename %.*S -> %.*S",
- oldNameLen, oldName, newNameLen, newName);
- if (DRM_file_exists(newName, newNameLen) != DRM_FILE_FAILURE)
- {
- Trace("DRM_file_rename: filename:%s exist",newName);
- return DRM_FILE_FAILURE;
- }
-
- if (convertFilename(oldName, oldNameLen, tmpPathBuf1) <= 0 ||
- convertFilename(newName, newNameLen, tmpPathBuf2) <= 0)
- {
- Trace("DRM_file_rename: bad filename");
- }
- else if (rename(tmpPathBuf1, tmpPathBuf2) != 0)
- {
- Trace("DRM_file_rename: failed errno=%d", errno);
- }
- else /* Success */
- {
- return DRM_FILE_SUCCESS;
- }
-
- return DRM_FILE_FAILURE;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_exists(const uint16_t *name, int32_t nameLen)
-{
- struct stat sbuf;
-
- Trace("DRM_file_exists: %.*S", nameLen, name);
-
- /*remove trailing "/" separators, except the first "/" standing for root*/
- while ((nameLen > 1) && (name[nameLen -1] == '/'))
- --nameLen;
-
- if (getFileStat(name, nameLen, &sbuf))
- {
- Trace("DRM_file_exists: stat returns mode 0x%x", sbuf.st_mode);
-
- if (S_ISDIR(sbuf.st_mode))
- return DRM_FILE_ISDIR;
- if (S_ISREG(sbuf.st_mode))
- return DRM_FILE_ISREG;
- }
-
- return DRM_FILE_FAILURE;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_open(const uint16_t *name, int32_t nameLen, int32_t mode,
- int32_t* handle)
-{
- int res;
-
-#if DRM_FILE_MODE_READ != 1 || DRM_FILE_MODE_WRITE != 2
-#error constants changed
-#endif
-
- /* Convert DRM file modes to posix modes */
- static const int modes[4] =
- { 0,
- O_RDONLY,
- O_WRONLY | O_CREAT,
- O_RDWR | O_CREAT
- };
-
- Trace("DRM_file_open %.*S mode 0x%x", nameLen, name, mode);
-
- assert((mode & ~(DRM_FILE_MODE_READ|DRM_FILE_MODE_WRITE)) == 0);
-
- if (convertFilename(name, nameLen, tmpPathBuf1) <= 0)
- {
- Trace("DRM_file_open: bad filename");
- return DRM_FILE_FAILURE;
- }
-
- if ((res = open(tmpPathBuf1, modes[mode], 0777)) == -1)
- {
- Trace("DRM_file_open: open failed errno=%d", errno);
- return DRM_FILE_FAILURE;
- }
-
- Trace("DRM_file_open: open '%s; returned %d", tmpPathBuf1, res);
- *handle = res;
-
- return DRM_FILE_SUCCESS;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_read(int32_t handle, uint8_t* dst, int32_t length)
-{
- int n;
-
- assert(length > 0);
-
- /* TODO: Make dst a void *? */
-
- n = read((int)handle, dst, (size_t)length);
- if (n > 0)
- {
- Trace("DRM_file_read handle=%d read %d bytes", handle, n);
- return n;
- }
- else if (n == 0)
- {
- Trace("DRM_file_read read EOF: handle=%d", handle);
- return DRM_FILE_EOF;
- }
- else
- {
- Trace("DRM_file_read failed handle=%d, errno=%d", handle, errno);
- return DRM_FILE_FAILURE;
- }
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_write(int32_t handle, const uint8_t* src, int32_t length)
-{
- /* TODO: Make dst a void *? */
- int n;
-#ifndef DEVICE_FILESYSTEM
- int delta;
- off_t prevPos;
- struct stat sbuf;
- int prevFileSize;
-#endif
-
- assert(length >= 0);
-
-#ifndef DEVICE_FILESYSTEM
- if ( -1 == fstat((int)handle, &sbuf) )
- {
- Trace("DRM_file_write: fstat error %d", errno);
- return DRM_FILE_FAILURE;
- }
- prevFileSize = (int)(sbuf.st_size);
- prevPos = lseek( (int)handle, 0, SEEK_CUR);
- if ( (off_t)-1 == prevPos )
- {
- Trace("DRM_file_write: get current pos error %d", errno);
- return DRM_FILE_FAILURE;
- }
- delta = (int)prevPos + length - prevFileSize;
- if (delta > availableSize)
- {
- Trace("DRM_file_write: not enough size!");
- return DRM_FILE_FAILURE;
- }
-#endif
- n = write((int)handle, src, (size_t)length);
- if (n < 0)
- {
- Trace("DRM_file_write failed errno=%d", errno);
- return DRM_FILE_FAILURE;
- }
-#ifndef DEVICE_FILESYSTEM
- delta = prevPos + n - prevFileSize;
-
- if ( delta > 0 )
- {
- availableSize -= delta;
- }
-#endif
- Trace("DRM_file_write handle=%d wrote %d/%d bytes", handle, n, length);
-
- return n;
-}
-
-/* see drm_file.h */
-int32_t DRM_file_close(int32_t handle)
-{
- if (close((int)handle) == 0)
- {
- Trace("DRM_file_close handle=%d success", handle);
- return DRM_FILE_SUCCESS;
- }
-
- Trace("DRM_file_close handle=%d failed", handle);
- return DRM_FILE_FAILURE;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_setPosition(int32_t handle, int32_t value)
-{
-#ifndef DEVICE_FILESYSTEM
- struct stat sbuf;
-#endif
- off_t newPos;
-
- if (value < 0)
- {
- Trace("DRM_file_setPosition: handle=%d negative value (%d)",
- handle, value);
- return DRM_FILE_FAILURE;
- }
-
-#ifndef DEVICE_FILESYSTEM
- if ( fstat((int)handle, &sbuf) == -1 )
- {
- Trace("DRM_file_setPosition: fstat fail errno=%d", errno);
- return DRM_FILE_FAILURE;
- }
-
- if ( ((off_t)value > sbuf.st_size) &&
- (availableSize < (value - (int)(sbuf.st_size))) )
- {
- Trace("DRM_file_setPosition: not enough space");
- return DRM_FILE_FAILURE;
- }
-#endif
-
- newPos = lseek( (int)handle, (off_t)value, SEEK_SET);
- if ( newPos == (off_t)-1 )
- {
- Trace("DRM_file_setPosition: seek failed: errno=%d", errno);
- }
- else
- {
-#ifndef DEVICE_FILESYSTEM
- if ( newPos > sbuf.st_size )
- {
- availableSize -= (int)(newPos - sbuf.st_size);
- }
-#endif
- return DRM_FILE_SUCCESS;
- }
-
- return DRM_FILE_FAILURE;
-}
-
-/* see drm_file.h */
-int32_t
-DRM_file_mkdir(const uint16_t* name, int32_t nameChars)
-{
- Trace("DRM_file_mkdir started!..");
-
- if (convertFilename(name, nameChars, tmpPathBuf1) <= 0)
- {
- Trace("DRM_file_mkdir: bad filename");
- return DRM_FILE_FAILURE;
- }
-
- if (mkdir(tmpPathBuf1,0777) != 0)
- {
- Trace("DRM_file_mkdir failed!errno=%d",errno);
- return DRM_FILE_FAILURE;
- }
-
- return DRM_FILE_SUCCESS;
-}
diff --git a/media/libdrm/mobile1/src/objmng/drm_i18n.c b/media/libdrm/mobile1/src/objmng/drm_i18n.c
deleted file mode 100644
index b1118a9..0000000
--- a/media/libdrm/mobile1/src/objmng/drm_i18n.c
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * 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.
- */
-
-#include <objmng/drm_i18n.h>
-
-#define IS_GB2312_HIGH_BYTE(c) ((c) >= 0xA1 && (c) <= 0xF7)
-#define IS_GB2312_LOW_BYTE(c) ((c) >= 0xA1 && (c) <= 0xFE)
-#define IS_GBK_HIGH_BYTE(c) ((c) >= 0x81 && (c) <= 0xFE)
-#define IS_GBK_LOW_BYTE(c) ((c) >= 0x40 && (c) <= 0xFE && (c) != 0x7F)
-#define IS_BIG5_HIGH_BYTE(c) ((c) >= 0xA1 && (c) <= 0xF9)
-#define IS_BIG5_LOW_BYTE(c) (((c) >= 0x40 && (c) <= 0x7E) \
- || ((c) >= 0xA1 && (c) <= 0xFE))
-#define IS_ASCII(c) ((c) <= 127)
-
-#define INVALID_UNICODE 0xFFFD
-
-#define I18N_LATIN1_SUPPORT
-#define I18N_UTF8_UTF16_SUPPORT
-
-
-/**
- * Simply convert ISO 8859-1 (latin1) to unicode
- */
-static int32_t latin1ToWcs(const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed);
-
-/**
- * Convert one unicode char to ISO 8859-1 (latin1) byte
- */
-static int32_t wcToLatin1(uint16_t wc, uint8_t * mbs, int32_t bufSize);
-
-/**
- * Convert UTF-8 to unicode
- */
-static int32_t utf8ToWcs(const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed);
-
-/**
- * Convert one unicode char to UTF-8 bytes
- */
-static int32_t wcToUtf8(uint16_t wc, uint8_t * mbs, int32_t bufSize);
-
-/**
- * Convert UTF-16 BE to unicode
- */
-static int32_t utf16beToWcs(const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed);
-
-/**
- * Convert one unicode char to UTF-16 BE bytes
- */
-static int32_t wcToUtf16be(uint16_t wc, uint8_t * mbs, int32_t bufSize);
-
-/**
- * Convert UTF-16 LE to unicode
- */
-static int32_t utf16leToWcs(const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed);
-
-/**
- * Convert one unicode char to UTF-16 LE bytes
- */
-static int32_t wcToUtf16le(uint16_t wc, uint8_t * mbs, int32_t bufSize);
-
-/*
- * see drm_i18n.h
- */
-int32_t DRM_i18n_mbsToWcs(DRM_Charset_t charset,
- const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed)
-{
- switch (charset)
- {
-#ifdef I18N_GB2312_SUPPORT
- case DRM_CHARSET_GB2312:
- return gb2312ToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
-#endif
-#ifdef I18N_GBK_SUPPORT
- case DRM_CHARSET_GBK:
- return gbkToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
-#endif
-#ifdef I18N_BIG5_SUPPORT
- case DRM_CHARSET_BIG5:
- return big5ToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
-#endif
-#ifdef I18N_LATIN1_SUPPORT
- case DRM_CHARSET_LATIN1:
- return latin1ToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
-#endif
-#ifdef I18N_ISO8859X_SUPPORT
- case DRM_CHARSET_LATIN2:
- case DRM_CHARSET_LATIN3:
- case DRM_CHARSET_LATIN4:
- case DRM_CHARSET_CYRILLIC:
- case DRM_CHARSET_ARABIC:
- case DRM_CHARSET_GREEK:
- case DRM_CHARSET_HEBREW:
- case DRM_CHARSET_LATIN5:
- case DRM_CHARSET_LATIN6:
- case DRM_CHARSET_THAI:
- case DRM_CHARSET_LATIN7:
- case DRM_CHARSET_LATIN8:
- case DRM_CHARSET_LATIN9:
- case DRM_CHARSET_LATIN10:
- return iso8859xToWcs(charset, mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
-#endif
-#ifdef I18N_UTF8_UTF16_SUPPORT
- case DRM_CHARSET_UTF8:
- return utf8ToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
- case DRM_CHARSET_UTF16BE:
- return utf16beToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
- case DRM_CHARSET_UTF16LE:
- return utf16leToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
-#endif
- default:
- return -1;
- }
-}
-
-/*
- * see drm_i18n.h
- */
-int32_t DRM_i18n_wcsToMbs(DRM_Charset_t charset,
- const uint16_t *wcs, int32_t wcsLen,
- uint8_t *mbsBuf, int32_t bufSizeInByte)
-{
- int32_t (* wcToMbFunc)(uint16_t, uint8_t *, int32_t);
- int32_t charIndex = 0;
- int32_t numMultiBytes = 0;
-
- switch (charset)
- {
-#ifdef I18N_LATIN1_SUPPORT
- case DRM_CHARSET_LATIN1:
- wcToMbFunc = wcToLatin1;
- break;
-#endif
-#ifdef I18N_UTF8_UTF16_SUPPORT
- case DRM_CHARSET_UTF8:
- wcToMbFunc = wcToUtf8;
- break;
- case DRM_CHARSET_UTF16BE:
- wcToMbFunc = wcToUtf16be;
- break;
- case DRM_CHARSET_UTF16LE:
- wcToMbFunc = wcToUtf16le;
- break;
-#endif
-#ifdef I18N_ISO8859X_SUPPORT
- case DRM_CHARSET_LATIN2:
- case DRM_CHARSET_LATIN3:
- case DRM_CHARSET_LATIN4:
- case DRM_CHARSET_CYRILLIC:
- case DRM_CHARSET_ARABIC:
- case DRM_CHARSET_GREEK:
- case DRM_CHARSET_HEBREW:
- case DRM_CHARSET_LATIN5:
- case DRM_CHARSET_LATIN6:
- case DRM_CHARSET_THAI:
- case DRM_CHARSET_LATIN7:
- case DRM_CHARSET_LATIN8:
- case DRM_CHARSET_LATIN9:
- case DRM_CHARSET_LATIN10:
- return wcsToIso8859x(charset, wcs, wcsLen, mbsBuf, bufSizeInByte);
-#endif
- default:
- return -1;
- }
-
- if (mbsBuf) {
- while (numMultiBytes < bufSizeInByte && charIndex < wcsLen) {
- /* TODO: handle surrogate pair values here */
- int32_t mbLen = wcToMbFunc(wcs[charIndex],
- &mbsBuf[numMultiBytes], bufSizeInByte - numMultiBytes);
-
- if (numMultiBytes + mbLen > bufSizeInByte) {
- /* Insufficient buffer. Don't update numMultiBytes */
- break;
- }
- charIndex++;
- numMultiBytes += mbLen;
- }
- } else {
- while (charIndex < wcsLen) {
- /* TODO: handle surrogate pair values here */
- numMultiBytes += wcToMbFunc(wcs[charIndex], NULL, 0);
- charIndex++;
- }
- }
-
- return numMultiBytes;
-}
-
-
-#ifdef I18N_LATIN1_SUPPORT
-
-int32_t latin1ToWcs(const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed)
-{
- int32_t charsToConvert;
- int32_t len;
-
- if (wcsBuf == NULL) {
- return mbsLen;
- }
-
- len = charsToConvert = mbsLen > bufSizeInWideChar ? bufSizeInWideChar : mbsLen;
- if (len < 0)
- return 0;
- while (len--) {
- *wcsBuf++ = *mbs++;
- }
-
- if (bytesConsumed)
- *bytesConsumed = charsToConvert;
-
- return charsToConvert;
-}
-
-int32_t wcToLatin1(uint16_t wc, uint8_t * mbs, int32_t bufSize)
-{
- uint8_t ch;
-
- if (wc < 0x100) {
- ch = (uint8_t)(wc & 0xff);
- } else {
- ch = '?';
- }
- if (mbs && bufSize > 0)
- *mbs = ch;
- return 1;
-}
-
-#endif /* I18N_LATIN1_SUPPORT */
-
-#ifdef I18N_UTF8_UTF16_SUPPORT
-
-int32_t utf8ToWcs(const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed)
-{
- int32_t charsConverted = 0;
- int32_t i = 0;
- int32_t wideChar;
-
- if (wcsBuf == NULL) {
- /* No conversion but we're still going to calculate bytesConsumed */
- bufSizeInWideChar = mbsLen * 2;
- }
-
- while((i < mbsLen) && (charsConverted < bufSizeInWideChar)) {
- uint8_t ch = mbs[i];
- uint8_t ch2, ch3, ch4;
-
- wideChar = -1;
-
- if(IS_ASCII(ch)) {
- wideChar = ch;
- i++;
- } else if ((ch & 0xc0) == 0xc0) {
- int utfStart = i;
- if ((ch & 0xe0) == 0xc0) {
- /* 2 byte sequence */
- if (i + 1 < mbsLen && ((ch2 = mbs[i + 1]) & 0xc0) == 0x80) {
- wideChar = (uint16_t)(((ch & 0x1F) << 6) | (ch2 & 0x3F));
- i += 2;
- } else {
- /* skip incomplete sequence */
- i++;
- }
- } else if ((ch & 0xf0) == 0xe0) {
- /* 3 byte sequence */
- if (i + 2 < mbsLen
- && ((ch2 = mbs[i + 1]) & 0xc0) == 0x80
- && ((ch3 = mbs[i + 2]) & 0xc0) == 0x80) {
- wideChar = (uint16_t)(((ch & 0x0F) << 12) | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F));
- i += 3;
- } else {
- /* skip incomplete sequence (up to 2 bytes) */
- i++;
- if (i < mbsLen && (mbs[i] & 0xc0) == 0x80)
- i++;
- }
- } else if ((ch & 0xf8) == 0xf0) {
- /* 4 byte sequence */
- if (i + 3 < mbsLen
- && ((ch2 = mbs[i + 1]) & 0xc0) == 0x80
- && ((ch3 = mbs[i + 2]) & 0xc0) == 0x80
- && ((ch4 = mbs[i + 3]) & 0xc0) == 0x80) {
- /* FIXME: we do NOT support U+10000 - U+10FFFF for now.
- * leave it as 0xFFFD. */
- wideChar = INVALID_UNICODE;
- i += 4;
- } else {
- /* skip incomplete sequence (up to 3 bytes) */
- i++;
- if (i < mbsLen && (mbs[i] & 0xc0) == 0x80) {
- i++;
- if (i < mbsLen && (mbs[i] & 0xc0) == 0x80) {
- i++;
- }
- }
- }
- } else {
- /* invalid */
- i++;
- }
- if (i >= mbsLen && wideChar == -1) {
- /* Possible incomplete UTF-8 sequence at the end of mbs.
- * Leave it to the caller.
- */
- i = utfStart;
- break;
- }
- } else {
- /* invalid */
- i++;
- }
- if(wcsBuf) {
- if (wideChar == -1)
- wideChar = INVALID_UNICODE;
- wcsBuf[charsConverted] = (uint16_t)wideChar;
- }
- charsConverted++;
- }
-
- if (bytesConsumed)
- *bytesConsumed = i;
-
- return charsConverted;
-}
-
-int32_t wcToUtf8(uint16_t wc, uint8_t * mbs, int32_t bufSize)
-{
- if (wc <= 0x7f) {
- if (mbs && (bufSize >= 1)) {
- *mbs = (uint8_t)wc;
- }
- return 1;
- } else if (wc <= 0x7ff) {
- if (mbs && (bufSize >= 2)) {
- *mbs++ = (uint8_t)((wc >> 6) | 0xc0);
- *mbs = (uint8_t)((wc & 0x3f) | 0x80);
- }
- return 2;
- } else {
- if (mbs && (bufSize >= 3)) {
- *mbs++ = (uint8_t)((wc >> 12) | 0xe0);
- *mbs++ = (uint8_t)(((wc >> 6) & 0x3f)| 0x80);
- *mbs = (uint8_t)((wc & 0x3f) | 0x80);
- }
- return 3;
- }
-}
-
-int32_t utf16beToWcs(const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed)
-{
- int32_t charsToConvert;
- int32_t len;
-
- if (wcsBuf == NULL) {
- return mbsLen / 2;
- }
-
- len = charsToConvert = (mbsLen / 2) > bufSizeInWideChar ? bufSizeInWideChar : (mbsLen / 2);
- while (len--) {
- /* TODO: handle surrogate pair values */
- *wcsBuf++ = (uint16_t)((*mbs << 8) | *(mbs + 1));
- mbs += 2;
- }
-
- if (bytesConsumed)
- *bytesConsumed = charsToConvert * 2;
-
- return charsToConvert;
-}
-
-int32_t wcToUtf16be(uint16_t wc, uint8_t * mbs, int32_t bufSize)
-{
- if (mbs && bufSize >= 2) {
- /* TODO: handle surrogate pair values */
- *mbs = (uint8_t)(wc >> 8);
- *(mbs + 1) = (uint8_t)(wc & 0xff);
- }
- return 2;
-}
-
-int32_t utf16leToWcs(const uint8_t *mbs, int32_t mbsLen,
- uint16_t *wcsBuf, int32_t bufSizeInWideChar,
- int32_t *bytesConsumed)
-{
- int32_t charsToConvert;
- int32_t len;
-
- if (wcsBuf == NULL) {
- return mbsLen / 2;
- }
-
- len = charsToConvert = (mbsLen / 2) > bufSizeInWideChar ? bufSizeInWideChar : (mbsLen / 2);
- while (len--) {
- /* TODO: handle surrogate pair values */
- *wcsBuf++ = (uint16_t)(*mbs | (*(mbs + 1) << 8));
- mbs += 2;
- }
-
- if (bytesConsumed)
- *bytesConsumed = charsToConvert * 2;
-
- return charsToConvert;
-}
-
-int32_t wcToUtf16le(uint16_t wc, uint8_t * mbs, int32_t bufSize)
-{
- if (mbs && bufSize >= 2) {
- /* TODO: handle surrogate pair values */
- *mbs = (uint8_t)(wc & 0xff);
- *(mbs + 1) = (uint8_t)(wc >> 8);
- }
- return 2;
-}
-
-#endif /* I18N_UTF8_UTF16_SUPPORT */
-
diff --git a/media/libdrm/mobile1/src/objmng/drm_rights_manager.c b/media/libdrm/mobile1/src/objmng/drm_rights_manager.c
deleted file mode 100644
index df22327..0000000
--- a/media/libdrm/mobile1/src/objmng/drm_rights_manager.c
+++ /dev/null
@@ -1,688 +0,0 @@
-/*
- * 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.
- */
-
-#include <drm_rights_manager.h>
-#include <drm_inner.h>
-#include <drm_file.h>
-#include <drm_i18n.h>
-
-static int32_t drm_getString(uint8_t* string, int32_t len, int32_t handle)
-{
- int32_t i;
-
- for (i = 0; i < len; i++) {
- if (DRM_FILE_FAILURE == DRM_file_read(handle, &string[i], 1))
- return FALSE;
- if (string[i] == '\n') {
- string[i + 1] = '\0';
- break;
- }
- }
- return TRUE;
-}
-
-static int32_t drm_putString(uint8_t* string, int32_t handle)
-{
- int32_t i = 0;
-
- for (i = 0;; i++) {
- if (string[i] == '\0')
- break;
- if (DRM_FILE_FAILURE == DRM_file_write(handle, &string[i], 1))
- return FALSE;
- }
- return TRUE;
-}
-
-static int32_t drm_writeToUidTxt(uint8_t* Uid, int32_t* id)
-{
- int32_t length;
- int32_t i;
- uint8_t idStr[8];
- int32_t idMax;
- uint8_t(*uidStr)[256];
- uint16_t nameUcs2[MAX_FILENAME_LEN];
- int32_t nameLen;
- int32_t bytesConsumed;
- int32_t handle;
- int32_t fileRes;
-
- if (*id < 1)
- return FALSE;
-
- /* convert in ucs2 */
- nameLen = strlen(DRM_UID_FILE_PATH);
- nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
- (uint8_t *)DRM_UID_FILE_PATH,
- nameLen,
- nameUcs2,
- MAX_FILENAME_LEN,
- &bytesConsumed);
- fileRes = DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_READ,
- &handle);
- if (DRM_FILE_SUCCESS != fileRes) {
- DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_WRITE,
- &handle);
- DRM_file_write(handle, (uint8_t *)"0\n", 2);
- DRM_file_close(handle);
- DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_READ,
- &handle);
- }
-
- if (!drm_getString(idStr, 8, handle)) {
- DRM_file_close(handle);
- return FALSE;
- }
- idMax = atoi((char *)idStr);
-
- if (idMax < *id)
- uidStr = malloc((idMax + 1) * 256);
- else
- uidStr = malloc(idMax * 256);
-
- for (i = 0; i < idMax; i++) {
- if (!drm_getString(uidStr[i], 256, handle)) {
- DRM_file_close(handle);
- free(uidStr);
- return FALSE;
- }
- }
- length = strlen((char *)Uid);
- strcpy((char *)uidStr[*id - 1], (char *)Uid);
- uidStr[*id - 1][length] = '\n';
- uidStr[*id - 1][length + 1] = '\0';
- if (idMax < (*id))
- idMax++;
- DRM_file_close(handle);
-
- DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_WRITE,
- &handle);
- sprintf((char *)idStr, "%d", idMax);
-
- if (!drm_putString(idStr, handle)) {
- DRM_file_close(handle);
- free(uidStr);
- return FALSE;
- }
- if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t *)"\n", 1)) {
- DRM_file_close(handle);
- free(uidStr);
- return FALSE;
- }
- for (i = 0; i < idMax; i++) {
- if (!drm_putString(uidStr[i], handle)) {
- DRM_file_close(handle);
- free(uidStr);
- return FALSE;
- }
- }
- if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t *)"\n", 1)) {
- DRM_file_close(handle);
- free(uidStr);
- return FALSE;
- }
- DRM_file_close(handle);
- free(uidStr);
- return TRUE;
-}
-
-/* See objmng_files.h */
-int32_t drm_readFromUidTxt(uint8_t* Uid, int32_t* id, int32_t option)
-{
- int32_t i;
- uint8_t p[256] = { 0 };
- uint8_t idStr[8];
- int32_t idMax = 0;
- uint16_t nameUcs2[MAX_FILENAME_LEN];
- int32_t nameLen = 0;
- int32_t bytesConsumed;
- int32_t handle;
- int32_t fileRes;
-
- if (NULL == id || NULL == Uid)
- return FALSE;
-
- DRM_file_startup();
-
- /* convert in ucs2 */
- nameLen = strlen(DRM_UID_FILE_PATH);
- nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
- (uint8_t *)DRM_UID_FILE_PATH,
- nameLen,
- nameUcs2,
- MAX_FILENAME_LEN,
- &bytesConsumed);
- fileRes = DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_READ,
- &handle);
- if (DRM_FILE_SUCCESS != fileRes) {
- DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_WRITE,
- &handle);
- DRM_file_write(handle, (uint8_t *)"0\n", 2);
- DRM_file_close(handle);
- DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_READ,
- &handle);
- }
-
- if (!drm_getString(idStr, 8, handle)) {
- DRM_file_close(handle);
- return FALSE;
- }
- idMax = atoi((char *)idStr);
-
- if (option == GET_UID) {
- if (*id < 1 || *id > idMax) {
- DRM_file_close(handle);
- return FALSE;
- }
- for (i = 1; i <= *id; i++) {
- if (!drm_getString(Uid, 256, handle)) {
- DRM_file_close(handle);
- return FALSE;
- }
- }
- DRM_file_close(handle);
- return TRUE;
- }
- if (option == GET_ID) {
- *id = -1;
- for (i = 1; i <= idMax; i++) {
- if (!drm_getString(p, 256, handle)) {
- DRM_file_close(handle);
- return FALSE;
- }
- if (strstr((char *)p, (char *)Uid) != NULL
- && strlen((char *)p) == strlen((char *)Uid) + 1) {
- *id = i;
- DRM_file_close(handle);
- return TRUE;
- }
- if ((*id == -1) && (strlen((char *)p) < 3))
- *id = i;
- }
- if (*id != -1) {
- DRM_file_close(handle);
- return FALSE;
- }
- *id = idMax + 1;
- DRM_file_close(handle);
- return FALSE;
- }
- DRM_file_close(handle);
- return FALSE;
-}
-
-static int32_t drm_acquireId(uint8_t* uid, int32_t* id)
-{
- if (TRUE == drm_readFromUidTxt(uid, id, GET_ID))
- return TRUE;
-
- drm_writeToUidTxt(uid, id);
-
- return FALSE; /* The Uid is not exit, then return FALSE indicate it */
-}
-
-int32_t drm_writeOrReadInfo(int32_t id, T_DRM_Rights* Ro, int32_t* RoAmount, int32_t option)
-{
- uint8_t fullname[MAX_FILENAME_LEN] = {0};
- int32_t tmpRoAmount;
- uint16_t nameUcs2[MAX_FILENAME_LEN];
- int32_t nameLen = 0;
- int32_t bytesConsumed;
- int32_t handle;
- int32_t fileRes;
-
- sprintf((char *)fullname, ANDROID_DRM_CORE_PATH"%d"EXTENSION_NAME_INFO, id);
-
- /* convert in ucs2 */
- nameLen = strlen((char *)fullname);
- nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
- fullname,
- nameLen,
- nameUcs2,
- MAX_FILENAME_LEN,
- &bytesConsumed);
- fileRes = DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_READ,
- &handle);
- if (DRM_FILE_SUCCESS != fileRes) {
- if (GET_ALL_RO == option || GET_A_RO == option)
- return FALSE;
-
- if (GET_ROAMOUNT == option) {
- *RoAmount = -1;
- return TRUE;
- }
- }
-
- DRM_file_close(handle);
- DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_READ | DRM_FILE_MODE_WRITE,
- &handle);
-
- switch(option) {
- case GET_ROAMOUNT:
- if (DRM_FILE_FAILURE == DRM_file_read(handle, (uint8_t*)RoAmount, sizeof(int32_t))) {
- DRM_file_close(handle);
- return FALSE;
- }
- break;
- case GET_ALL_RO:
- DRM_file_setPosition(handle, sizeof(int32_t));
-
- if (DRM_FILE_FAILURE == DRM_file_read(handle, (uint8_t*)Ro, (*RoAmount) * sizeof(T_DRM_Rights))) {
- DRM_file_close(handle);
- return FALSE;
- }
- break;
- case SAVE_ALL_RO:
- if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t*)RoAmount, sizeof(int32_t))) {
- DRM_file_close(handle);
- return FALSE;
- }
-
- if (NULL != Ro && *RoAmount >= 1) {
- if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t*) Ro, (*RoAmount) * sizeof(T_DRM_Rights))) {
- DRM_file_close(handle);
- return FALSE;
- }
- }
- break;
- case GET_A_RO:
- DRM_file_setPosition(handle, sizeof(int32_t) + (*RoAmount - 1) * sizeof(T_DRM_Rights));
-
- if (DRM_FILE_FAILURE == DRM_file_read(handle, (uint8_t*)Ro, sizeof(T_DRM_Rights))) {
- DRM_file_close(handle);
- return FALSE;
- }
- break;
- case SAVE_A_RO:
- DRM_file_setPosition(handle, sizeof(int32_t) + (*RoAmount - 1) * sizeof(T_DRM_Rights));
-
- if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t*)Ro, sizeof(T_DRM_Rights))) {
- DRM_file_close(handle);
- return FALSE;
- }
-
- DRM_file_setPosition(handle, 0);
- if (DRM_FILE_FAILURE == DRM_file_read(handle, (uint8_t*)&tmpRoAmount, sizeof(int32_t))) {
- DRM_file_close(handle);
- return FALSE;
- }
- if (tmpRoAmount < *RoAmount) {
- DRM_file_setPosition(handle, 0);
- DRM_file_write(handle, (uint8_t*)RoAmount, sizeof(int32_t));
- }
- break;
- default:
- DRM_file_close(handle);
- return FALSE;
- }
-
- DRM_file_close(handle);
- return TRUE;
-}
-
-int32_t drm_appendRightsInfo(T_DRM_Rights* rights)
-{
- int32_t id;
- int32_t roAmount;
-
- if (NULL == rights)
- return FALSE;
-
- drm_acquireId(rights->uid, &id);
-
- if (FALSE == drm_writeOrReadInfo(id, NULL, &roAmount, GET_ROAMOUNT))
- return FALSE;
-
- if (-1 == roAmount)
- roAmount = 0;
-
- /* The RO amount increase */
- roAmount++;
-
- /* Save the rights information */
- if (FALSE == drm_writeOrReadInfo(id, rights, &roAmount, SAVE_A_RO))
- return FALSE;
-
- return TRUE;
-}
-
-int32_t drm_getMaxIdFromUidTxt()
-{
- uint8_t idStr[8];
- int32_t idMax = 0;
- uint16_t nameUcs2[MAX_FILENAME_LEN] = {0};
- int32_t nameLen = 0;
- int32_t bytesConsumed;
- int32_t handle;
- int32_t fileRes;
-
- /* convert in ucs2 */
- nameLen = strlen(DRM_UID_FILE_PATH);
- nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
- (uint8_t *)DRM_UID_FILE_PATH,
- nameLen,
- nameUcs2,
- MAX_FILENAME_LEN,
- &bytesConsumed);
- fileRes = DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_READ,
- &handle);
-
- /* this means the uid.txt file is not exist, so there is not any DRM object */
- if (DRM_FILE_SUCCESS != fileRes)
- return 0;
-
- if (!drm_getString(idStr, 8, handle)) {
- DRM_file_close(handle);
- return -1;
- }
- DRM_file_close(handle);
-
- idMax = atoi((char *)idStr);
- return idMax;
-}
-
-int32_t drm_removeIdInfoFile(int32_t id)
-{
- uint8_t filename[MAX_FILENAME_LEN] = {0};
- uint16_t nameUcs2[MAX_FILENAME_LEN];
- int32_t nameLen = 0;
- int32_t bytesConsumed;
-
- if (id <= 0)
- return FALSE;
-
- sprintf((char *)filename, ANDROID_DRM_CORE_PATH"%d"EXTENSION_NAME_INFO, id);
-
- /* convert in ucs2 */
- nameLen = strlen((char *)filename);
- nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
- filename,
- nameLen,
- nameUcs2,
- MAX_FILENAME_LEN,
- &bytesConsumed);
- if (DRM_FILE_SUCCESS != DRM_file_delete(nameUcs2, nameLen))
- return FALSE;
-
- return TRUE;
-}
-
-int32_t drm_updateUidTxtWhenDelete(int32_t id)
-{
- uint16_t nameUcs2[MAX_FILENAME_LEN];
- int32_t nameLen = 0;
- int32_t bytesConsumed;
- int32_t handle;
- int32_t fileRes;
- int32_t bufferLen;
- uint8_t *buffer;
- uint8_t idStr[8];
- int32_t idMax;
-
- if (id <= 0)
- return FALSE;
-
- nameLen = strlen(DRM_UID_FILE_PATH);
- nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
- (uint8_t *)DRM_UID_FILE_PATH,
- nameLen,
- nameUcs2,
- MAX_FILENAME_LEN,
- &bytesConsumed);
- bufferLen = DRM_file_getFileLength(nameUcs2, nameLen);
- if (bufferLen <= 0)
- return FALSE;
-
- buffer = (uint8_t *)malloc(bufferLen);
- if (NULL == buffer)
- return FALSE;
-
- fileRes = DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_READ,
- &handle);
- if (DRM_FILE_SUCCESS != fileRes) {
- free(buffer);
- return FALSE;
- }
-
- drm_getString(idStr, 8, handle);
- idMax = atoi((char *)idStr);
-
- bufferLen -= strlen((char *)idStr);
- fileRes = DRM_file_read(handle, buffer, bufferLen);
- buffer[bufferLen] = '\0';
- DRM_file_close(handle);
-
- /* handle this buffer */
- {
- uint8_t *pStart, *pEnd;
- int32_t i, movLen;
-
- pStart = buffer;
- pEnd = pStart;
- for (i = 0; i < id; i++) {
- if (pEnd != pStart)
- pStart = ++pEnd;
- while ('\n' != *pEnd)
- pEnd++;
- if (pStart == pEnd)
- pStart--;
- }
- movLen = bufferLen - (pEnd - buffer);
- memmove(pStart, pEnd, movLen);
- bufferLen -= (pEnd - pStart);
- }
-
- if (DRM_FILE_SUCCESS != DRM_file_delete(nameUcs2, nameLen)) {
- free(buffer);
- return FALSE;
- }
-
- fileRes = DRM_file_open(nameUcs2,
- nameLen,
- DRM_FILE_MODE_WRITE,
- &handle);
- if (DRM_FILE_SUCCESS != fileRes) {
- free(buffer);
- return FALSE;
- }
- sprintf((char *)idStr, "%d", idMax);
- drm_putString(idStr, handle);
- DRM_file_write(handle, (uint8_t*)"\n", 1);
- DRM_file_write(handle, buffer, bufferLen);
- free(buffer);
- DRM_file_close(handle);
- return TRUE;
-}
-
-int32_t drm_getKey(uint8_t* uid, uint8_t* KeyValue)
-{
- T_DRM_Rights ro;
- int32_t id, roAmount;
-
- if (NULL == uid || NULL == KeyValue)
- return FALSE;
-
- if (FALSE == drm_readFromUidTxt(uid, &id, GET_ID))
- return FALSE;
-
- if (FALSE == drm_writeOrReadInfo(id, NULL, &roAmount, GET_ROAMOUNT))
- return FALSE;
-
- if (roAmount <= 0)
- return FALSE;
-
- memset(&ro, 0, sizeof(T_DRM_Rights));
- roAmount = 1;
- if (FALSE == drm_writeOrReadInfo(id, &ro, &roAmount, GET_A_RO))
- return FALSE;
-
- memcpy(KeyValue, ro.KeyValue, DRM_KEY_LEN);
- return TRUE;
-}
-
-void drm_discardPaddingByte(uint8_t *decryptedBuf, int32_t *decryptedBufLen)
-{
- int32_t tmpLen = *decryptedBufLen;
- int32_t i;
-
- if (NULL == decryptedBuf || *decryptedBufLen < 0)
- return;
-
- /* Check whether the last several bytes are padding or not */
- for (i = 1; i < decryptedBuf[tmpLen - 1]; i++) {
- if (decryptedBuf[tmpLen - 1 - i] != decryptedBuf[tmpLen - 1])
- break; /* Not the padding bytes */
- }
- if (i == decryptedBuf[tmpLen - 1]) /* They are padding bytes */
- *decryptedBufLen = tmpLen - i;
- return;
-}
-
-int32_t drm_aesDecBuffer(uint8_t * Buffer, int32_t * BufferLen, AES_KEY *key)
-{
- uint8_t dbuf[3 * DRM_ONE_AES_BLOCK_LEN], buf[DRM_ONE_AES_BLOCK_LEN];
- uint64_t i, len, wlen = DRM_ONE_AES_BLOCK_LEN, curLen, restLen;
- uint8_t *pTarget, *pTargetHead;
-
- pTargetHead = Buffer;
- pTarget = Buffer;
- curLen = 0;
- restLen = *BufferLen;
-
- if (restLen > 2 * DRM_ONE_AES_BLOCK_LEN) {
- len = 2 * DRM_ONE_AES_BLOCK_LEN;
- } else {
- len = restLen;
- }
- memcpy(dbuf, Buffer, (size_t)len);
- restLen -= len;
- Buffer += len;
-
- if (len < 2 * DRM_ONE_AES_BLOCK_LEN) { /* The original file is less than one block in length */
- len -= DRM_ONE_AES_BLOCK_LEN;
- /* Decrypt from position len to position len + DRM_ONE_AES_BLOCK_LEN */
- AES_decrypt((dbuf + len), (dbuf + len), key);
-
- /* Undo the CBC chaining */
- for (i = 0; i < len; ++i)
- dbuf[i] ^= dbuf[i + DRM_ONE_AES_BLOCK_LEN];
-
- /* Output the decrypted bytes */
- memcpy(pTarget, dbuf, (size_t)len);
- pTarget += len;
- } else {
- uint8_t *b1 = dbuf, *b2 = b1 + DRM_ONE_AES_BLOCK_LEN, *b3 = b2 + DRM_ONE_AES_BLOCK_LEN, *bt;
-
- for (;;) { /* While some ciphertext remains, prepare to decrypt block b2 */
- /* Read in the next block to see if ciphertext stealing is needed */
- b3 = Buffer;
- if (restLen > DRM_ONE_AES_BLOCK_LEN) {
- len = DRM_ONE_AES_BLOCK_LEN;
- } else {
- len = restLen;
- }
- restLen -= len;
- Buffer += len;
-
- /* Decrypt the b2 block */
- AES_decrypt((uint8_t *)b2, buf, key);
-
- if (len == 0 || len == DRM_ONE_AES_BLOCK_LEN) { /* No ciphertext stealing */
- /* Unchain CBC using the previous ciphertext block in b1 */
- for (i = 0; i < DRM_ONE_AES_BLOCK_LEN; ++i)
- buf[i] ^= b1[i];
- } else { /* Partial last block - use ciphertext stealing */
- wlen = len;
- /* Produce last 'len' bytes of plaintext by xoring with */
- /* The lowest 'len' bytes of next block b3 - C[N-1] */
- for (i = 0; i < len; ++i)
- buf[i] ^= b3[i];
-
- /* Reconstruct the C[N-1] block in b3 by adding in the */
- /* Last (DRM_ONE_AES_BLOCK_LEN - len) bytes of C[N-2] in b2 */
- for (i = len; i < DRM_ONE_AES_BLOCK_LEN; ++i)
- b3[i] = buf[i];
-
- /* Decrypt the C[N-1] block in b3 */
- AES_decrypt((uint8_t *)b3, (uint8_t *)b3, key);
-
- /* Produce the last but one plaintext block by xoring with */
- /* The last but two ciphertext block */
- for (i = 0; i < DRM_ONE_AES_BLOCK_LEN; ++i)
- b3[i] ^= b1[i];
-
- /* Write decrypted plaintext blocks */
- memcpy(pTarget, b3, DRM_ONE_AES_BLOCK_LEN);
- pTarget += DRM_ONE_AES_BLOCK_LEN;
- }
-
- /* Write the decrypted plaintext block */
- memcpy(pTarget, buf, (size_t)wlen);
- pTarget += wlen;
-
- if (len != DRM_ONE_AES_BLOCK_LEN) {
- *BufferLen = pTarget - pTargetHead;
- return 0;
- }
-
- /* Advance the buffer pointers */
- bt = b1, b1 = b2, b2 = b3, b3 = bt;
- }
- }
- return 0;
-}
-
-int32_t drm_updateDcfDataLen(uint8_t* pDcfLastData, uint8_t* keyValue, int32_t* moreBytes)
-{
- AES_KEY key;
- int32_t len = DRM_TWO_AES_BLOCK_LEN;
-
- if (NULL == pDcfLastData || NULL == keyValue)
- return FALSE;
-
- AES_set_decrypt_key(keyValue, DRM_KEY_LEN * 8, &key);
-
- if (drm_aesDecBuffer(pDcfLastData, &len, &key) < 0)
- return FALSE;
-
- drm_discardPaddingByte(pDcfLastData, &len);
-
- *moreBytes = DRM_TWO_AES_BLOCK_LEN - len;
-
- return TRUE;
-}
diff --git a/media/libdrm/mobile1/src/objmng/drm_time.c b/media/libdrm/mobile1/src/objmng/drm_time.c
deleted file mode 100644
index fceb4952..0000000
--- a/media/libdrm/mobile1/src/objmng/drm_time.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * @file
- * DRM 1.0 Reference Port: linux implementation of drm_time.c.
- */
-
-#include <objmng/drm_time.h>
-#include <unistd.h>
-
-/* See drm_time.h */
-uint32_t DRM_time_getElapsedSecondsFrom1970(void)
-{
- return time(NULL);
-}
-
-/* See drm_time.h */
-void DRM_time_sleep(uint32_t ms)
-{
- usleep(ms * 1000);
-}
-
-/* See drm_time.h */
-void DRM_time_getSysTime(T_DB_TIME_SysTime *time_ptr)
-{
- time_t t;
- struct tm *tm_t;
-
- time(&t);
- tm_t = gmtime(&t);
-
- time_ptr->year = tm_t->tm_year + 1900;
- time_ptr->month = tm_t->tm_mon + 1;
- time_ptr->day = tm_t->tm_mday;
- time_ptr->hour = tm_t->tm_hour;
- time_ptr->min = tm_t->tm_min;
- time_ptr->sec = tm_t->tm_sec;
-}
diff --git a/media/libdrm/mobile1/src/parser/parser_dcf.c b/media/libdrm/mobile1/src/parser/parser_dcf.c
deleted file mode 100644
index 3eac120..0000000
--- a/media/libdrm/mobile1/src/parser/parser_dcf.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.
- */
-
-#include <parser_dcf.h>
-#include <svc_drm.h>
-
-static int32_t drm_parseUintVar(uint8_t * buffer, uint8_t * len)
-{
- int32_t i;
- int32_t byteLen;
- int32_t sum;
-
- if (NULL == buffer)
- return DRM_UINT_VAR_ERR;
-
- byteLen = 0;
- while ((buffer[byteLen] & UINT_VAR_FLAG) > 0 && byteLen < MAX_UINT_VAR_BYTE) /* UINT_VAR_FLAG == 0x80 */
- byteLen++;
-
- if (byteLen >= MAX_UINT_VAR_BYTE) /* MAX_UINT_VAR_BYTE == 5 */
- return DRM_UINT_VAR_ERR; /* The var int is too large, and that is impossible */
-
- *len = (uint8_t)(byteLen + 1);
- sum = buffer[byteLen];
- for (i = byteLen - 1; i >= 0; i--)
- sum += ((buffer[i] & UINT_VAR_DATA) << 7 * (byteLen - i)); /* UINT_VAR_DATA == 0x7F */
-
- return sum;
-}
-
-/* See parser_dcf.h */
-int32_t drm_dcfParser(uint8_t *buffer, int32_t bufferLen, T_DRM_DCF_Info *pDcfInfo,
- uint8_t **ppEncryptedData)
-{
- uint8_t *tmpBuf;
- uint8_t *pStart, *pEnd;
- uint8_t *pHeader, *pData;
- uint8_t varLen;
-
- if (NULL == buffer || bufferLen <= 0 || NULL == pDcfInfo)
- return FALSE;
-
- tmpBuf = buffer;
- /* 1. Parse the version, content-type and content-url */
- pDcfInfo->Version = *(tmpBuf++);
- if (0x01 != pDcfInfo->Version) /* Because it is OMA DRM v1.0, the vension must be 1 */
- return FALSE;
-
- pDcfInfo->ContentTypeLen = *(tmpBuf++);
- if (pDcfInfo->ContentTypeLen >= MAX_CONTENT_TYPE_LEN)
- return FALSE;
-
- pDcfInfo->ContentURILen = *(tmpBuf++);
- if (pDcfInfo->ContentURILen >= MAX_CONTENT_URI_LEN)
- return FALSE;
-
- strncpy((char *)pDcfInfo->ContentType, (char *)tmpBuf, pDcfInfo->ContentTypeLen);
- pDcfInfo->ContentType[MAX_CONTENT_TYPE_LEN - 1] = 0;
- tmpBuf += pDcfInfo->ContentTypeLen;
- strncpy((char *)pDcfInfo->ContentURI, (char *)tmpBuf, pDcfInfo->ContentURILen);
- pDcfInfo->ContentURI[MAX_CONTENT_URI_LEN - 1] = 0;
- tmpBuf += pDcfInfo->ContentURILen;
-
- /* 2. Get the headers length and data length */
- pDcfInfo->HeadersLen = drm_parseUintVar(tmpBuf, &varLen);
- if (DRM_UINT_VAR_ERR == pDcfInfo->HeadersLen)
- return FALSE;
- tmpBuf += varLen;
- pDcfInfo->DecryptedDataLen = DRM_UNKNOWN_DATA_LEN;
- pDcfInfo->EncryptedDataLen = drm_parseUintVar(tmpBuf, &varLen);
- if (DRM_UINT_VAR_ERR == pDcfInfo->EncryptedDataLen)
- return FALSE;
- tmpBuf += varLen;
- pHeader = tmpBuf;
- tmpBuf += pDcfInfo->HeadersLen;
- pData = tmpBuf;
-
- /* 3. Parse the headers */
- pStart = pHeader;
- while (pStart < pData) {
- pEnd = pStart;
- while ('\r' != *pEnd && pEnd < pData)
- pEnd++;
-
- if (0 == strncmp((char *)pStart, HEADER_ENCRYPTION_METHOD, HEADER_ENCRYPTION_METHOD_LEN)) {
- if ((pEnd - pStart - HEADER_ENCRYPTION_METHOD_LEN) >= MAX_ENCRYPTION_METHOD_LEN)
- return FALSE;
- strncpy((char *)pDcfInfo->Encryption_Method,
- (char *)(pStart + HEADER_ENCRYPTION_METHOD_LEN),
- pEnd - pStart - HEADER_ENCRYPTION_METHOD_LEN);
- pDcfInfo->Encryption_Method[MAX_ENCRYPTION_METHOD_LEN - 1] = 0;
- } else if (0 == strncmp((char *)pStart, HEADER_RIGHTS_ISSUER, HEADER_RIGHTS_ISSUER_LEN)) {
- if ((pEnd - pStart - HEADER_RIGHTS_ISSUER_LEN) >= MAX_RIGHTS_ISSUER_LEN)
- return FALSE;
- strncpy((char *)pDcfInfo->Rights_Issuer,
- (char *)(pStart + HEADER_RIGHTS_ISSUER_LEN),
- pEnd - pStart - HEADER_RIGHTS_ISSUER_LEN);
- pDcfInfo->Rights_Issuer[MAX_RIGHTS_ISSUER_LEN - 1] = 0;
- } else if (0 == strncmp((char *)pStart, HEADER_CONTENT_NAME, HEADER_CONTENT_NAME_LEN)) {
- if ((pEnd - pStart - HEADER_CONTENT_NAME_LEN) >= MAX_CONTENT_NAME_LEN)
- return FALSE;
- strncpy((char *)pDcfInfo->Content_Name,
- (char *)(pStart + HEADER_CONTENT_NAME_LEN),
- pEnd - pStart - HEADER_CONTENT_NAME_LEN);
- pDcfInfo->Content_Name[MAX_CONTENT_NAME_LEN - 1] = 0;
- } else if (0 == strncmp((char *)pStart, HEADER_CONTENT_DESCRIPTION, HEADER_CONTENT_DESCRIPTION_LEN)) {
- if ((pEnd - pStart - HEADER_CONTENT_DESCRIPTION_LEN) >= MAX_CONTENT_DESCRIPTION_LEN)
- return FALSE;
- strncpy((char *)pDcfInfo->ContentDescription,
- (char *)(pStart + HEADER_CONTENT_DESCRIPTION_LEN),
- pEnd - pStart - HEADER_CONTENT_DESCRIPTION_LEN);
- pDcfInfo->ContentDescription[MAX_CONTENT_DESCRIPTION_LEN - 1] = 0;
- } else if (0 == strncmp((char *)pStart, HEADER_CONTENT_VENDOR, HEADER_CONTENT_VENDOR_LEN)) {
- if ((pEnd - pStart - HEADER_CONTENT_VENDOR_LEN) >= MAX_CONTENT_VENDOR_LEN)
- return FALSE;
- strncpy((char *)pDcfInfo->ContentVendor,
- (char *)(pStart + HEADER_CONTENT_VENDOR_LEN),
- pEnd - pStart - HEADER_CONTENT_VENDOR_LEN);
- pDcfInfo->ContentVendor[MAX_CONTENT_VENDOR_LEN - 1] = 0;
- } else if (0 == strncmp((char *)pStart, HEADER_ICON_URI, HEADER_ICON_URI_LEN)) {
- if ((pEnd - pStart - HEADER_ICON_URI_LEN) >= MAX_ICON_URI_LEN)
- return FALSE;
- strncpy((char *)pDcfInfo->Icon_URI,
- (char *)(pStart + HEADER_ICON_URI_LEN),
- pEnd - pStart - HEADER_ICON_URI_LEN);
- pDcfInfo->Icon_URI[MAX_ICON_URI_LEN - 1] = 0;
- }
-
- if ('\n' == *(pEnd + 1))
- pStart = pEnd + 2; /* Two bytes: a '\r' and a '\n' */
- else
- pStart = pEnd + 1;
- }
-
- /* 4. Give out the location of encrypted data */
- if (NULL != ppEncryptedData)
- *ppEncryptedData = pData;
-
- return TRUE;
-}
diff --git a/media/libdrm/mobile1/src/parser/parser_dm.c b/media/libdrm/mobile1/src/parser/parser_dm.c
deleted file mode 100644
index 4b4a2da..0000000
--- a/media/libdrm/mobile1/src/parser/parser_dm.c
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * 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.
- */
-
-#include <parser_dm.h>
-#include <parser_dcf.h>
-#include <svc_drm.h>
-#include "log.h"
-
-#define DRM_SKIP_SPACE_TAB(p) while( (*(p) == ' ') || (*(p) == '\t') ) \
- p++
-
-typedef enum _DM_PARSE_STATUS {
- DM_PARSE_START,
- DM_PARSING_RIGHTS,
- DM_PARSING_CONTENT,
- DM_PARSE_END
-} DM_PARSE_STATUS;
-
-static int drm_strnicmp(const uint8_t* s1, const uint8_t* s2, int32_t n)
-{
- if (n < 0 || NULL == s1 || NULL == s2)
- return -1;
-
- if (n == 0)
- return 0;
-
- while (n-- != 0 && tolower(*s1) == tolower(*s2))
- {
- if (n == 0 || *s1 == '\0' || *s2 == '\0')
- break;
- s1++;
- s2++;
- }
-
- return tolower(*s1) - tolower(*s2);
-}
-
-const uint8_t * drm_strnstr(const uint8_t * str, const uint8_t * strSearch, int32_t len)
-{
- int32_t i, stringLen;
-
- if (NULL == str || NULL == strSearch || len <= 0)
- return NULL;
-
- stringLen = strlen((char *)strSearch);
- for (i = 0; i < len - stringLen + 1; i++) {
- if (str[i] == *strSearch && 0 == memcmp(str + i, strSearch, stringLen))
- return str + i;
- }
- return NULL;
-}
-
-/* See parser_dm.h */
-int32_t drm_parseDM(const uint8_t *buffer, int32_t bufferLen, T_DRM_DM_Info *pDmInfo)
-{
- const uint8_t *pStart = NULL, *pEnd = NULL;
- const uint8_t *pBufferEnd;
- int32_t contentLen, leftLen;
- DM_PARSE_STATUS status = DM_PARSE_START;
- int32_t boundaryLen;
-
- if (NULL == buffer || bufferLen <= 0 || NULL == pDmInfo)
- return FALSE;
-
- /* Find the end of the input buffer */
- pBufferEnd = buffer + bufferLen;
- leftLen = bufferLen;
-
- /* Find out the boundary */
- pStart = drm_strnstr(buffer, (uint8_t *)"--", bufferLen);
- if (NULL == pStart)
- return FALSE; /* No boundary error */
- pEnd = pStart;
-
- /* Record the boundary */
- pEnd = drm_strnstr(pStart, (uint8_t *)DRM_NEW_LINE_CRLF, leftLen);
- /* if can not find the CRLF, return FALSE */
- if (NULL == pEnd)
- return FALSE;
- if ((pEnd - pStart) >= MAX_CONTENT_BOUNDARY_LEN)
- return FALSE;
- strncpy((char *)pDmInfo->boundary, (char *)pStart, pEnd - pStart);
- pDmInfo->boundary[MAX_CONTENT_BOUNDARY_LEN - 1] = 0;
- boundaryLen = strlen((char *)pDmInfo->boundary) + 2; /* 2 means: '\r' and '\n' */
-
- pEnd += 2; /* skip the '\r' and '\n' */
- pStart = pEnd;
- leftLen = pBufferEnd - pStart;
- do {
- pDmInfo->transferEncoding = DRM_MESSAGE_CODING_7BIT; /* According RFC2045 chapter 6.1, the default value should be 7bit.*/
- strcpy((char *)pDmInfo->contentType, "text/plain"); /* According RFC2045 chapter 5.2, the default value should be "text/plain". */
-
- /* Deal the header information */
- while ((('\r' != *pStart) || ('\n' != *(pStart + 1))) && pStart < pBufferEnd) {
- pEnd = drm_strnstr(pStart, (uint8_t *)DRM_NEW_LINE_CRLF, leftLen);
- if (NULL == pEnd)
- return FALSE;
-
- if (0 != pDmInfo->deliveryType) { /* This means the delivery type has been confirmed */
- if (0 == strncmp((char *)pStart, HEADERS_TRANSFER_CODING, HEADERS_TRANSFER_CODING_LEN)) {
- pStart += HEADERS_TRANSFER_CODING_LEN;
- DRM_SKIP_SPACE_TAB(pStart);
-
- if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_7BIT, pEnd - pStart))
- pDmInfo->transferEncoding = DRM_MESSAGE_CODING_7BIT;
- else if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_8BIT, pEnd - pStart))
- pDmInfo->transferEncoding = DRM_MESSAGE_CODING_8BIT;
- else if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_BINARY, pEnd - pStart))
- pDmInfo->transferEncoding = DRM_MESSAGE_CODING_BINARY;
- else if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_BASE64, pEnd - pStart))
- pDmInfo->transferEncoding = DRM_MESSAGE_CODING_BASE64;
- else
- return FALSE; /* Unknown transferCoding error */
- } else if (0 == drm_strnicmp(pStart, (uint8_t *)HEADERS_CONTENT_TYPE, HEADERS_CONTENT_TYPE_LEN)) {
- pStart += HEADERS_CONTENT_TYPE_LEN;
- DRM_SKIP_SPACE_TAB(pStart);
-
- if (pEnd - pStart > 0) {
- if ((pEnd - pStart) >= MAX_CONTENT_TYPE_LEN)
- return FALSE;
- strncpy((char *)pDmInfo->contentType, (char *)pStart, pEnd - pStart);
- pDmInfo->contentType[pEnd - pStart] = '\0';
- }
- } else if (0 == drm_strnicmp(pStart, (uint8_t *)HEADERS_CONTENT_ID, HEADERS_CONTENT_ID_LEN)) {
- uint8_t tmpBuf[MAX_CONTENT_ID] = {0};
- uint8_t *pTmp;
-
- pStart += HEADERS_CONTENT_ID_LEN;
- DRM_SKIP_SPACE_TAB(pStart);
-
- /* error: more than one content id */
- if(drm_strnstr(pStart, (uint8_t*)HEADERS_CONTENT_ID, pBufferEnd - pStart)){
- ALOGD("drm_dmParser: error: more than one content id\r\n");
- return FALSE;
- }
-
- status = DM_PARSING_CONTENT; /* can go here means that the rights object has been parsed. */
-
- /* Change the format from <...> to cid:... */
- if (NULL != (pTmp = (uint8_t *)memchr((char *)pStart, '<', pEnd - pStart))) {
- if ((pEnd - pTmp - 1) >= (int) sizeof(tmpBuf))
- return FALSE;
- strncpy((char *)tmpBuf, (char *)(pTmp + 1), pEnd - pTmp - 1);
- tmpBuf[MAX_CONTENT_ID - 1] = 0;
-
- if (NULL != (pTmp = (uint8_t *)memchr((char *)tmpBuf, '>', pEnd - pTmp - 1))) {
- *pTmp = '\0';
-
- memset(pDmInfo->contentID, 0, MAX_CONTENT_ID);
- snprintf((char *)pDmInfo->contentID, MAX_CONTENT_ID, "%s%s", "cid:", (int8_t *)tmpBuf);
- }
- }
- }
- } else { /* First confirm delivery type, Forward_Lock, Combined Delivery or Separate Delivery */
- if (0 == drm_strnicmp(pStart, (uint8_t *)HEADERS_CONTENT_TYPE, HEADERS_CONTENT_TYPE_LEN)) {
- pStart += HEADERS_CONTENT_TYPE_LEN;
- DRM_SKIP_SPACE_TAB(pStart);
-
- if (pEnd - pStart > 0) {
- strncpy((char *)pDmInfo->contentType, (char *)pStart, pEnd - pStart);
- pDmInfo->contentType[pEnd - pStart] = '\0';
- }
-
- if (0 == strcmp((char *)pDmInfo->contentType, DRM_MIME_TYPE_RIGHTS_XML)) {
- pDmInfo->deliveryType = COMBINED_DELIVERY;
- status = DM_PARSING_RIGHTS;
- }
- else if (0 == strcmp((char *)pDmInfo->contentType, DRM_MIME_TYPE_CONTENT)) {
- pDmInfo->deliveryType = SEPARATE_DELIVERY_FL;
- status = DM_PARSING_CONTENT;
- }
- else if (0 == pDmInfo->deliveryType) {
- pDmInfo->deliveryType = FORWARD_LOCK;
- status = DM_PARSING_CONTENT;
- }
- }
- }
- pEnd += 2; /* skip the '\r' and '\n' */
- pStart = pEnd;
- leftLen = pBufferEnd - pStart;
- }
- pStart += 2; /* skip the second CRLF: "\r\n" */
- pEnd = pStart;
-
- /* Deal the content part, including rel or real content */
- while (leftLen > 0) {
- if (NULL == (pEnd = memchr(pEnd, '\r', leftLen))) {
- pEnd = pBufferEnd;
- break; /* no boundary found */
- }
-
- leftLen = pBufferEnd - pEnd;
- if (leftLen < boundaryLen) {
- pEnd = pBufferEnd;
- break; /* here means may be the boundary has been split */
- }
-
- if (('\n' == *(pEnd + 1)) && (0 == memcmp(pEnd + 2, pDmInfo->boundary, strlen((char *)pDmInfo->boundary))))
- break; /* find the boundary here */
-
- pEnd++;
- leftLen--;
- }
-
- if (pEnd >= pBufferEnd)
- contentLen = DRM_UNKNOWN_DATA_LEN;
- else
- contentLen = pEnd - pStart;
-
- switch(pDmInfo->deliveryType) {
- case FORWARD_LOCK:
- pDmInfo->contentLen = contentLen;
- pDmInfo->contentOffset = pStart - buffer;
- status = DM_PARSE_END;
- break;
- case COMBINED_DELIVERY:
- if (DM_PARSING_RIGHTS == status) {
- pDmInfo->rightsLen = contentLen;
- pDmInfo->rightsOffset = pStart - buffer;
- } else {
- pDmInfo->contentLen = contentLen;
- pDmInfo->contentOffset = pStart - buffer;
- status = DM_PARSE_END;
- }
- break;
- case SEPARATE_DELIVERY_FL:
- {
- T_DRM_DCF_Info dcfInfo;
- uint8_t* pEncData = NULL;
-
- memset(&dcfInfo, 0, sizeof(T_DRM_DCF_Info));
- if (DRM_UNKNOWN_DATA_LEN == contentLen)
- contentLen = pEnd - pStart;
- if (FALSE == drm_dcfParser(pStart, contentLen, &dcfInfo, &pEncData))
- return FALSE;
-
- pDmInfo->contentLen = dcfInfo.EncryptedDataLen;
- pDmInfo->contentOffset = pEncData - buffer;
- strcpy((char *)pDmInfo->contentType, (char *)dcfInfo.ContentType);
- strcpy((char *)pDmInfo->contentID, (char *)dcfInfo.ContentURI);
- strcpy((char *)pDmInfo->rightsIssuer, (char *)dcfInfo.Rights_Issuer);
- status = DM_PARSE_END;
- }
- break;
- default:
- return FALSE;
- }
-
- if (DM_PARSING_RIGHTS == status) {
- /* Here means the rights object data has been completed, boundary must exist */
- leftLen = pBufferEnd - pEnd;
- pStart = drm_strnstr(pEnd, pDmInfo->boundary, leftLen);
- if (NULL == pStart)
- return FALSE;
- leftLen = pBufferEnd - pStart;
- pEnd = drm_strnstr(pStart, (uint8_t *)DRM_NEW_LINE_CRLF, leftLen);
- if (NULL == pEnd)
- return FALSE; /* only rights object, no media object, error */
-
- pEnd += 2; /* skip the "\r\n" */
- pStart = pEnd;
- }
- } while (DM_PARSE_END != status);
-
- return TRUE;
-}
diff --git a/media/libdrm/mobile1/src/parser/parser_rel.c b/media/libdrm/mobile1/src/parser/parser_rel.c
deleted file mode 100644
index 537fa9ce..0000000
--- a/media/libdrm/mobile1/src/parser/parser_rel.c
+++ /dev/null
@@ -1,663 +0,0 @@
-/*
- * 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.
- */
-
-#include <parser_rel.h>
-#include <parser_dm.h>
-#include <xml_tinyParser.h>
-#include <wbxml_tinyparser.h>
-#include <drm_decoder.h>
-#include <svc_drm.h>
-
-/* See parser_rel.h */
-int32_t drm_monthDays(int32_t year, int32_t month)
-{
- switch (month) {
- case 1:
- case 3:
- case 5:
- case 7:
- case 8:
- case 10:
- case 12:
- return 31;
- case 4:
- case 6:
- case 9:
- case 11:
- return 30;
- case 2:
- if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
- return 29;
- else
- return 28;
- default:
- return -1;
- }
-}
-
-int32_t drm_checkDate(int32_t year, int32_t month, int32_t day,
- int32_t hour, int32_t min, int32_t sec)
-{
- if (month >= 1 && month <= 12 &&
- day >= 1 && day <= drm_monthDays(year, month) &&
- hour >= 0 && hour <= 23 &&
- min >= 0 && min <= 59 && sec >= 0 && sec <= 59)
- return 0;
- else
- return -1;
-}
-
-static int32_t drm_getStartEndTime(uint8_t * pValue, int32_t valueLen,
- T_DRM_DATETIME * dateTime)
-{
- int32_t year, mon, day, hour, min, sec;
- uint8_t pTmp[64] = {0};
-
- strncpy((char *)pTmp, (char *)pValue, valueLen);
- {
- uint8_t * pHead = pTmp;
- uint8_t * pEnd = NULL;
- uint8_t tmpByte;
-
- /** get year */
- pEnd = (uint8_t *)strstr((char *)pHead, "-");
- if(NULL == pEnd)
- return FALSE;
- tmpByte = *pEnd;
- *pEnd = '\0';
- year = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpByte;
-
- /** get month */
- pEnd = (uint8_t *)strstr((char *)pHead, "-");
- if(NULL == pEnd)
- return FALSE;
- tmpByte = *pEnd;
- *pEnd = '\0';
- mon = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpByte;
-
- /** get day */
- pEnd = (uint8_t *)strstr((char *)pHead, "T");
- if(NULL == pEnd)
- return FALSE;
- tmpByte = *pEnd;
- *pEnd = '\0';
- day = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpByte;
-
- /** get hour */
- pEnd = (uint8_t *)strstr((char *)pHead, ":");
- if(NULL == pEnd)
- return FALSE;
- tmpByte = *pEnd;
- *pEnd = '\0';
- hour = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpByte;
-
- /** get minute */
- pEnd = (uint8_t *)strstr((char *)pHead, ":");
- if(NULL == pEnd)
- return FALSE;
- tmpByte = *pEnd;
- *pEnd = '\0';
- min = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpByte;
-
- /** get second */
- sec = atoi((char *)pHead);
- }
- if (0 != drm_checkDate(year, mon, day, hour, min, sec))
- return FALSE;
-
- YMD_HMS_2_INT(year, mon, day, dateTime->date, hour, min, sec,
- dateTime->time);
- return TRUE;
-}
-
-static int32_t drm_checkWhetherHasUnknowConstraint(uint8_t* drm_constrain)
-{
- char* begin_constrain = "<o-ex:constraint>";
- char* end_constrain = "</o-ex:constraint>";
- char* constrain_begin = strstr((char*)drm_constrain,begin_constrain);
- char* constrain_end = strstr((char*)drm_constrain,end_constrain);
- uint32_t constrain_len = 0;
-
- if(NULL == constrain_begin)
- return FALSE;
-
- if(NULL == constrain_end)
- return TRUE;
-
- /* compute valid characters length */
- {
- uint32_t constrain_begin_len = strlen(begin_constrain);
- char* cur_pos = constrain_begin + constrain_begin_len;
-
- constrain_len = (constrain_end - constrain_begin) - constrain_begin_len;
-
- while(cur_pos < constrain_end){
- if(isspace(*cur_pos))
- constrain_len--;
-
- cur_pos++;
- }
- }
-
- /* check all constraints */
- {
- #define DRM_ALL_CONSTRAINT_COUNT 5
-
- int32_t i = 0;
- int32_t has_datetime = FALSE;
- int32_t has_start_or_end = FALSE;
-
- char* all_vaild_constraints[DRM_ALL_CONSTRAINT_COUNT][2] = {
- {"<o-dd:count>","</o-dd:count>"},
- {"<o-dd:interval>","</o-dd:interval>"},
- {"<o-dd:datetime>","</o-dd:datetime>"},
- {"<o-dd:start>","</o-dd:start>"},
- {"<o-dd:end>","</o-dd:end>"}
- };
-
- for(i = 0; i < DRM_ALL_CONSTRAINT_COUNT; i++){
- char*start = strstr((char*)drm_constrain,all_vaild_constraints[i][0]);
-
- if(start && (start < constrain_end)){
- char* end = strstr((char*)drm_constrain,all_vaild_constraints[i][1]);
-
- if(end && (end < constrain_end)){
- if(0 == strncmp(all_vaild_constraints[i][0],"<o-dd:datetime>",strlen("<o-dd:datetime>"))){
- constrain_len -= strlen(all_vaild_constraints[i][0]);
- constrain_len -= strlen(all_vaild_constraints[i][1]);
-
- if(0 == constrain_len)
- return TRUE;
-
- has_datetime = TRUE;
- continue;
- }
-
- if((0 == strncmp(all_vaild_constraints[i][0],"<o-dd:start>",strlen("<o-dd:start>")))
- || (0 == strncmp(all_vaild_constraints[i][0],"<o-dd:end>",strlen("<o-dd:end>")))){
- if(FALSE == has_datetime)
- return TRUE;
- else
- has_start_or_end = TRUE;
- }
-
- constrain_len -= (end - start);
- constrain_len -= strlen(all_vaild_constraints[i][1]);
-
- if(0 == constrain_len)
- if(has_datetime != has_start_or_end)
- return TRUE;
- else
- return FALSE;
- }
- else
- return TRUE;
- }
- }
-
- if(has_datetime != has_start_or_end)
- return TRUE;
-
- if(constrain_len)
- return TRUE;
- else
- return FALSE;
- }
-}
-
-static int32_t drm_getRightValue(uint8_t * buffer, int32_t bufferLen,
- T_DRM_Rights * ro, uint8_t * operation,
- uint8_t oper_char)
-{
- uint8_t *pBuf, *pValue;
- uint8_t sProperty[256];
- int32_t valueLen;
- int32_t year, mon, day, hour, min, sec;
- T_DRM_Rights_Constraint *pConstraint;
- int32_t *bIsAble;
- uint8_t *ret = NULL;
- int32_t flag = 0;
-
- if (operation == NULL) {
- switch (oper_char) {
- case REL_TAG_PLAY:
- pConstraint = &(ro->PlayConstraint);
- bIsAble = &(ro->bIsPlayable);
- break;
- case REL_TAG_DISPLAY:
- pConstraint = &(ro->DisplayConstraint);
- bIsAble = &(ro->bIsDisplayable);
- break;
- case REL_TAG_EXECUTE:
- pConstraint = &(ro->ExecuteConstraint);
- bIsAble = &(ro->bIsExecuteable);
- break;
- case REL_TAG_PRINT:
- pConstraint = &(ro->PrintConstraint);
- bIsAble = &(ro->bIsPrintable);
- break;
- default:
- return FALSE; /* The input parm is err */
- }
- } else {
- if (strcmp((char *)operation, "play") == 0) {
- pConstraint = &(ro->PlayConstraint);
- bIsAble = &(ro->bIsPlayable);
- } else if (strcmp((char *)operation, "display") == 0) {
- pConstraint = &(ro->DisplayConstraint);
- bIsAble = &(ro->bIsDisplayable);
- } else if (strcmp((char *)operation, "execute") == 0) {
- pConstraint = &(ro->ExecuteConstraint);
- bIsAble = &(ro->bIsExecuteable);
- } else if (strcmp((char *)operation, "print") == 0) {
- pConstraint = &(ro->PrintConstraint);
- bIsAble = &(ro->bIsPrintable);
- } else
- return FALSE; /* The input parm is err */
- }
-
- if (operation == NULL) {
- sprintf((char *)sProperty, "%c%c%c%c", REL_TAG_RIGHTS,
- REL_TAG_AGREEMENT, REL_TAG_PERMISSION, oper_char);
- ret = WBXML_DOM_getNode(buffer, bufferLen, sProperty);
- } else {
- sprintf((char *)sProperty,
- "o-ex:rights\\o-ex:agreement\\o-ex:permission\\o-dd:%s",
- operation);
- ret = XML_DOM_getNode(buffer, sProperty);
- }
- CHECK_VALIDITY(ret);
- if (NULL == ret)
- return TRUE;
- WRITE_RO_FLAG(*bIsAble, 1, pConstraint->Indicator, DRM_NO_CONSTRAINT); /* If exit first assume have utter rights */
- flag = 1;
-
- if (operation == NULL) { /* If father element node is not exit then return */
- sprintf((char *)sProperty, "%c%c%c%c%c", REL_TAG_RIGHTS,
- REL_TAG_AGREEMENT, REL_TAG_PERMISSION, oper_char,
- REL_TAG_CONSTRAINT);
- ret = WBXML_DOM_getNode(buffer, bufferLen, sProperty);
- } else {
- sprintf((char *)sProperty,
- "o-ex:rights\\o-ex:agreement\\o-ex:permission\\o-dd:%s\\o-ex:constraint",
- operation);
- ret = XML_DOM_getNode(buffer, sProperty);
- }
-
- CHECK_VALIDITY(ret);
- if (ret == NULL)
- return TRUE;
-
- if(TRUE == drm_checkWhetherHasUnknowConstraint(ret))
- return FALSE;
-
- *bIsAble = 0;
- pConstraint->Indicator = DRM_NO_PERMISSION; /* If exit constraint assume have no rights */
- flag = 2;
-
- if (operation == NULL) {
- sprintf((char *)sProperty, "%c%c%c%c%c%c", REL_TAG_RIGHTS,
- REL_TAG_AGREEMENT, REL_TAG_PERMISSION, oper_char,
- REL_TAG_CONSTRAINT, REL_TAG_INTERVAL);
- pBuf =
- WBXML_DOM_getNodeValue(buffer, bufferLen, sProperty, (uint8_t **)&pValue,
- &valueLen);
- } else {
- sprintf((char *)sProperty,
- "o-ex:rights\\o-ex:agreement\\o-ex:permission\\o-dd:%s\\o-ex:constraint\\o-dd:interval",
- operation);
- pBuf = XML_DOM_getNodeValue(buffer, sProperty, &pValue, &valueLen);
- }
- CHECK_VALIDITY(pBuf);
- if (pBuf) { /* If interval element exit then get the value */
- uint8_t pTmp[64] = {0};
-
- strncpy((char *)pTmp, (char *)pValue, valueLen);
- {
- uint8_t * pHead = pTmp + 1;
- uint8_t * pEnd = NULL;
- uint8_t tmpChar;
-
- /** get year */
- pEnd = (uint8_t *)strstr((char *)pHead, "Y");
- if(NULL == pEnd)
- return FALSE;
- tmpChar = *pEnd;
- *pEnd = '\0';
- year = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpChar;
-
- /** get month */
- pEnd = (uint8_t *)strstr((char *)pHead, "M");
- if(NULL == pEnd)
- return FALSE;
- tmpChar = *pEnd;
- *pEnd = '\0';
- mon = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpChar;
-
- /** get day */
- pEnd = (uint8_t *)strstr((char *)pHead, "D");
- if(NULL == pEnd)
- return FALSE;
- tmpChar = *pEnd;
- *pEnd = '\0';
- day = atoi((char *)pHead);
- pHead = pEnd + 2;
- *pEnd = tmpChar;
-
- /** get hour */
- pEnd = (uint8_t *)strstr((char *)pHead, "H");
- if(NULL == pEnd)
- return FALSE;
- tmpChar = *pEnd;
- *pEnd = '\0';
- hour = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpChar;
-
- /** get minute */
- pEnd = (uint8_t *)strstr((char *)pHead, "M");
- if(NULL == pEnd)
- return FALSE;
- tmpChar = *pEnd;
- *pEnd = '\0';
- min = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpChar;
-
- /** get second */
- pEnd = (uint8_t *)strstr((char *)pHead, "S");
- if(NULL == pEnd)
- return FALSE;
- tmpChar = *pEnd;
- *pEnd = '\0';
- sec = atoi((char *)pHead);
- pHead = pEnd + 1;
- *pEnd = tmpChar;
- }
-
- if (year < 0 || mon < 0 || day < 0 || hour < 0
- || min < 0 || sec < 0)
- return FALSE;
- YMD_HMS_2_INT(year, mon, day, pConstraint->Interval.date, hour,
- min, sec, pConstraint->Interval.time);
- WRITE_RO_FLAG(*bIsAble, 1, pConstraint->Indicator,
- DRM_INTERVAL_CONSTRAINT);
- flag = 3;
- }
-
- if (operation == NULL) {
- sprintf((char *)sProperty, "%c%c%c%c%c%c", REL_TAG_RIGHTS,
- REL_TAG_AGREEMENT, REL_TAG_PERMISSION, oper_char,
- REL_TAG_CONSTRAINT, REL_TAG_COUNT);
- pBuf =
- WBXML_DOM_getNodeValue(buffer, bufferLen, sProperty, (uint8_t **)&pValue,
- &valueLen);
- } else {
- sprintf((char *)sProperty,
- "o-ex:rights\\o-ex:agreement\\o-ex:permission\\o-dd:%s\\o-ex:constraint\\o-dd:count",
- operation);
- pBuf = XML_DOM_getNodeValue(buffer, sProperty, &pValue, &valueLen);
- }
- CHECK_VALIDITY(pBuf);
- if (pBuf) { /* If count element exit the get the value */
- uint8_t pTmp[16] = {0};
- int32_t i;
-
- for (i = 0; i < valueLen; i++) { /* Check the count format */
- if (0 == isdigit(*(pValue + i)))
- return FALSE;
- }
-
- strncpy((char *)pTmp, (char *)pValue, valueLen);
- pConstraint->Count = atoi((char *)pTmp);
-
- if(0 == pConstraint->Count)
- {
- WRITE_RO_FLAG(*bIsAble, 0, pConstraint->Indicator, DRM_NO_PERMISSION);
- }
- else if( pConstraint->Count > 0)
- {
- WRITE_RO_FLAG(*bIsAble, 1, pConstraint->Indicator, DRM_COUNT_CONSTRAINT);
- }
- else /* < 0 */
- {
- return FALSE;
- }
-
- flag = 3;
- }
-
- if (operation == NULL) {
- sprintf((char *)sProperty, "%c%c%c%c%c%c%c", REL_TAG_RIGHTS,
- REL_TAG_AGREEMENT, REL_TAG_PERMISSION, oper_char,
- REL_TAG_CONSTRAINT, REL_TAG_DATETIME, REL_TAG_START);
- pBuf =
- WBXML_DOM_getNodeValue(buffer, bufferLen, sProperty, (uint8_t **)&pValue,
- &valueLen);
- } else {
- sprintf((char *)sProperty,
- "o-ex:rights\\o-ex:agreement\\o-ex:permission\\o-dd:%s\\o-ex:constraint\\o-dd:datetime\\o-dd:start",
- operation);
- pBuf = XML_DOM_getNodeValue(buffer, sProperty, &pValue, &valueLen);
- }
- CHECK_VALIDITY(pBuf);
- if (pBuf) { /* If start element exit then get the value */
- if (FALSE ==
- drm_getStartEndTime(pValue, valueLen, &pConstraint->StartTime))
- return FALSE;
- WRITE_RO_FLAG(*bIsAble, 1, pConstraint->Indicator, DRM_START_TIME_CONSTRAINT);
- flag = 3;
- }
-
- if (operation == NULL) {
- sprintf((char *)sProperty, "%c%c%c%c%c%c%c", REL_TAG_RIGHTS,
- REL_TAG_AGREEMENT, REL_TAG_PERMISSION, oper_char,
- REL_TAG_CONSTRAINT, REL_TAG_DATETIME, REL_TAG_END);
- pBuf =
- WBXML_DOM_getNodeValue(buffer, bufferLen, sProperty, (uint8_t **)&pValue,
- &valueLen);
- } else {
- sprintf((char *)sProperty,
- "o-ex:rights\\o-ex:agreement\\o-ex:permission\\o-dd:%s\\o-ex:constraint\\o-dd:datetime\\o-dd:end",
- operation);
- pBuf = XML_DOM_getNodeValue(buffer, sProperty, &pValue, &valueLen);
- }
- CHECK_VALIDITY(pBuf);
- if (pBuf) {
- if (FALSE ==
- drm_getStartEndTime(pValue, valueLen, &pConstraint->EndTime))
- return FALSE;
- WRITE_RO_FLAG(*bIsAble, 1, pConstraint->Indicator, DRM_END_TIME_CONSTRAINT);
- flag = 3;
- }
-
- if (2 == flag)
- WRITE_RO_FLAG(*bIsAble, 1, pConstraint->Indicator, DRM_NO_CONSTRAINT); /* If exit first assume have utter rights */
- return TRUE;
-}
-
-/* See parser_rel.h */
-int32_t drm_relParser(uint8_t* buffer, int32_t bufferLen, int32_t Format, T_DRM_Rights* pRights)
-{
- uint8_t *pBuf, *pValue;
- uint8_t sProperty[256];
- int32_t valueLen;
-
- if (TYPE_DRM_RIGHTS_WBXML != Format && TYPE_DRM_RIGHTS_XML != Format) /* It is not the support parse format */
- return FALSE;
-
- if (TYPE_DRM_RIGHTS_XML == Format) {
- /* Check whether it is a CD, and parse it using TYPE_DRM_RIGHTS_XML */
- if (NULL != drm_strnstr(buffer, (uint8_t *)HEADERS_CONTENT_ID, bufferLen))
- return FALSE;
-
- pBuf =
- XML_DOM_getNodeValue(buffer,
- (uint8_t *)"o-ex:rights\\o-ex:context\\o-dd:version",
- &pValue, &valueLen);
- CHECK_VALIDITY(pBuf);
-
- if (pBuf) {
- if (valueLen > 8) /* Check version lenth */
- return FALSE;
-
- /* error version */
- if(strncmp(pValue,"1.0",valueLen))
- return FALSE;
-
- strncpy((char *)pRights->Version, (char *)pValue, valueLen);
- } else
- return FALSE;
-
- /* this means there is more than one version label in rights */
- if(strstr((char*)pBuf, "<o-dd:version>"))
- return FALSE;
-
- pBuf =
- XML_DOM_getNodeValue(buffer,
- (uint8_t *)"o-ex:rights\\o-ex:agreement\\o-ex:asset\\ds:KeyInfo\\ds:KeyValue",
- &pValue, &valueLen);
- CHECK_VALIDITY(pBuf);
- if (pBuf) { /* Get keyvalue */
- int32_t keyLen;
-
- if (24 != valueLen)
- return FALSE;
-
- keyLen = drm_decodeBase64(NULL, 0, pValue, &valueLen);
- if (keyLen < 0)
- return FALSE;
-
- if (DRM_KEY_LEN != drm_decodeBase64(pRights->KeyValue, keyLen, pValue, &valueLen))
- return FALSE;
- }
-
- pBuf =
- XML_DOM_getNodeValue(buffer,
- (uint8_t *)"o-ex:rights\\o-ex:agreement\\o-ex:asset\\o-ex:context\\o-dd:uid",
- &pValue, &valueLen);
- CHECK_VALIDITY(pBuf);
- if (pBuf) {
- if (valueLen > DRM_UID_LEN)
- return FALSE;
- strncpy((char *)pRights->uid, (char *)pValue, valueLen);
- pRights->uid[valueLen] = '\0';
- } else
- return FALSE;
-
- /* this means there is more than one uid label in rights */
- if(strstr((char*)pBuf, "<o-dd:uid>"))
- return FALSE;
-
- if (FALSE ==
- drm_getRightValue(buffer, bufferLen, pRights, (uint8_t *)"play", 0))
- return FALSE;
-
- if (FALSE ==
- drm_getRightValue(buffer, bufferLen, pRights, (uint8_t *)"display", 0))
- return FALSE;
-
- if (FALSE ==
- drm_getRightValue(buffer, bufferLen, pRights, (uint8_t *)"execute", 0))
- return FALSE;
-
- if (FALSE ==
- drm_getRightValue(buffer, bufferLen, pRights, (uint8_t *)"print", 0))
- return FALSE;
- } else if (TYPE_DRM_RIGHTS_WBXML == Format) {
- if (!REL_CHECK_WBXML_HEADER(buffer))
- return FALSE;
-
- sprintf((char *)sProperty, "%c%c%c", REL_TAG_RIGHTS, REL_TAG_CONTEXT,
- REL_TAG_VERSION);
- pBuf =
- WBXML_DOM_getNodeValue(buffer, bufferLen, sProperty, (uint8_t **)&pValue,
- &valueLen);
- CHECK_VALIDITY(pBuf);
-
- if (pBuf) {
- if (valueLen > 8) /* Check version lenth */
- return FALSE;
- strncpy((char *)pRights->Version, (char *)pValue, valueLen);
- } else
- return FALSE;
-
- sprintf((char *)sProperty, "%c%c%c%c%c",
- REL_TAG_RIGHTS, REL_TAG_AGREEMENT, REL_TAG_ASSET,
- REL_TAG_KEYINFO, REL_TAG_KEYVALUE);
- pBuf =
- WBXML_DOM_getNodeValue(buffer, bufferLen, sProperty, (uint8_t **)&pValue,
- &valueLen);
- CHECK_VALIDITY(pBuf);
- if (pBuf) {
- if (DRM_KEY_LEN != valueLen)
- return FALSE;
- memcpy(pRights->KeyValue, pValue, DRM_KEY_LEN);
- memset(pValue, 0, DRM_KEY_LEN); /* Clean the KeyValue */
- }
-
- sprintf((char *)sProperty, "%c%c%c%c%c",
- REL_TAG_RIGHTS, REL_TAG_AGREEMENT, REL_TAG_ASSET,
- REL_TAG_CONTEXT, REL_TAG_UID);
- pBuf =
- WBXML_DOM_getNodeValue(buffer, bufferLen, sProperty, (uint8_t **)&pValue,
- &valueLen);
- CHECK_VALIDITY(pBuf);
- if (pBuf) {
- if (valueLen > DRM_UID_LEN)
- return FALSE;
- strncpy((char *)pRights->uid, (char *)pValue, valueLen);
- pRights->uid[valueLen] = '\0';
- } else
- return FALSE;
-
- if (FALSE ==
- drm_getRightValue(buffer, bufferLen, pRights, NULL,
- REL_TAG_PLAY))
- return FALSE;
-
- if (FALSE ==
- drm_getRightValue(buffer, bufferLen, pRights, NULL,
- REL_TAG_DISPLAY))
- return FALSE;
-
- if (FALSE ==
- drm_getRightValue(buffer, bufferLen, pRights, NULL,
- REL_TAG_EXECUTE))
- return FALSE;
-
- if (FALSE ==
- drm_getRightValue(buffer, bufferLen, pRights, NULL,
- REL_TAG_PRINT))
- return FALSE;
- }
-
- return TRUE;
-}
diff --git a/media/libdrm/mobile1/src/xml/xml_tinyparser.c b/media/libdrm/mobile1/src/xml/xml_tinyparser.c
deleted file mode 100644
index 7580312..0000000
--- a/media/libdrm/mobile1/src/xml/xml_tinyparser.c
+++ /dev/null
@@ -1,834 +0,0 @@
-/*
- * 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.
- */
-
-#include <xml/xml_tinyParser.h>
-
-int32_t xml_errno;
-
-#ifdef XML_DOM_PARSER
-
-#define XML_IS_WHITESPACE(x) ((x) == '\t' || (x) == '\n' || (x) == ' ' || (x) == '\r')
-#define XML_IS_NAMECHAR(ch) (isalpha(ch) || isdigit(ch) || ch ==':' || \
- ch == '_' || ch == '-' || ch =='.')
-
-static uint8_t *xml_ignore_blank(uint8_t *buffer)
-{
- if (NULL == buffer)
- return NULL;
-
- while (XML_IS_WHITESPACE(*buffer))
- buffer++;
-
- return buffer;
-}
-
-static uint8_t *xml_goto_tagend(uint8_t *buffer)
-{
- int32_t nameLen, valueLen;
- uint8_t *name, *value;
-
- if (NULL == buffer)
- return NULL;
-
- /* Ignore the start-tag */
- if (*buffer == '<') {
- buffer++;
- while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
- buffer++;
- if (NULL == buffer)
- return NULL;
- }
-
- do {
- if (NULL == (buffer = xml_ignore_blank(buffer)))
- return NULL;
-
- if (*buffer == '>' || (*buffer == '/' && *(buffer + 1) == '>'))
- return buffer;
-
- if (NULL ==
- XML_DOM_getAttr(buffer, &name, &nameLen, &value, &valueLen))
- return NULL;
-
- buffer = value + valueLen + 1;
- } while (*buffer != '\0');
-
- return NULL;
-}
-
-static uint8_t *xml_match_tag(uint8_t *buffer)
-{
- int32_t tagLen, tagType, bal;
-
- if (NULL == buffer)
- return NULL;
-
- bal = 0;
- do {
- if (NULL == (buffer = XML_DOM_getTag(buffer, &tagLen, &tagType)))
- return NULL;
-
- switch (tagType) {
- case XML_TAG_SELF:
- case XML_TAG_START:
- if (NULL == (buffer = xml_goto_tagend(buffer + tagLen + 1)))
- return NULL;
- if (strncmp((char *)buffer, "/>", 2) == 0) {
- buffer += 2;
- } else {
- bal++;
- }
- break;
-
- case XML_TAG_END:
- if (bal <= 0)
- return NULL;
- buffer = buffer + tagLen + 2;
- bal--;
- break;
- }
- } while (bal != 0);
-
- return buffer;
-}
-
-uint8_t *XML_DOM_getAttr(uint8_t *buffer, uint8_t **pName, int32_t *nameLen,
- uint8_t **pValue, int32_t *valueLen)
-{
- uint8_t charQuoted;
-
- if (NULL == buffer) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- /* Ignore the tag */
- if (*buffer == '<') {
- buffer++;
- /* Ignore the STag */
- while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
- buffer++;
- if (NULL == buffer)
- return NULL;
- }
-
- if (NULL == (buffer = xml_ignore_blank(buffer))) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- /* Name */
- *pName = buffer;
- while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
- buffer++;
- if (NULL == buffer) {
- XML_ERROR(XML_ERROR_ATTR_NAME);
- return NULL;
- }
- *nameLen = buffer - *pName;
- if (*nameLen <= 0) {
- XML_ERROR(XML_ERROR_ATTR_NAME);
- return NULL;
- }
-
- /* '=' */
- buffer = xml_ignore_blank(buffer);
- if (NULL == buffer || *buffer != '=') {
- XML_ERROR(XML_ERROR_ATTR_MISSED_EQUAL);
- return NULL;
- }
-
- /* Value */
- buffer++;
- buffer = xml_ignore_blank(buffer);
- if (NULL == buffer || (*buffer != '"' && *buffer != '\'')) {
- XML_ERROR(XML_ERROR_ATTR_VALUE);
- return NULL;
- }
- charQuoted = *buffer++;
- *pValue = buffer;
- while (*buffer != '\0' && *buffer != charQuoted)
- buffer++;
- if (*buffer != charQuoted) {
- XML_ERROR(XML_ERROR_ATTR_VALUE);
- return NULL;
- }
- *valueLen = buffer - *pValue;
-
- XML_ERROR(XML_ERROR_OK);
-
- return buffer + 1;
-}
-
-uint8_t *XML_DOM_getValue(uint8_t *buffer, uint8_t **pValue, int32_t *valueLen)
-{
- uint8_t *pEnd;
-
- if (NULL == buffer) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- /* Ignore the STag */
- if (*buffer == '<') {
- buffer++;
- /* If it's an end_tag, no value should be returned */
- if (*buffer == '/') {
- *valueLen = 0;
- XML_ERROR(XML_ERROR_NOVALUE);
- return NULL;
- }
-
- while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
- buffer++;
- if (NULL == buffer) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- if (NULL == (buffer = xml_goto_tagend(buffer))) {
- XML_ERROR(XML_ERROR_PROPERTY_END);
- return NULL;
- }
- }
-
- /* <test/> node found */
- if (*buffer == '/') {
- if (*(buffer + 1) != '>') {
- XML_ERROR(XML_ERROR_PROPERTY_END);
- return NULL;
- }
- XML_ERROR(XML_ERROR_OK);
- *valueLen = 0;
- return buffer;
- }
-
- if (*buffer == '>')
- buffer++;
-
- if (NULL == (buffer = xml_ignore_blank(buffer))) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- /* the following is a tag instead of the value */
- if (*buffer == '<') { /* nono value, such as <test></test> */
- buffer++;
- if (*buffer != '/') {
- XML_ERROR(XML_ERROR_ENDTAG);
- return NULL;
- }
- *valueLen = 0;
- XML_ERROR(XML_ERROR_OK);
- return NULL;
- }
-
- *pValue = buffer;
- pEnd = NULL;
- while (*buffer != '\0' && *buffer != '<') {
- if (!XML_IS_WHITESPACE(*buffer))
- pEnd = buffer;
- buffer++;
- }
- if (*buffer != '<' || pEnd == NULL) {
- XML_ERROR(XML_ERROR_VALUE);
- return NULL;
- }
-
- *valueLen = pEnd - *pValue + 1;
-
- buffer++;
- if (*buffer != '/') {
- XML_ERROR(XML_ERROR_ENDTAG);
- return NULL;
- }
-
- XML_ERROR(XML_ERROR_OK);
-
- return buffer - 1;
-}
-
-uint8_t *XML_DOM_getTag(uint8_t *buffer, int32_t *tagLen, int32_t *tagType)
-{
- uint8_t *pStart;
-
- /* WARNING: <!-- --> comment is not supported in this verison */
- if (NULL == buffer) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- do {
- while (*buffer != '<') {
- if (*buffer == '\0') {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- if (*buffer == '\"' || *buffer == '\'') {
- uint8_t charQuoted = *buffer;
- buffer++;
- while (*buffer != '\0' && *buffer != charQuoted)
- buffer++;
- if (*buffer == '\0') {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
- }
- buffer++;
- }
- buffer++;
- } while (*buffer == '!' || *buffer == '?');
-
- pStart = buffer - 1;
-
- if (*buffer == '/') {
- buffer++;
- *tagType = XML_TAG_END;
- } else {
- /* check here if it is self-end-tag */
- uint8_t *pCheck = xml_goto_tagend(pStart);
- if (pCheck == NULL) {
- XML_ERROR(XML_ERROR_PROPERTY_END);
- return NULL;
- }
-
- if (*pCheck == '>')
- *tagType = XML_TAG_START;
- else if (strncmp((char *)pCheck, "/>", 2) == 0)
- *tagType = XML_TAG_SELF;
- else {
- XML_ERROR(XML_ERROR_PROPERTY_END);
- return NULL;
- }
- }
-
- while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
- buffer++;
- if (NULL == buffer) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- if (*tagType == XML_TAG_END)
- *tagLen = buffer - pStart - 2;
- else
- *tagLen = buffer - pStart - 1;
-
- XML_ERROR(XML_ERROR_OK);
-
- return pStart;
-}
-
-uint8_t *XML_DOM_getNode(uint8_t *buffer, const uint8_t *const node)
-{
- uint8_t *pStart;
- uint8_t buf[XML_MAX_PROPERTY_LEN + 2];
- uint8_t *nodeStr = buf;
- uint8_t *retPtr = NULL;
- int32_t tagLen, tagType;
- uint8_t *lastNode = (uint8_t *)"";
-
- if (NULL == buffer) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- strncpy((char *)nodeStr, (char *)node, XML_MAX_PROPERTY_LEN);
- strcat((char *)nodeStr, "\\");
- pStart = (uint8_t *)strchr((char *)nodeStr, '\\');
-
- while (pStart != NULL) {
- *pStart = '\0';
-
- /* get the first start_tag from buffer */
- if (NULL == (buffer = XML_DOM_getTag(buffer, &tagLen, &tagType))) {
- XML_ERROR(XML_ERROR_NO_SUCH_NODE);
- return NULL;
- }
-
- if (tagType == XML_TAG_END) {
- if (0 ==
- strncmp((char *)lastNode, (char *)(buffer + 2), strlen((char *)lastNode)))
- XML_ERROR(XML_ERROR_NO_SUCH_NODE);
- else
- XML_ERROR(XML_ERROR_NO_START_TAG);
- return NULL;
- }
-
- /* wrong node, contiue to fetch the next node */
- if ((int32_t) strlen((char *)nodeStr) != tagLen
- || strncmp((char *)nodeStr, (char *)(buffer + 1), tagLen) != 0) {
- /* we should ignore all the middle code */
- buffer = xml_match_tag(buffer);
- continue;
- }
-
- retPtr = buffer; /* retPtr starts with '<xxx>' */
- buffer += (tagLen + 1);
-
- if (tagType == XML_TAG_SELF) {
- nodeStr = pStart + 1;
- break;
- }
-
- lastNode = nodeStr;
- nodeStr = pStart + 1;
- pStart = (uint8_t *)strchr((char *)nodeStr, '\\');
- }
-
- /* Check 5: nodeStr should be empty here */
- if (*nodeStr != '\0') {
- XML_ERROR(XML_ERROR_NO_SUCH_NODE);
- return NULL;
- }
-
- XML_ERROR(XML_ERROR_OK);
-
- return retPtr;
-}
-
-uint8_t *XML_DOM_getNodeValue(uint8_t *buffer, uint8_t *node,
- uint8_t **value, int32_t *valueLen)
-{
- uint8_t *pStart;
- uint8_t *lastTag;
-
- if (NULL == node || NULL == buffer) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- lastTag = node + strlen((char *)node) - 1;
- while (lastTag >= node && *lastTag != '\\')
- lastTag--;
- lastTag++;
-
- if (NULL == (pStart = XML_DOM_getNode(buffer, node)))
- return NULL;
-
- pStart += (strlen((char *)lastTag) + 1);
-
- if (NULL == (pStart = xml_goto_tagend(pStart))) {
- XML_ERROR(XML_ERROR_PROPERTY_END);
- return NULL;
- }
-
- if (NULL == (pStart = XML_DOM_getValue(pStart, value, valueLen)))
- return NULL;
-
- /* Check the end tag */
-#ifdef XML_DOM_CHECK_ENDTAG
- if (strncmp((char *)pStart, "/>", 2) == 0) {
-
- } else if (strncmp((char *)lastTag, (char *)(pStart + 2), strlen((char *)lastTag)) !=
- 0) {
- XML_ERROR(XML_ERROR_ENDTAG);
- return NULL;
- }
-#endif
-
- XML_ERROR(XML_ERROR_OK);
-
- return *value;
-}
-
-uint8_t *XML_DOM_getNextNode(uint8_t *buffer, uint8_t **pNodeName, int32_t *nodenameLen)
-{
- int32_t tagType;
-
- if (NULL == buffer)
- return NULL;
-
- do {
- if (NULL ==
- (buffer = XML_DOM_getTag(buffer + 1, nodenameLen, &tagType))) {
- XML_ERROR(XML_ERROR_NO_SUCH_NODE);
- return NULL;
- }
- } while (tagType == XML_TAG_END);
-
- *pNodeName = buffer + 1;
-
- XML_ERROR(XML_ERROR_OK);
-
- return buffer;
-}
-
-#endif /* XML_DOM_PARSER */
-
-#ifdef WBXML_DOM_PARSER
-
-#ifdef WBXML_OLD_VERSION
-uint8_t *WBXML_DOM_getNode(uint8_t *buffer, int32_t bufferLen,
- uint8_t *node)
-{
- int32_t i = 0, j = 0;
-
- if (NULL == buffer || node == NULL) {
- XML_ERROR(XML_ERROR_BUFFER_NULL);
- return NULL;
- }
-
- while (i < bufferLen) {
- if (WBXML_GET_TAG(buffer[i]) == WBXML_GET_TAG(node[j])) {
- j++;
- if (node[j] == '\0')
- break;
-
- /* Check if there is the content(it should have content) */
- if (!WBXML_HAS_CONTENT(buffer[i])) {
- /*XML_ERROR(WBXML_ERROR_MISSED_CONTENT); */
- XML_ERROR(XML_ERROR_NO_SUCH_NODE);
- return NULL;
- }
-
- /* Ignore the attrib filed */
- if (WBXML_HAS_ATTR(buffer[i])) {
- while (i < bufferLen && buffer[i] != WBXML_ATTR_END)
- i++;
- if (i >= bufferLen)
- break;
- }
- }
- i++;
-
- /* Ignore the content filed */
- if (buffer[i] == WBXML_STR_I) {
- while (i < bufferLen && buffer[i] != WBXML_END)
- i++;
- if (i >= bufferLen)
- break;
- i++;
- }
- }
-
- if (i >= bufferLen) {
- XML_ERROR(XML_ERROR_NO_SUCH_NODE);
- return NULL;
- }
-
- XML_ERROR(XML_ERROR_OK);
-
- return buffer + i + 1;
-}
-
-uint8_t *WBXML_DOM_getNodeValue(uint8_t *buffer, int32_t bufferLen,
- uint8_t *node,
- uint8_t **value, int32_t *valueLen)
-{
- int32_t i;
- uint8_t *pEnd;
-
- *value = NULL;
- *valueLen = 0;
-
- pEnd = buffer + bufferLen;
- buffer = WBXML_DOM_getNode(buffer, bufferLen, node);
- if (NULL == buffer) {
- XML_ERROR(XML_ERROR_NO_SUCH_NODE);
- return NULL;
- }
-
- if (*buffer == WBXML_OPAUE) {
- buffer++;
- *valueLen = WBXML_GetUintVar(buffer, &i);
- if (*valueLen < 0) {
- XML_ERROR(WBXML_ERROR_MBUINT32);
- return NULL;
- }
- buffer += i;
- *value = buffer;
- return *value;
- }
-
- if (*buffer != WBXML_STR_I) {
- XML_ERROR(WBXML_ERROR_MISSED_STARTTAG);
- return NULL;
- }
-
- buffer++;
-
- i = 0;
- while ((buffer + i) < pEnd && buffer[i] != WBXML_END)
- i++;
-
- if (buffer[i] != WBXML_END) {
- XML_ERROR(WBXML_ERROR_MISSED_ENDTAG);
- return NULL;
- }
-
- *value = buffer;
- *valueLen = i;
- XML_ERROR(XML_ERROR_OK);
-
- return *value;
-}
-#endif /* WBXML_OLD_VERSION */
-
-#define MAX_UINT_VAR_BYTE 4
-#define UINTVAR_INVALID -1
-int32_t WBXML_GetUintVar(const uint8_t *const buffer, int32_t *len)
-{
- int32_t i, byteLen;
- int32_t sum;
-
- byteLen = 0;
- while ((buffer[byteLen] & 0x80) > 0 && byteLen < MAX_UINT_VAR_BYTE)
- byteLen++;
-
- if (byteLen > MAX_UINT_VAR_BYTE)
- return UINTVAR_INVALID;
-
- *len = byteLen + 1;
- sum = buffer[byteLen];
- for (i = byteLen - 1; i >= 0; i--)
- sum += ((buffer[i] & 0x7F) << 7 * (byteLen - i));
-
- return sum;
-}
-
-XML_BOOL WBXML_DOM_Init(WBXML * pWbxml, uint8_t *buffer,
- int32_t bufferLen)
-{
- int32_t num, len;
-
- pWbxml->End = buffer + bufferLen;
- pWbxml->version = *buffer++;
- if (UINTVAR_INVALID == (num = WBXML_GetUintVar(buffer, &len)))
- return XML_FALSE;
- buffer += len;
- pWbxml->publicid = num;
- if (UINTVAR_INVALID == (num = WBXML_GetUintVar(buffer, &len)))
- return XML_FALSE;
- buffer += len;
- pWbxml->charset = num;
- if (UINTVAR_INVALID == (num = WBXML_GetUintVar(buffer, &len)))
- return XML_FALSE;
- buffer += len;
- pWbxml->strTable = buffer;
- pWbxml->strTableLen = num;
- buffer += num;
- pWbxml->curPtr = pWbxml->Content = buffer;
- pWbxml->depth = 0;
-
- return XML_TRUE;
-}
-
-void WBXML_DOM_Rewind(WBXML * pWbxml)
-{
- pWbxml->curPtr = pWbxml->Content;
-}
-
-XML_BOOL WBXML_DOM_Eof(WBXML * pWbxml)
-{
- if (pWbxml->curPtr > pWbxml->End)
- return XML_TRUE;
-
- return XML_FALSE;
-}
-
-uint8_t WBXML_DOM_GetTag(WBXML * pWbxml)
-{
- uint8_t tagChar;
-
- if (pWbxml->curPtr > pWbxml->End)
- return XML_EOF;
-
- tagChar = *pWbxml->curPtr;
- pWbxml->curPtr++;
-
- if (WBXML_GET_TAG(tagChar) == WBXML_CONTENT_END)
- pWbxml->depth--;
- else
- pWbxml->depth++;
-
- return tagChar;
-}
-
-uint8_t WBXML_DOM_GetChar(WBXML * pWbxml)
-{
- return *pWbxml->curPtr++;
-}
-
-void WBXML_DOM_Seek(WBXML * pWbxml, int32_t offset)
-{
- pWbxml->curPtr += offset;
-}
-
-uint8_t WBXML_DOM_GetUIntVar(WBXML * pWbxml)
-{
- int32_t num, len;
-
- num = WBXML_GetUintVar(pWbxml->curPtr, &len);
- pWbxml->curPtr += len;
-
- return (uint8_t)num;
-}
-
-#ifdef XML_TREE_STRUCTURE
-
-#ifdef DEBUG_MODE
-static int32_t malloc_times = 0;
-static int32_t free_times = 0;
-void XML_PrintMallocInfo()
-{
- printf("====XML_PrintMallocInfo====\n");
- printf(" Total malloc times:%d\n", malloc_times);
- printf(" Total free times:%d\n", free_times);
- printf("===========================\n");
-}
-#endif
-
-void *xml_malloc(int32_t size)
-{
-#ifdef DEBUG_MODE
- malloc_times++;
-#endif
- return malloc(size);
-}
-
-void xml_free(void *buffer)
-{
-#ifdef DEBUG_MODE
- free_times++;
-#endif
- free(buffer);
-}
-
-XML_TREE *xml_tree_fillnode(uint8_t **buf, int32_t tagLen)
-{
- XML_TREE *Tree;
- uint8_t *pAttr, *pName, *pValue;
- int32_t nameLen, valueLen;
- uint8_t *buffer = *buf;
-
- if (NULL == (Tree = (XML_TREE *) xml_malloc(sizeof(XML_TREE))))
- return NULL;
- memset(Tree, 0, sizeof(XML_TREE));
-
- strncpy((char *)Tree->tag, (char *)++buffer, tagLen);
- buffer += tagLen;
- pAttr = buffer;
-
- /* attribute */
- while (NULL !=
- (pAttr =
- XML_DOM_getAttr(pAttr, &pName, &nameLen, &pValue,
- &valueLen))) {
- XML_TREE_ATTR *attr;
- if (NULL ==
- (attr = (XML_TREE_ATTR *) xml_malloc(sizeof(XML_TREE_ATTR))))
- return NULL;
- memset(attr, 0, sizeof(XML_TREE_ATTR));
- strncpy((char *)attr->name, (char *)pName, nameLen);
- strncpy((char *)attr->value, (char *)pValue, valueLen);
- buffer = pValue + valueLen + 1;
-
- if (NULL != Tree->attr) // no attribute now
- Tree->last_attr->next = attr;
- else
- Tree->attr = attr;
- Tree->last_attr = attr;
- }
-
- /* value */
- pAttr = XML_DOM_getValue(buffer, &pValue, &valueLen);
- if (pAttr != NULL && valueLen > 0) {
- strncpy((char *)Tree->value, (char *)pValue, valueLen);
- buffer = pValue + valueLen;
- }
-
- *buf = buffer;
- return Tree;
-}
-
-XML_TREE *XML_makeTree(uint8_t **buf)
-{
- uint8_t *pBuf;
- int32_t valueLen, tagType;
- uint8_t *buffer = *buf;
- XML_TREE *TreeHead = NULL;
-
- if (NULL == (buffer = XML_DOM_getTag(buffer, &valueLen, &tagType)))
- return NULL;
- if (XML_TAG_END == tagType)
- return NULL;
- if (NULL == (TreeHead = xml_tree_fillnode(&buffer, valueLen)))
- return NULL;
- if (XML_TAG_SELF == tagType) {
- *buf = buffer;
- return TreeHead;
- }
-
- do {
- if (NULL == (pBuf = XML_DOM_getTag(buffer, &valueLen, &tagType)))
- return NULL;
-
- switch (tagType) {
- case XML_TAG_SELF:
- case XML_TAG_START:
- if (NULL == TreeHead->child)
- TreeHead->child = XML_makeTree(&buffer);
- else if (NULL == TreeHead->child->last_brother) {
- TreeHead->child->brother = XML_makeTree(&buffer);
- TreeHead->child->last_brother = TreeHead->child->brother;
- } else {
- TreeHead->child->last_brother->brother =
- XML_makeTree(&buffer);
- TreeHead->child->last_brother =
- TreeHead->child->last_brother->brother;
- }
- break;
- case XML_TAG_END:
- *buf = pBuf;
- return TreeHead;
- }
- buffer++;
- } while (1);
-}
-
-void XML_freeTree(XML_TREE * pTree)
-{
- XML_TREE *p, *pNext;
- XML_TREE_ATTR *pa, *lastpa;
-
- if (NULL == pTree)
- return;
-
- p = pTree->brother;
- while (NULL != p) {
- pNext = p->brother;
- p->brother = NULL;
- XML_freeTree(p);
- p = pNext;
- }
-
- if (NULL != pTree->child)
- XML_freeTree(pTree->child);
-
- pa = pTree->attr;
- while (NULL != pa) {
- lastpa = pa;
- pa = pa->next;
- xml_free(lastpa);
- }
- xml_free(pTree);
-}
-
-#endif /* XML_TREE_STRUCTURE */
-
-#endif /* WBXML_DOM_PARSER */
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index c9afa19..1e6b2e7 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -7,7 +7,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := easymocklib
+LOCAL_STATIC_JAVA_LIBRARIES := easymocklib mockito-target
LOCAL_PACKAGE_NAME := mediaframeworktest
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index b698705..91ee2c6 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -71,4 +71,9 @@
android:label="Media Power tests InstrumentationRunner">
</instrumentation>
+ <instrumentation android:name=".MediaFrameworkIntegrationTestRunner"
+ android:targetPackage="com.android.mediaframeworktest"
+ android:label="MediaFramework integration tests InstrumentationRunner">
+ </instrumentation>
+
</manifest>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java
new file mode 100644
index 0000000..7751fcc
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+
+import com.android.mediaframeworktest.integration.CameraBinderTest;
+import com.android.mediaframeworktest.integration.CameraDeviceBinderTest;
+
+import junit.framework.TestSuite;
+
+/**
+ * Instrumentation Test Runner for all media framework integration tests.
+ *
+ * Running all tests:
+ *
+ * adb shell am instrument -w com.android.mediaframeworktest/.MediaFrameworkIntegrationTestRunner
+ */
+
+public class MediaFrameworkIntegrationTestRunner extends InstrumentationTestRunner {
+
+ private static final String TAG = "MediaFrameworkIntegrationTestRunner";
+
+ public static int mCameraId = 0;
+
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(CameraBinderTest.class);
+ suite.addTestSuite(CameraDeviceBinderTest.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return MediaFrameworkIntegrationTestRunner.class.getClassLoader();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ String cameraId = (String) icicle.get("camera_id");
+ if (cameraId != null) {
+ try {
+ Log.v(TAG,
+ String.format("Reading camera_id from icicle: '%s'", cameraId));
+ mCameraId = Integer.parseInt(cameraId);
+ }
+ catch (NumberFormatException e) {
+ Log.e(TAG, String.format("Failed to convert camera_id to integer"));
+ }
+ }
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index 92ac9eb..cbb6642 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -50,7 +50,7 @@
* Running all tests:
*
* adb shell am instrument \
- * -w com.android.smstests.MediaPlayerInstrumentationTestRunner
+ * -w com.android.mediaframeworktest/.MediaFrameworkTestRunner
*/
public class MediaFrameworkTestRunner extends InstrumentationTestRunner {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
index 62af3f3..ecdc287 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
@@ -48,6 +48,7 @@
addMediaRecorderStateUnitTests(suite);
addMediaPlayerStateUnitTests(suite);
addMediaScannerUnitTests(suite);
+ addCameraUnitTests(suite);
return suite;
}
@@ -56,6 +57,14 @@
return MediaFrameworkUnitTestRunner.class.getClassLoader();
}
+ private void addCameraUnitTests(TestSuite suite) {
+ suite.addTestSuite(CameraUtilsDecoratorTest.class);
+ suite.addTestSuite(CameraUtilsRuntimeExceptionTest.class);
+ suite.addTestSuite(CameraUtilsUncheckedThrowTest.class);
+ suite.addTestSuite(CameraUtilsBinderDecoratorTest.class);
+ suite.addTestSuite(CameraMetadataTest.class);
+ }
+
// Running all unit tests checking the state machine may be time-consuming.
private void addMediaMetadataRetrieverStateUnitTests(TestSuite suite) {
suite.addTestSuite(MediaMetadataRetrieverTest.class);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
index 2f864d7..7f23ba5 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
@@ -36,7 +36,12 @@
/**
* Junit / Instrumentation test case for the camera api
-
+ *
+ * To run only tests in this class:
+ *
+ * adb shell am instrument \
+ * -e class com.android.mediaframeworktest.functional.CameraTest \
+ * -w com.android.mediaframeworktest/.MediaFrameworkTestRunner
*/
public class CameraTest extends ActivityInstrumentationTestCase<MediaFrameworkTest> {
private String TAG = "CameraTest";
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
new file mode 100644
index 0000000..bc3adc9
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.integration;
+
+import android.hardware.CameraInfo;
+import android.hardware.ICamera;
+import android.hardware.ICameraClient;
+import android.hardware.ICameraServiceListener;
+import android.hardware.IProCameraCallbacks;
+import android.hardware.IProCameraUser;
+import android.hardware.photography.CameraMetadata;
+import android.hardware.photography.ICameraDeviceCallbacks;
+import android.hardware.photography.ICameraDeviceUser;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+/**
+ * <p>
+ * Junit / Instrumentation test case for the camera2 api
+ * </p>
+ * <p>
+ * To run only tests in this class:
+ * </p>
+ *
+ * <pre>
+ * adb shell am instrument \
+ * -e class com.android.mediaframeworktest.integration.CameraBinderTest \
+ * -w com.android.mediaframeworktest/.MediaFrameworkIntegrationTestRunner
+ * </pre>
+ */
+public class CameraBinderTest extends AndroidTestCase {
+ static String TAG = "CameraBinderTest";
+
+ protected CameraBinderTestUtils mUtils;
+
+ public CameraBinderTest() {
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mUtils = new CameraBinderTestUtils(getContext());
+ }
+
+ @SmallTest
+ public void testNumberOfCameras() throws Exception {
+
+ int numCameras = mUtils.getCameraService().getNumberOfCameras();
+ assertTrue("At least this many cameras: " + mUtils.getGuessedNumCameras(),
+ numCameras >= mUtils.getGuessedNumCameras());
+ Log.v(TAG, "Number of cameras " + numCameras);
+ }
+
+ @SmallTest
+ public void testCameraInfo() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ CameraInfo info = new CameraInfo();
+ info.info.facing = -1;
+ info.info.orientation = -1;
+
+ assertTrue(
+ "Camera service returned info for camera " + cameraId,
+ mUtils.getCameraService().getCameraInfo(cameraId, info) ==
+ CameraBinderTestUtils.NO_ERROR);
+ assertTrue("Facing was not set for camera " + cameraId, info.info.facing != -1);
+ assertTrue("Orientation was not set for camera " + cameraId,
+ info.info.orientation != -1);
+
+ Log.v(TAG, "Camera " + cameraId + " info: facing " + info.info.facing
+ + ", orientation " + info.info.orientation);
+ }
+ }
+
+ static abstract class DummyBase extends Binder implements android.os.IInterface {
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+ }
+
+ static class DummyCameraClient extends DummyBase implements ICameraClient {
+ }
+
+ @SmallTest
+ public void testConnect() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ ICameraClient dummyCallbacks = new DummyCameraClient();
+
+ String clientPackageName = getContext().getPackageName();
+
+ ICamera cameraUser = mUtils.getCameraService().connect(dummyCallbacks, cameraId,
+ clientPackageName,
+ CameraBinderTestUtils.USE_CALLING_UID);
+ assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
+
+ Log.v(TAG, String.format("Camera %s connected", cameraId));
+
+ cameraUser.disconnect();
+ }
+ }
+
+ static class DummyProCameraCallbacks extends DummyBase implements IProCameraCallbacks {
+ }
+
+ @SmallTest
+ public void testConnectPro() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ IProCameraCallbacks dummyCallbacks = new DummyProCameraCallbacks();
+
+ String clientPackageName = getContext().getPackageName();
+
+ IProCameraUser cameraUser = mUtils.getCameraService().connectPro(dummyCallbacks,
+ cameraId,
+ clientPackageName, CameraBinderTestUtils.USE_CALLING_UID);
+ assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
+
+ Log.v(TAG, String.format("Camera %s connected", cameraId));
+
+ cameraUser.disconnect();
+ }
+ }
+
+ static class DummyCameraDeviceCallbacks extends DummyBase implements ICameraDeviceCallbacks {
+
+ @Override
+ public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
+ }
+
+ @Override
+ public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException {
+ }
+ }
+
+ @SmallTest
+ public void testConnectDevice() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ ICameraDeviceCallbacks dummyCallbacks = new DummyCameraDeviceCallbacks();
+
+ String clientPackageName = getContext().getPackageName();
+
+ ICameraDeviceUser cameraUser = mUtils.getCameraService().connectDevice(dummyCallbacks,
+ cameraId,
+ clientPackageName, CameraBinderTestUtils.USE_CALLING_UID);
+ assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
+
+ Log.v(TAG, String.format("Camera %s connected", cameraId));
+
+ cameraUser.disconnect();
+ }
+ }
+
+ static class DummyCameraServiceListener extends DummyBase implements ICameraServiceListener {
+ @Override
+ public void onStatusChanged(int status, int cameraId)
+ throws RemoteException {
+ Log.v(TAG, String.format("Camera %d has status changed to 0x%x", cameraId, status));
+ }
+ }
+
+ /**
+ * <pre>
+ * adb shell am instrument \
+ * -e class 'com.android.mediaframeworktest.integration.CameraBinderTest#testAddRemoveListeners' \
+ * -w com.android.mediaframeworktest/.MediaFrameworkIntegrationTestRunner
+ * </pre>
+ */
+ @SmallTest
+ public void testAddRemoveListeners() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ ICameraServiceListener listener = new DummyCameraServiceListener();
+
+ assertTrue(
+ "Listener was removed before added",
+ mUtils.getCameraService().removeListener(listener) ==
+ CameraBinderTestUtils.BAD_VALUE);
+
+ assertTrue("Listener was not added",
+ mUtils.getCameraService().addListener(listener) ==
+ CameraBinderTestUtils.NO_ERROR);
+ assertTrue(
+ "Listener was wrongly added again",
+ mUtils.getCameraService().addListener(listener) ==
+ CameraBinderTestUtils.ALREADY_EXISTS);
+
+ assertTrue(
+ "Listener was not removed",
+ mUtils.getCameraService().removeListener(listener) ==
+ CameraBinderTestUtils.NO_ERROR);
+ assertTrue(
+ "Listener was wrongly removed again",
+ mUtils.getCameraService().removeListener(listener) ==
+ CameraBinderTestUtils.BAD_VALUE);
+ }
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
new file mode 100644
index 0000000..f0fc817
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
@@ -0,0 +1,92 @@
+
+package com.android.mediaframeworktest.integration;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManager;
+import android.hardware.ICameraService;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+public class CameraBinderTestUtils {
+ private final ICameraService mCameraService;
+ private int mGuessedNumCameras;
+
+ static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
+
+ protected static final int USE_CALLING_UID = -1;
+ protected static final int BAD_VALUE = -22;
+ protected static final int ALREADY_EXISTS = -17;
+ protected static final int NO_ERROR = 0;
+ private final Context mContext;
+
+ public CameraBinderTestUtils(Context context) {
+
+ mContext = context;
+
+ guessNumCameras();
+
+ IBinder cameraServiceBinder = ServiceManager
+ .getService(CameraBinderTestUtils.CAMERA_SERVICE_BINDER_NAME);
+ assertNotNull("Camera service IBinder should not be null", cameraServiceBinder);
+
+ this.mCameraService = ICameraService.Stub.asInterface(cameraServiceBinder);
+ assertNotNull("Camera service should not be null", getCameraService());
+ }
+
+ private void guessNumCameras() {
+
+ /**
+ * Why do we need this? This way we have no dependency on getNumCameras
+ * actually working. On most systems there are only 0, 1, or 2 cameras,
+ * and this covers that 'usual case'. On other systems there might be 3+
+ * cameras, but this will at least check the first 2.
+ */
+ this.mGuessedNumCameras = 0;
+
+ // Front facing camera
+ if (CameraBinderTestUtils.isFeatureAvailable(mContext,
+ PackageManager.FEATURE_CAMERA_FRONT)) {
+ this.mGuessedNumCameras = getGuessedNumCameras() + 1;
+ }
+
+ // Back facing camera
+ if (CameraBinderTestUtils.isFeatureAvailable(mContext,
+ PackageManager.FEATURE_CAMERA)) {
+ this.mGuessedNumCameras = getGuessedNumCameras() + 1;
+ }
+
+ // Any facing camera
+ if (getGuessedNumCameras() == 0
+ && CameraBinderTestUtils.isFeatureAvailable(mContext,
+ PackageManager.FEATURE_CAMERA_ANY)) {
+ this.mGuessedNumCameras = getGuessedNumCameras() + 1;
+ }
+
+ Log.v(CameraBinderTest.TAG, "Guessing there are at least " + getGuessedNumCameras()
+ + " cameras");
+ }
+
+ final static public boolean isFeatureAvailable(Context context, String feature) {
+ final PackageManager packageManager = context.getPackageManager();
+ final FeatureInfo[] featuresList = packageManager.getSystemAvailableFeatures();
+ for (FeatureInfo f : featuresList) {
+ if (f.name != null && f.name.equals(feature)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ICameraService getCameraService() {
+ return mCameraService;
+ }
+
+ int getGuessedNumCameras() {
+ return mGuessedNumCameras;
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
new file mode 100644
index 0000000..0cbb989
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.integration;
+
+import android.graphics.SurfaceTexture;
+import android.hardware.photography.CameraMetadata;
+import android.hardware.photography.CaptureRequest;
+import android.hardware.photography.ICameraDeviceCallbacks;
+import android.hardware.photography.ICameraDeviceUser;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import android.view.Surface;
+
+import static android.hardware.photography.CameraDevice.TEMPLATE_PREVIEW;
+
+import com.android.mediaframeworktest.MediaFrameworkIntegrationTestRunner;
+import com.android.mediaframeworktest.integration.CameraBinderTest.DummyBase;
+
+public class CameraDeviceBinderTest extends AndroidTestCase {
+ private static String TAG = "CameraDeviceBinderTest";
+
+ private int mCameraId;
+ private ICameraDeviceUser mCameraUser;
+ private CameraBinderTestUtils mUtils;
+
+ public CameraDeviceBinderTest() {
+ }
+
+ static class DummyCameraDeviceCallbacks extends DummyBase implements ICameraDeviceCallbacks {
+
+ @Override
+ public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
+ }
+
+ @Override
+ public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException {
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mUtils = new CameraBinderTestUtils(getContext());
+
+ // This cannot be set in the constructor, since the onCreate isn't
+ // called yet
+ mCameraId = MediaFrameworkIntegrationTestRunner.mCameraId;
+
+ ICameraDeviceCallbacks dummyCallbacks = new DummyCameraDeviceCallbacks();
+
+ String clientPackageName = getContext().getPackageName();
+
+ mCameraUser = mUtils.getCameraService().connectDevice(dummyCallbacks, mCameraId,
+ clientPackageName, CameraBinderTestUtils.USE_CALLING_UID);
+ assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser);
+
+ Log.v(TAG, String.format("Camera %s connected", mCameraId));
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mCameraUser.disconnect();
+ mCameraUser = null;
+ }
+
+ @SmallTest
+ public void testCreateDefaultRequest() throws Exception {
+ CameraMetadata metadata = new CameraMetadata();
+ assertTrue(metadata.isEmpty());
+
+ int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata);
+ assertEquals(CameraBinderTestUtils.NO_ERROR, status);
+ assertFalse(metadata.isEmpty());
+
+ metadata.close();
+ }
+
+ @SmallTest
+ public void testCreateStream() throws Exception {
+ SurfaceTexture surfaceTexture = new SurfaceTexture(/* ignored */0);
+ surfaceTexture.setDefaultBufferSize(640, 480);
+ Surface surface = new Surface(surfaceTexture);
+
+ int streamId = mCameraUser.createStream(/* ignored */10, /* ignored */20, /* ignored */30,
+ surface);
+
+ assertEquals(0, streamId);
+
+ assertEquals(CameraBinderTestUtils.ALREADY_EXISTS,
+ mCameraUser.createStream(/* ignored */0, /* ignored */0, /* ignored */0, surface));
+
+ assertEquals(CameraBinderTestUtils.NO_ERROR, mCameraUser.deleteStream(streamId));
+ }
+
+ @SmallTest
+ public void testDeleteInvalidStream() throws Exception {
+ assertEquals(CameraBinderTestUtils.BAD_VALUE, mCameraUser.deleteStream(-1));
+ assertEquals(CameraBinderTestUtils.BAD_VALUE, mCameraUser.deleteStream(0));
+ assertEquals(CameraBinderTestUtils.BAD_VALUE, mCameraUser.deleteStream(1));
+ assertEquals(CameraBinderTestUtils.BAD_VALUE, mCameraUser.deleteStream(0xC0FFEE));
+ }
+
+ @SmallTest
+ public void testCreateStreamTwo() throws Exception {
+
+ // Create first stream
+
+ SurfaceTexture surfaceTexture = new SurfaceTexture(/* ignored */0);
+ surfaceTexture.setDefaultBufferSize(640, 480);
+ Surface surface = new Surface(surfaceTexture);
+
+ int streamId = mCameraUser.createStream(/* ignored */0, /* ignored */0, /* ignored */0,
+ surface);
+
+ assertEquals(0, streamId);
+
+ assertEquals(CameraBinderTestUtils.ALREADY_EXISTS,
+ mCameraUser.createStream(/* ignored */0, /* ignored */0, /* ignored */0, surface));
+
+ // Create second stream.
+
+ SurfaceTexture surfaceTexture2 = new SurfaceTexture(/* ignored */0);
+ surfaceTexture2.setDefaultBufferSize(640, 480);
+ Surface surface2 = new Surface(surfaceTexture2);
+
+ int streamId2 = mCameraUser.createStream(/* ignored */0, /* ignored */0, /* ignored */0,
+ surface2);
+
+ assertEquals(1, streamId2);
+
+ // Clean up streams
+
+ assertEquals(CameraBinderTestUtils.NO_ERROR, mCameraUser.deleteStream(streamId));
+ assertEquals(CameraBinderTestUtils.NO_ERROR, mCameraUser.deleteStream(streamId2));
+ }
+
+ @SmallTest
+ public void testSubmitBadRequest() throws Exception {
+
+ CameraMetadata metadata = new CameraMetadata();
+ assertTrue(metadata.isEmpty());
+
+ CaptureRequest request = new CaptureRequest();
+ assertTrue(request.isEmpty());
+
+ int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata);
+ assertEquals(CameraBinderTestUtils.NO_ERROR, status);
+ assertFalse(metadata.isEmpty());
+
+ request.swap(metadata);
+ assertFalse(request.isEmpty());
+ assertTrue(metadata.isEmpty());
+
+ status = mCameraUser.submitRequest(request, /* streaming */false);
+ assertEquals("Expected submitRequest to return BAD_VALUE " +
+ "since we had 0 surface targets set.", CameraBinderTestUtils.BAD_VALUE, status);
+
+ SurfaceTexture surfaceTexture = new SurfaceTexture(/* ignored */0);
+ surfaceTexture.setDefaultBufferSize(640, 480);
+ Surface surface = new Surface(surfaceTexture);
+ request.addTarget(surface);
+
+ status = mCameraUser.submitRequest(request, /* streaming */false);
+ assertEquals("Expected submitRequest to return BAD_VALUE since " +
+ "the target surface wasn't registered with createStream.",
+ CameraBinderTestUtils.BAD_VALUE, status);
+
+ request.close();
+ metadata.close();
+ surface.release();
+ }
+
+ @SmallTest
+ public void testSubmitGoodRequest() throws Exception {
+
+ CameraMetadata metadata = new CameraMetadata();
+ assertTrue(metadata.isEmpty());
+
+ CaptureRequest request = new CaptureRequest();
+ assertTrue(request.isEmpty());
+
+ // Create default request from template.
+
+ int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata);
+ assertEquals(CameraBinderTestUtils.NO_ERROR, status);
+ assertFalse(metadata.isEmpty());
+
+ request.swap(metadata);
+ assertFalse(request.isEmpty());
+ assertTrue(metadata.isEmpty());
+
+ SurfaceTexture surfaceTexture = new SurfaceTexture(/* ignored */0);
+ surfaceTexture.setDefaultBufferSize(640, 480);
+ Surface surface = new Surface(surfaceTexture);
+
+ // Create stream first. Pre-requisite to submitting a request using that
+ // stream.
+
+ int streamId = mCameraUser.createStream(/* ignored */10, /* ignored */20, /* ignored */30,
+ surface);
+ assertEquals(0, streamId);
+
+ request.addTarget(surface);
+
+ // Submit valid request twice.
+
+ int requestId1;
+ requestId1 = mCameraUser.submitRequest(request, /* streaming */false);
+ assertTrue("Request IDs should be non-negative", requestId1 >= 0);
+
+ int requestId2 = mCameraUser.submitRequest(request, /* streaming */false);
+ assertTrue("Request IDs should be non-negative", requestId2 >= 0);
+ assertNotSame("Request IDs should be unique for multiple requests", requestId1, requestId2);
+
+ surface.release();
+ request.close();
+ metadata.close();
+ }
+
+ @SmallTest
+ public void testSubmitStreamingRequest() throws Exception {
+
+ CameraMetadata metadata = new CameraMetadata();
+ assertTrue(metadata.isEmpty());
+
+ CaptureRequest request = new CaptureRequest();
+ assertTrue(request.isEmpty());
+
+ // Create default request from template.
+
+ int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata);
+ assertEquals(CameraBinderTestUtils.NO_ERROR, status);
+ assertFalse(metadata.isEmpty());
+
+ request.swap(metadata);
+ assertFalse(request.isEmpty());
+ assertTrue(metadata.isEmpty());
+
+ SurfaceTexture surfaceTexture = new SurfaceTexture(/* ignored */0);
+ surfaceTexture.setDefaultBufferSize(640, 480);
+ Surface surface = new Surface(surfaceTexture);
+
+ // Create stream first. Pre-requisite to submitting a request using that
+ // stream.
+
+ int streamId = mCameraUser.createStream(/* ignored */10, /* ignored */20, /* ignored */30,
+ surface);
+ assertEquals(0, streamId);
+
+ request.addTarget(surface);
+
+ // Submit valid request once (non-streaming), and another time
+ // (streaming)
+
+ int requestId1;
+ requestId1 = mCameraUser.submitRequest(request, /* streaming */true);
+ assertTrue("Request IDs should be non-negative", requestId1 >= 0);
+
+ int requestIdStreaming = mCameraUser.submitRequest(request, /* streaming */false);
+ assertTrue("Request IDs should be non-negative", requestIdStreaming >= 0);
+ assertNotSame("Request IDs should be unique for multiple requests", requestId1,
+ requestIdStreaming);
+
+ status = mCameraUser.cancelRequest(-1);
+ assertEquals("Invalid request IDs should not be cancellable",
+ CameraBinderTestUtils.BAD_VALUE, status);
+
+ status = mCameraUser.cancelRequest(requestId1);
+ assertEquals("Non-streaming request IDs should not be cancellable",
+ CameraBinderTestUtils.BAD_VALUE, status);
+
+ status = mCameraUser.cancelRequest(requestIdStreaming);
+ assertEquals("Streaming request IDs should be cancellable", CameraBinderTestUtils.NO_ERROR,
+ status);
+
+ surface.release();
+ request.close();
+ metadata.close();
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
new file mode 100644
index 0000000..3400434
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.hardware.photography.CameraMetadata;
+
+/**
+ * <pre>
+ * adb shell am instrument \
+ * -e class 'com.android.mediaframeworktest.unit.CameraMetadataTest' \
+ * -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
+ * </pre>
+ */
+public class CameraMetadataTest extends junit.framework.TestCase {
+
+ CameraMetadata mMetadata;
+ Parcel mParcel;
+
+ @Override
+ public void setUp() {
+ mMetadata = new CameraMetadata();
+ mParcel = Parcel.obtain();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mMetadata.close();
+ mMetadata = null;
+
+ mParcel.recycle();
+ mParcel = null;
+ }
+
+ @SmallTest
+ public void testNew() {
+ assertEquals(0, mMetadata.getEntryCount());
+ assertTrue(mMetadata.isEmpty());
+ }
+
+ @SmallTest
+ public void testClose() throws Exception {
+ mMetadata.isEmpty(); // no throw
+
+ assertFalse(mMetadata.isClosed());
+
+ mMetadata.close();
+
+ assertTrue(mMetadata.isClosed());
+
+ // OK: second close should not throw
+ mMetadata.close();
+
+ assertTrue(mMetadata.isClosed());
+
+ // All other calls after close should throw IllegalStateException
+
+ try {
+ mMetadata.isEmpty();
+ fail("Unreachable -- isEmpty after close should throw IllegalStateException");
+ } catch (IllegalStateException e) {
+ // good: we expect calling this method after close to fail
+ }
+
+ try {
+ mMetadata.getEntryCount();
+ fail("Unreachable -- getEntryCount after close should throw IllegalStateException");
+ } catch (IllegalStateException e) {
+ // good: we expect calling this method after close to fail
+ }
+
+
+ try {
+ mMetadata.swap(mMetadata);
+ fail("Unreachable -- swap after close should throw IllegalStateException");
+ } catch (IllegalStateException e) {
+ // good: we expect calling this method after close to fail
+ }
+
+ try {
+ mMetadata.readFromParcel(mParcel);
+ fail("Unreachable -- readFromParcel after close should throw IllegalStateException");
+ } catch (IllegalStateException e) {
+ // good: we expect calling this method after close to fail
+ }
+
+ try {
+ mMetadata.writeToParcel(mParcel, /*flags*/0);
+ fail("Unreachable -- writeToParcel after close should throw IllegalStateException");
+ } catch (IllegalStateException e) {
+ // good: we expect calling this method after close to fail
+ }
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java
new file mode 100644
index 0000000..7c48992
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import android.hardware.photography.CameraAccessException;
+import android.hardware.photography.utils.CameraBinderDecorator;
+import android.hardware.photography.utils.CameraRuntimeException;
+import android.os.DeadObjectException;
+import android.os.RemoteException;
+import android.os.TransactionTooLargeException;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.mockito.Mockito.*;
+import static android.hardware.photography.utils.CameraBinderDecorator.*;
+import static android.hardware.photography.CameraAccessException.*;
+
+import junit.framework.Assert;
+
+public class CameraUtilsBinderDecoratorTest extends junit.framework.TestCase {
+
+ private interface ICameraBinderStereotype {
+
+ double doNothing();
+
+ // int is a 'status_t'
+ int doSomethingPositive();
+
+ int doSomethingNoError();
+
+ int doSomethingPermissionDenied();
+
+ int doSomethingAlreadyExists();
+
+ int doSomethingBadValue();
+
+ int doSomethingDeadObject() throws CameraRuntimeException;
+
+ int doSomethingBadPolicy() throws CameraRuntimeException;
+
+ int doSomethingDeviceBusy() throws CameraRuntimeException;
+
+ int doSomethingNoSuchDevice() throws CameraRuntimeException;
+
+ int doSomethingUnknownErrorCode();
+
+ int doSomethingThrowDeadObjectException() throws RemoteException;
+
+ int doSomethingThrowTransactionTooLargeException() throws RemoteException;
+ }
+
+ private static final double SOME_ARBITRARY_DOUBLE = 1.0;
+ private static final int SOME_ARBITRARY_POSITIVE_INT = 5;
+ private static final int SOME_ARBITRARY_NEGATIVE_INT = -0xC0FFEE;
+
+ @SmallTest
+ public void testStereotypes() {
+
+ ICameraBinderStereotype mock = mock(ICameraBinderStereotype.class);
+ try {
+ when(mock.doNothing()).thenReturn(SOME_ARBITRARY_DOUBLE);
+ when(mock.doSomethingPositive()).thenReturn(SOME_ARBITRARY_POSITIVE_INT);
+ when(mock.doSomethingNoError()).thenReturn(NO_ERROR);
+ when(mock.doSomethingPermissionDenied()).thenReturn(PERMISSION_DENIED);
+ when(mock.doSomethingAlreadyExists()).thenReturn(ALREADY_EXISTS);
+ when(mock.doSomethingBadValue()).thenReturn(BAD_VALUE);
+ when(mock.doSomethingDeadObject()).thenReturn(DEAD_OBJECT);
+ when(mock.doSomethingBadPolicy()).thenReturn(EACCES);
+ when(mock.doSomethingDeviceBusy()).thenReturn(EBUSY);
+ when(mock.doSomethingNoSuchDevice()).thenReturn(ENODEV);
+ when(mock.doSomethingUnknownErrorCode()).thenReturn(SOME_ARBITRARY_NEGATIVE_INT);
+ when(mock.doSomethingThrowDeadObjectException()).thenThrow(new DeadObjectException());
+ when(mock.doSomethingThrowTransactionTooLargeException()).thenThrow(
+ new TransactionTooLargeException());
+ } catch (RemoteException e) {
+ Assert.fail("Unreachable");
+ }
+
+ ICameraBinderStereotype decoratedMock = CameraBinderDecorator.newInstance(mock);
+
+ // ignored by decorator because return type is double, not int
+ assertEquals(SOME_ARBITRARY_DOUBLE, decoratedMock.doNothing());
+
+ // pass through for positive values
+ assertEquals(SOME_ARBITRARY_POSITIVE_INT, decoratedMock.doSomethingPositive());
+
+ // pass through NO_ERROR
+ assertEquals(NO_ERROR, decoratedMock.doSomethingNoError());
+
+ try {
+ decoratedMock.doSomethingPermissionDenied();
+ Assert.fail("Should've thrown SecurityException");
+ } catch (SecurityException e) {
+ }
+
+ assertEquals(ALREADY_EXISTS, decoratedMock.doSomethingAlreadyExists());
+
+ try {
+ decoratedMock.doSomethingBadValue();
+ Assert.fail("Should've thrown IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ decoratedMock.doSomethingDeadObject();
+ Assert.fail("Should've thrown CameraRuntimeException");
+ } catch (CameraRuntimeException e) {
+ assertEquals(CAMERA_DISCONNECTED, e.getReason());
+ }
+
+ try {
+ decoratedMock.doSomethingBadPolicy();
+ Assert.fail("Should've thrown CameraRuntimeException");
+ } catch (CameraRuntimeException e) {
+ assertEquals(CAMERA_DISABLED, e.getReason());
+ }
+
+ try {
+ decoratedMock.doSomethingDeviceBusy();
+ Assert.fail("Should've thrown CameraRuntimeException");
+ } catch (CameraRuntimeException e) {
+ assertEquals(CAMERA_IN_USE, e.getReason());
+ }
+
+ try {
+ decoratedMock.doSomethingNoSuchDevice();
+ Assert.fail("Should've thrown CameraRuntimeException");
+ } catch (CameraRuntimeException e) {
+ assertEquals(CAMERA_DISCONNECTED, e.getReason());
+ }
+
+ try {
+ decoratedMock.doSomethingUnknownErrorCode();
+ Assert.fail("Should've thrown UnsupportedOperationException");
+ } catch (UnsupportedOperationException e) {
+ assertEquals(String.format("Unknown error %d",
+ SOME_ARBITRARY_NEGATIVE_INT), e.getMessage());
+ }
+
+ try {
+ decoratedMock.doSomethingThrowDeadObjectException();
+ Assert.fail("Should've thrown CameraRuntimeException");
+ } catch (CameraRuntimeException e) {
+ assertEquals(CAMERA_DISCONNECTED, e.getReason());
+ } catch (RemoteException e) {
+ Assert.fail("Should not throw a DeadObjectException directly, but rethrow");
+ }
+
+ try {
+ decoratedMock.doSomethingThrowTransactionTooLargeException();
+ Assert.fail("Should've thrown UnsupportedOperationException");
+ } catch (UnsupportedOperationException e) {
+ assertTrue(e.getCause() instanceof TransactionTooLargeException);
+ } catch (RemoteException e) {
+ Assert.fail("Should not throw a TransactionTooLargeException directly, but rethrow");
+ }
+ }
+
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsDecoratorTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsDecoratorTest.java
new file mode 100644
index 0000000..bae17fa
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsDecoratorTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.hardware.photography.utils.*;
+import android.hardware.photography.utils.Decorator.DecoratorListener;
+
+import junit.framework.Assert;
+
+import java.lang.reflect.Method;
+
+/**
+ * adb shell am instrument -e class 'com.android.mediaframeworktest.unit.CameraUtilsDecoratorTest' \
+ * -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
+ */
+public class CameraUtilsDecoratorTest extends junit.framework.TestCase {
+ private DummyListener mDummyListener;
+ private DummyInterface mIface;
+
+ @Override
+ public void setUp() {
+ mDummyListener = new DummyListener();
+ mIface = Decorator.newInstance(new DummyImpl(), mDummyListener);
+ }
+
+ interface DummyInterface {
+ int addValues(int x, int y, int z);
+
+ void raiseException() throws Exception;
+
+ void raiseUnsupportedOperationException() throws UnsupportedOperationException;
+ }
+
+ class DummyImpl implements DummyInterface {
+ @Override
+ public int addValues(int x, int y, int z) {
+ return x + y + z;
+ }
+
+ @Override
+ public void raiseException() throws Exception {
+ throw new Exception("Test exception");
+ }
+
+ @Override
+ public void raiseUnsupportedOperationException() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException("Test exception");
+ }
+ }
+
+ class DummyListener implements DecoratorListener {
+
+ public boolean beforeCalled = false;
+ public boolean afterCalled = false;
+ public boolean catchCalled = false;
+ public boolean finallyCalled = false;
+ public Object resultValue = null;
+
+ public boolean raiseException = false;
+
+ @Override
+ public void onBeforeInvocation(Method m, Object[] args) {
+ beforeCalled = true;
+ }
+
+ @Override
+ public void onAfterInvocation(Method m, Object[] args, Object result) {
+ afterCalled = true;
+ resultValue = result;
+
+ if (raiseException) {
+ throw new UnsupportedOperationException("Test exception");
+ }
+ }
+
+ @Override
+ public boolean onCatchException(Method m, Object[] args, Throwable t) {
+ catchCalled = true;
+ return false;
+ }
+
+ @Override
+ public void onFinally(Method m, Object[] args) {
+ finallyCalled = true;
+ }
+
+ };
+
+ @SmallTest
+ public void testDecorator() {
+
+ // TODO rewrite this using mocks
+
+ assertTrue(mIface.addValues(1, 2, 3) == 6);
+ assertTrue(mDummyListener.beforeCalled);
+ assertTrue(mDummyListener.afterCalled);
+
+ int resultValue = (Integer)mDummyListener.resultValue;
+ assertTrue(resultValue == 6);
+ assertTrue(mDummyListener.finallyCalled);
+ assertFalse(mDummyListener.catchCalled);
+ }
+
+ @SmallTest
+ public void testDecoratorExceptions() {
+
+ boolean gotExceptions = false;
+ try {
+ mIface.raiseException();
+ } catch (Exception e) {
+ gotExceptions = true;
+ assertTrue(e.getMessage() == "Test exception");
+ }
+ assertTrue(gotExceptions);
+ assertTrue(mDummyListener.beforeCalled);
+ assertFalse(mDummyListener.afterCalled);
+ assertTrue(mDummyListener.catchCalled);
+ assertTrue(mDummyListener.finallyCalled);
+ }
+
+ @SmallTest
+ public void testDecoratorUnsupportedOperationException() {
+
+ boolean gotExceptions = false;
+ try {
+ mIface.raiseUnsupportedOperationException();
+ } catch (UnsupportedOperationException e) {
+ gotExceptions = true;
+ assertTrue(e.getMessage() == "Test exception");
+ }
+ assertTrue(gotExceptions);
+ assertTrue(mDummyListener.beforeCalled);
+ assertFalse(mDummyListener.afterCalled);
+ assertTrue(mDummyListener.catchCalled);
+ assertTrue(mDummyListener.finallyCalled);
+ }
+
+ @SmallTest
+ public void testDecoratorRaisesException() {
+
+ boolean gotExceptions = false;
+ try {
+ mDummyListener.raiseException = true;
+ mIface.addValues(1, 2, 3);
+ Assert.fail("unreachable");
+ } catch (UnsupportedOperationException e) {
+ gotExceptions = true;
+ assertTrue(e.getMessage() == "Test exception");
+ }
+ assertTrue(gotExceptions);
+ assertTrue(mDummyListener.beforeCalled);
+ assertTrue(mDummyListener.afterCalled);
+ assertFalse(mDummyListener.catchCalled);
+ assertTrue(mDummyListener.finallyCalled);
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsRuntimeExceptionTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsRuntimeExceptionTest.java
new file mode 100644
index 0000000..8c2dd4d
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsRuntimeExceptionTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import android.hardware.photography.CameraAccessException;
+import android.hardware.photography.utils.CameraRuntimeException;
+import android.hardware.photography.utils.UncheckedThrow;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.Assert;
+
+public class CameraUtilsRuntimeExceptionTest extends junit.framework.TestCase {
+
+ @SmallTest
+ public void testCameraRuntimeException1() {
+ try {
+ CameraRuntimeException runtimeExc = new CameraRuntimeException(12345);
+ throw runtimeExc.asChecked();
+ } catch (CameraAccessException e) {
+ assertEquals(12345, e.getReason());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+ }
+
+ @SmallTest
+ public void testCameraRuntimeException2() {
+ try {
+ CameraRuntimeException runtimeExc = new CameraRuntimeException(12345, "Hello");
+ throw runtimeExc.asChecked();
+ } catch (CameraAccessException e) {
+ assertEquals(12345, e.getReason());
+ assertEquals("Hello", e.getMessage());
+ assertNull(e.getCause());
+ }
+ }
+
+ @SmallTest
+ public void testCameraRuntimeException3() {
+ Throwable cause = new IllegalStateException("For great justice");
+ try {
+ CameraRuntimeException runtimeExc = new CameraRuntimeException(12345, cause);
+ throw runtimeExc.asChecked();
+ } catch (CameraAccessException e) {
+ assertEquals(12345, e.getReason());
+ assertNull(e.getMessage());
+ assertEquals(cause, e.getCause());
+ }
+ }
+
+ @SmallTest
+ public void testCameraRuntimeException4() {
+ Throwable cause = new IllegalStateException("For great justice");
+ try {
+ CameraRuntimeException runtimeExc = new CameraRuntimeException(12345, "Hello", cause);
+ throw runtimeExc.asChecked();
+ } catch (CameraAccessException e) {
+ assertEquals(12345, e.getReason());
+ assertEquals("Hello", e.getMessage());
+ assertEquals(cause, e.getCause());
+ }
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsUncheckedThrowTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsUncheckedThrowTest.java
new file mode 100644
index 0000000..cbe123c
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsUncheckedThrowTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import android.hardware.photography.CameraAccessException;
+import android.hardware.photography.utils.UncheckedThrow;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.Assert;
+
+public class CameraUtilsUncheckedThrowTest extends junit.framework.TestCase {
+
+ private void fakeNeverThrowsCameraAccess() throws CameraAccessException {
+ }
+
+ @SmallTest
+ public void testUncheckedThrow() {
+ try {
+ UncheckedThrow.throwAnyException(new CameraAccessException(
+ CameraAccessException.CAMERA_DISCONNECTED));
+ Assert.fail("unreachable");
+ fakeNeverThrowsCameraAccess();
+ } catch (CameraAccessException e) {
+ }
+ }
+}
diff --git a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
index fe3929d..0304640 100644
--- a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
+++ b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
@@ -429,7 +429,7 @@
mMediaRecorder.start();
mState = 1;
} catch (Exception e) {
- Log.e(TAG, "Could start MediaRecorder: " + e.toString());
+ Log.e(TAG, "Could start MediaRecorder: ", e);
mMediaRecorder.release();
mMediaRecorder = null;
mState = 0;
@@ -439,7 +439,7 @@
mMediaRecorder.stop();
mMediaRecorder.reset();
} catch (Exception e) {
- Log.e(TAG, "Could not stop MediaRecorder: " + e.toString());
+ Log.e(TAG, "Could not stop MediaRecorder: ", e);
mMediaRecorder.release();
mMediaRecorder = null;
} finally {
@@ -466,7 +466,7 @@
mMediaRecorder.prepare();
}
catch (Exception e) {
- Log.e(TAG, "Could not prepare MediaRecorder: " + e.toString());
+ Log.e(TAG, "Could not prepare MediaRecorder: ", e);
mMediaRecorder.release();
mMediaRecorder = null;
}
@@ -475,9 +475,14 @@
@Override
public void stop() {
if (mMediaRecorder != null) {
- mMediaRecorder.stop();
- mMediaRecorder.release();
- mMediaRecorder = null;
+ try {
+ mMediaRecorder.stop();
+ } catch (Exception e) {
+ Log.e(TAG, "Could not stop MediaRecorder: ", e);
+ } finally {
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+ }
}
updatePlayPauseButton();
}
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 207cc4b..cda38e0 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -20,6 +20,7 @@
liblog \
libcutils \
libandroidfw \
+ libinput \
libutils \
libbinder \
libui \
diff --git a/native/android/input.cpp b/native/android/input.cpp
index f6ea576..e9d08b4 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -18,8 +18,8 @@
#include <utils/Log.h>
#include <android/input.h>
-#include <androidfw/Input.h>
-#include <androidfw/InputTransport.h>
+#include <input/Input.h>
+#include <input/InputTransport.h>
#include <utils/Looper.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
diff --git a/nfc-extras/Android.mk b/nfc-extras/Android.mk
index 131d898..330e2d4 100644
--- a/nfc-extras/Android.mk
+++ b/nfc-extras/Android.mk
@@ -9,6 +9,3 @@
LOCAL_MODULE:= com.android.nfc_extras
include $(BUILD_JAVA_LIBRARY)
-
-# put the classes.jar, with full class files instead of classes.dex inside, into the dist directory
-$(call dist-for-goals, droidcore, $(full_classes_jar):com.android.nfc_extras.jar)
diff --git a/opengl/java/android/opengl/EGL14.java b/opengl/java/android/opengl/EGL14.java
index cd53c17..b93557d 100644
--- a/opengl/java/android/opengl/EGL14.java
+++ b/opengl/java/android/opengl/EGL14.java
@@ -1,5 +1,4 @@
/*
-**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/opengl/java/android/opengl/EGLConfig.java b/opengl/java/android/opengl/EGLConfig.java
index d457c9f..a7a6bbb 100644
--- a/opengl/java/android/opengl/EGLConfig.java
+++ b/opengl/java/android/opengl/EGLConfig.java
@@ -29,7 +29,7 @@
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (!(o instanceof EGLConfig)) return false;
EGLConfig that = (EGLConfig) o;
return getHandle() == that.getHandle();
diff --git a/opengl/java/android/opengl/EGLContext.java b/opengl/java/android/opengl/EGLContext.java
index 41b8ef1..c93bd6e 100644
--- a/opengl/java/android/opengl/EGLContext.java
+++ b/opengl/java/android/opengl/EGLContext.java
@@ -29,7 +29,7 @@
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (!(o instanceof EGLContext)) return false;
EGLContext that = (EGLContext) o;
return getHandle() == that.getHandle();
diff --git a/opengl/java/android/opengl/EGLDisplay.java b/opengl/java/android/opengl/EGLDisplay.java
index 17d1a64..5b8043a 100644
--- a/opengl/java/android/opengl/EGLDisplay.java
+++ b/opengl/java/android/opengl/EGLDisplay.java
@@ -29,7 +29,7 @@
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (!(o instanceof EGLDisplay)) return false;
EGLDisplay that = (EGLDisplay) o;
return getHandle() == that.getHandle();
diff --git a/opengl/java/android/opengl/EGLExt.java b/opengl/java/android/opengl/EGLExt.java
index 2e0363d1..b74b5fb 100644
--- a/opengl/java/android/opengl/EGLExt.java
+++ b/opengl/java/android/opengl/EGLExt.java
@@ -1,5 +1,4 @@
/*
-**
** Copyright 2013, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/opengl/java/android/opengl/EGLSurface.java b/opengl/java/android/opengl/EGLSurface.java
index 65bec4f..c379dc9 100644
--- a/opengl/java/android/opengl/EGLSurface.java
+++ b/opengl/java/android/opengl/EGLSurface.java
@@ -29,7 +29,7 @@
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (!(o instanceof EGLSurface)) return false;
EGLSurface that = (EGLSurface) o;
return getHandle() == that.getHandle();
diff --git a/opengl/java/android/opengl/Matrix.java b/opengl/java/android/opengl/Matrix.java
index 72128ac..ce3f57e 100644
--- a/opengl/java/android/opengl/Matrix.java
+++ b/opengl/java/android/opengl/Matrix.java
@@ -19,24 +19,21 @@
/**
* Matrix math utilities. These methods operate on OpenGL ES format
* matrices and vectors stored in float arrays.
- *
+ * <p>
* Matrices are 4 x 4 column-vector matrices stored in column-major
* order:
* <pre>
* m[offset + 0] m[offset + 4] m[offset + 8] m[offset + 12]
* m[offset + 1] m[offset + 5] m[offset + 9] m[offset + 13]
* m[offset + 2] m[offset + 6] m[offset + 10] m[offset + 14]
- * m[offset + 3] m[offset + 7] m[offset + 11] m[offset + 15]
- * </pre>
+ * m[offset + 3] m[offset + 7] m[offset + 11] m[offset + 15]</pre>
*
- * Vectors are 4 row x 1 column column-vectors stored in order:
+ * Vectors are 4 x 1 column vectors stored in order:
* <pre>
* v[offset + 0]
* v[offset + 1]
* v[offset + 2]
- * v[offset + 3]
- * </pre>
- *
+ * v[offset + 3]</pre>
*/
public class Matrix {
@@ -44,12 +41,18 @@
private final static float[] sTemp = new float[32];
/**
- * Multiply two 4x4 matrices together and store the result in a third 4x4
+ * @deprecated All methods are static, do not instantiate this class.
+ */
+ @Deprecated
+ public Matrix() {}
+
+ /**
+ * Multiplies two 4x4 matrices together and stores the result in a third 4x4
* matrix. In matrix notation: result = lhs x rhs. Due to the way
* matrix multiplication works, the result matrix will have the same
* effect as first multiplying by the rhs matrix, then multiplying by
* the lhs matrix. This is the opposite of what you might expect.
- *
+ * <p>
* The same float array may be passed for result, lhs, and/or rhs. However,
* the result element values are undefined if the result elements overlap
* either the lhs or rhs elements.
@@ -70,9 +73,9 @@
float[] lhs, int lhsOffset, float[] rhs, int rhsOffset);
/**
- * Multiply a 4 element vector by a 4x4 matrix and store the result in a 4
- * element column vector. In matrix notation: result = lhs x rhs
- *
+ * Multiplies a 4 element vector by a 4x4 matrix and stores the result in a
+ * 4-element column vector. In matrix notation: result = lhs x rhs
+ * <p>
* The same float array may be passed for resultVec, lhsMat, and/or rhsVec.
* However, the resultVec element values are undefined if the resultVec
* elements overlap either the lhsMat or rhsVec elements.
@@ -97,12 +100,14 @@
/**
* Transposes a 4 x 4 matrix.
+ * <p>
+ * mTrans and m must not overlap.
*
- * @param mTrans the array that holds the output inverted matrix
- * @param mTransOffset an offset into mInv where the inverted matrix is
+ * @param mTrans the array that holds the output transposed matrix
+ * @param mTransOffset an offset into mTrans where the transposed matrix is
* stored.
* @param m the input array
- * @param mOffset an offset into m where the matrix is stored.
+ * @param mOffset an offset into m where the input matrix is stored.
*/
public static void transposeM(float[] mTrans, int mTransOffset, float[] m,
int mOffset) {
@@ -117,12 +122,14 @@
/**
* Inverts a 4 x 4 matrix.
+ * <p>
+ * mInv and m must not overlap.
*
* @param mInv the array that holds the output inverted matrix
* @param mInvOffset an offset into mInv where the inverted matrix is
* stored.
* @param m the input array
- * @param mOffset an offset into m where the matrix is stored.
+ * @param mOffset an offset into m where the input matrix is stored.
* @return true if the matrix could be inverted, false if it could not.
*/
public static boolean invertM(float[] mInv, int mInvOffset, float[] m,
@@ -301,10 +308,11 @@
/**
- * Define a projection matrix in terms of six clip planes
- * @param m the float array that holds the perspective matrix
+ * Defines a projection matrix in terms of six clip planes.
+ *
+ * @param m the float array that holds the output perspective matrix
* @param offset the offset into float array m where the perspective
- * matrix data is written
+ * matrix data is written
* @param left
* @param right
* @param bottom
@@ -358,11 +366,12 @@
}
/**
- * Define a projection matrix in terms of a field of view angle, an
- * aspect ratio, and z clip planes
+ * Defines a projection matrix in terms of a field of view angle, an
+ * aspect ratio, and z clip planes.
+ *
* @param m the float array that holds the perspective matrix
* @param offset the offset into float array m where the perspective
- * matrix data is written
+ * matrix data is written
* @param fovy field of view in y direction, in degrees
* @param aspect width to height aspect ratio of the viewport
* @param zNear
@@ -395,7 +404,7 @@
}
/**
- * Computes the length of a vector
+ * Computes the length of a vector.
*
* @param x x coordinate of a vector
* @param y y coordinate of a vector
@@ -408,6 +417,7 @@
/**
* Sets matrix m to the identity matrix.
+ *
* @param sm returns the result
* @param smOffset index into sm where the result matrix starts
*/
@@ -421,7 +431,10 @@
}
/**
- * Scales matrix m by x, y, and z, putting the result in sm
+ * Scales matrix m by x, y, and z, putting the result in sm.
+ * <p>
+ * m and sm must not overlap.
+ *
* @param sm returns the result
* @param smOffset index into sm where the result matrix starts
* @param m source matrix
@@ -444,7 +457,8 @@
}
/**
- * Scales matrix m in place by sx, sy, and sz
+ * Scales matrix m in place by sx, sy, and sz.
+ *
* @param m matrix to scale
* @param mOffset index into m where the matrix starts
* @param x scale factor x
@@ -462,7 +476,10 @@
}
/**
- * Translates matrix m by x, y, and z, putting the result in tm
+ * Translates matrix m by x, y, and z, putting the result in tm.
+ * <p>
+ * m and tm must not overlap.
+ *
* @param tm returns the result
* @param tmOffset index into sm where the result matrix starts
* @param m source matrix
@@ -487,6 +504,7 @@
/**
* Translates matrix m by x, y, and z in place.
+ *
* @param m matrix
* @param mOffset index into m where the matrix starts
* @param x translation factor x
@@ -503,15 +521,18 @@
}
/**
- * Rotates matrix m by angle a (in degrees) around the axis (x, y, z)
+ * Rotates matrix m by angle a (in degrees) around the axis (x, y, z).
+ * <p>
+ * m and rm must not overlap.
+ *
* @param rm returns the result
* @param rmOffset index into rm where the result matrix starts
* @param m source matrix
* @param mOffset index into m where the source matrix starts
* @param a angle to rotate in degrees
- * @param x scale factor x
- * @param y scale factor y
- * @param z scale factor z
+ * @param x X axis component
+ * @param y Y axis component
+ * @param z Z axis component
*/
public static void rotateM(float[] rm, int rmOffset,
float[] m, int mOffset,
@@ -524,13 +545,14 @@
/**
* Rotates matrix m in place by angle a (in degrees)
- * around the axis (x, y, z)
+ * around the axis (x, y, z).
+ *
* @param m source matrix
* @param mOffset index into m where the matrix starts
* @param a angle to rotate in degrees
- * @param x scale factor x
- * @param y scale factor y
- * @param z scale factor z
+ * @param x X axis component
+ * @param y Y axis component
+ * @param z Z axis component
*/
public static void rotateM(float[] m, int mOffset,
float a, float x, float y, float z) {
@@ -542,13 +564,18 @@
}
/**
- * Rotates matrix m by angle a (in degrees) around the axis (x, y, z)
+ * Creates a matrix for rotation by angle a (in degrees)
+ * around the axis (x, y, z).
+ * <p>
+ * An optimized path will be used for rotation about a major axis
+ * (e.g. x=1.0f y=0.0f z=0.0f).
+ *
* @param rm returns the result
* @param rmOffset index into rm where the result matrix starts
* @param a angle to rotate in degrees
- * @param x scale factor x
- * @param y scale factor y
- * @param z scale factor z
+ * @param x X axis component
+ * @param y Y axis component
+ * @param z Z axis component
*/
public static void setRotateM(float[] rm, int rmOffset,
float a, float x, float y, float z) {
@@ -608,7 +635,8 @@
}
/**
- * Converts Euler angles to a rotation matrix
+ * Converts Euler angles to a rotation matrix.
+ *
* @param rm returns the result
* @param rmOffset index into rm where the result matrix starts
* @param x angle of rotation, in degrees
@@ -651,7 +679,7 @@
}
/**
- * Define a viewing transformation in terms of an eye point, a center of
+ * Defines a viewing transformation in terms of an eye point, a center of
* view, and an up vector.
*
* @param rm returns the result
diff --git a/packages/BackupRestoreConfirmation/Android.mk b/packages/BackupRestoreConfirmation/Android.mk
index e775b44..b84c07f 100644
--- a/packages/BackupRestoreConfirmation/Android.mk
+++ b/packages/BackupRestoreConfirmation/Android.mk
@@ -23,6 +23,7 @@
LOCAL_PACKAGE_NAME := BackupRestoreConfirmation
LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PACKAGE)
diff --git a/packages/BackupRestoreConfirmation/res/values-sw/strings.xml b/packages/BackupRestoreConfirmation/res/values-sw/strings.xml
index 493c168..eb57aa2 100644
--- a/packages/BackupRestoreConfirmation/res/values-sw/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-sw/strings.xml
@@ -17,9 +17,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"Kuhifadhi kikamilifu"</string>
- <string name="restore_confirm_title" msgid="5469365809567486602">"Kurejeza kamili"</string>
+ <string name="restore_confirm_title" msgid="5469365809567486602">"Kurejesha kila kitu"</string>
<string name="backup_confirm_text" msgid="1878021282758896593">"Chelezo kamili la data iliyounganishwa kwenye eneo kazi la kompyuta limeombwa. Unataka kuruhusu hii kutendeka?"\n\n" Ikiwa hukuomba chelezo mwenyewe, usikubali uendeshaji kuendelea."</string>
- <string name="allow_backup_button_label" msgid="4217228747769644068">"Cheleza data yangu"</string>
+ <string name="allow_backup_button_label" msgid="4217228747769644068">"Hifadhi nakala ya data yangu"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Usicheleze"</string>
<string name="restore_confirm_text" msgid="7499866728030461776">"Kurejesha kamili kwa data nzima kutoka kwa eneo kazi la kompyuta lililounganishwa limeombwa. Unataka kuruhusu hii kutendeka?"\n\n" Ikiwa hukuweza kurejesha upya mwenyewe, usiruhusu uendeshaji huu kuendelea. Hii itaweka upya data yoyote iliyo kwenye kifaa hiki sasa!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Rejesha upya data yangu"</string>
@@ -32,7 +32,7 @@
<string name="restore_enc_password_text" msgid="6140898525580710823">"Ikiwa data iliyorejeshwa upya, tafadhali ingiza nenosiri lililo hapo chini:"</string>
<string name="toast_backup_started" msgid="550354281452756121">"Inaanza kuhifadhi..."</string>
<string name="toast_backup_ended" msgid="3818080769548726424">"Imemaliza kuhifadhi"</string>
- <string name="toast_restore_started" msgid="7881679218971277385">"Inaanza kurejeza..."</string>
- <string name="toast_restore_ended" msgid="1764041639199696132">"Kurejeza kumekamilika"</string>
+ <string name="toast_restore_started" msgid="7881679218971277385">"Inaanza kurejesha..."</string>
+ <string name="toast_restore_ended" msgid="1764041639199696132">"Kurejesha kumekamilika"</string>
<string name="toast_timeout" msgid="5276598587087626877">"Muda wa uendeshaji umeisha"</string>
</resources>
diff --git a/packages/DefaultContainerService/Android.mk b/packages/DefaultContainerService/Android.mk
index 56b8005..9961168 100644
--- a/packages/DefaultContainerService/Android.mk
+++ b/packages/DefaultContainerService/Android.mk
@@ -11,6 +11,8 @@
LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index d3bd197..6343d0a 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -146,6 +146,8 @@
Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " Security: "
+ e.getMessage());
return PackageManager.INSTALL_FAILED_INVALID_APK;
+ } finally {
+ IoUtils.closeQuietly(autoOut);
}
}
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
new file mode 100644
index 0000000..1e45807
--- /dev/null
+++ b/packages/DocumentsUI/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := DocumentsUI
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
new file mode 100644
index 0000000..ae11a8c
--- /dev/null
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.documentsui">
+
+ <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
+
+ <application android:label="@string/app_label">
+ <activity
+ android:name=".DocumentsActivity"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.Holo.Light">
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.OPEN_DOCUMENT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.CREATE_DOCUMENT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ </activity>
+
+ <!-- TODO: remove when we have real clients -->
+ <activity android:name=".TestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dir.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dir.png
new file mode 100644
index 0000000..d02534f
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_dir.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_create_dir.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_create_dir.png
new file mode 100644
index 0000000..6eb31f1
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_create_dir.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_grid.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_grid.png
new file mode 100644
index 0000000..d1326e5
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_grid.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_list.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_list.png
new file mode 100644
index 0000000..e03e345
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_list.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sort.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sort.png
new file mode 100644
index 0000000..680d482
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sort.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/item_background.xml b/packages/DocumentsUI/res/drawable/item_background.xml
new file mode 100644
index 0000000..4fb32fc
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/item_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_activated="true" android:state_pressed="true" android:drawable="@*android:drawable/list_activated_holo" />
+ <item android:state_activated="true" android:drawable="@*android:drawable/list_activated_holo" />
+ <item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_disabled_holo_light" />
+ <item android:state_focused="true" android:state_enabled="false" android:drawable="@*android:drawable/list_selector_disabled_holo_light" />
+ <item android:state_focused="true" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" />
+ <item android:state_focused="false" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" />
+ <item android:state_focused="true" android:drawable="@*android:drawable/list_focused_holo" />
+ <item android:drawable="@android:color/transparent" />
+</selector>
diff --git a/packages/DocumentsUI/res/layout/activity.xml b/packages/DocumentsUI/res/layout/activity.xml
new file mode 100644
index 0000000..f96d459
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/activity.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/directory"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+
+ <FrameLayout
+ android:id="@+id/save"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/fragment_backend.xml b/packages/DocumentsUI/res/layout/fragment_backend.xml
new file mode 100644
index 0000000..2648de2
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/fragment_backend.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <GridView
+ android:id="@+id/grid"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:listSelector="@android:color/transparent"
+ android:paddingTop="?android:attr/listPreferredItemPaddingStart"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart" />
+
+</FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
new file mode 100644
index 0000000..638ac92
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:listSelector="@android:color/transparent" />
+
+ <GridView
+ android:id="@+id/grid"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:listSelector="@android:color/transparent"
+ android:paddingTop="?android:attr/listPreferredItemPaddingStart"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart" />
+
+</FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
new file mode 100644
index 0000000..85c48b1
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:background="@color/chip"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:scaleType="centerInside"
+ android:contentDescription="@null" />
+
+ <EditText
+ android:id="@android:id/title"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:singleLine="true" />
+
+ <Button
+ android:id="@android:id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@string/menu_save"
+ android:textAllCaps="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:padding="8dp" />
+
+</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/item_backend.xml b/packages/DocumentsUI/res/layout/item_backend.xml
new file mode 100644
index 0000000..6ec7566
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/item_backend.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/chip"
+ android:foreground="?android:attr/selectableItemBackground"
+ android:duplicateParentState="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="24dip"
+ android:layout_height="24dip"
+ android:layout_marginEnd="8dp"
+ android:scaleType="centerInside"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart" />
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+</FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
new file mode 100644
index 0000000..caa9db6
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="140dip"
+ android:paddingBottom="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/chip"
+ android:foreground="@drawable/item_background"
+ android:duplicateParentState="true">
+
+ <GridLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:columnCount="1">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_gravity="fill_vertical"
+ android:background="#bbb"
+ android:scaleType="centerInside"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_marginStart="8dip"
+ android:layout_marginEnd="8dip"
+ android:layout_marginTop="8dip"
+ android:layout_marginBottom="8dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart" />
+
+ </GridLayout>
+
+ </FrameLayout>
+
+</FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
new file mode 100644
index 0000000..39e55be
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/item_background"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:columnCount="3">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:layout_rowSpan="2"
+ android:layout_marginEnd="8dip"
+ android:scaleType="centerInside"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="0dip"
+ android:layout_gravity="fill_horizontal"
+ android:layout_marginTop="2dip"
+ android:layout_columnSpan="2"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textAlignment="viewStart" />
+
+ <ImageView
+ android:id="@android:id/icon1"
+ android:layout_width="24dip"
+ android:layout_height="24dip"
+ android:layout_marginEnd="8dip"
+ android:visibility="gone"
+ android:scaleType="centerInside"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_marginTop="2dip"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+</GridLayout>
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
new file mode 100644
index 0000000..bf7c161
--- /dev/null
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/menu_create_dir"
+ android:title="@string/menu_create_dir"
+ android:icon="@drawable/ic_menu_create_dir"
+ android:showAsAction="always" />
+</menu>
diff --git a/packages/DocumentsUI/res/menu/directory.xml b/packages/DocumentsUI/res/menu/directory.xml
new file mode 100644
index 0000000..c1fa228
--- /dev/null
+++ b/packages/DocumentsUI/res/menu/directory.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/menu_grid"
+ android:title="@string/menu_grid"
+ android:icon="@drawable/ic_menu_grid"
+ android:showAsAction="ifRoom" />
+ <item
+ android:id="@+id/menu_list"
+ android:title="@string/menu_list"
+ android:icon="@drawable/ic_menu_list"
+ android:showAsAction="ifRoom" />
+ <item
+ android:id="@+id/menu_sort"
+ android:title="@string/menu_sort"
+ android:icon="@drawable/ic_menu_sort"
+ android:showAsAction="ifRoom" />
+</menu>
diff --git a/packages/DocumentsUI/res/menu/mode_directory.xml b/packages/DocumentsUI/res/menu/mode_directory.xml
new file mode 100644
index 0000000..6b6d7e91
--- /dev/null
+++ b/packages/DocumentsUI/res/menu/mode_directory.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/menu_open"
+ android:title="@string/menu_open"
+ android:showAsAction="always" />
+</menu>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
new file mode 100644
index 0000000..ff3e999
--- /dev/null
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <color name="chip">#ddd</color>
+</resources>
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
new file mode 100644
index 0000000..e5c4138
--- /dev/null
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <dimen name="grid_width">180dp</dimen>
+</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
new file mode 100644
index 0000000..6ae2d12
--- /dev/null
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label">Documents</string>
+
+ <string name="title_open">Open</string>
+ <string name="title_save">Save</string>
+
+ <string name="menu_create_dir">Create folder</string>
+ <string name="menu_grid">Grid view</string>
+ <string name="menu_list">List view</string>
+ <string name="menu_sort">Sort by</string>
+
+ <string name="menu_open">Open</string>
+ <string name="menu_save">Save</string>
+
+ <string name="mode_selected_count"><xliff:g id="count" example="3">%1$d</xliff:g> selected</string>
+
+ <string name="sort_name">Name</string>
+ <string name="sort_date">Date modified</string>
+
+</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BackendFragment.java b/packages/DocumentsUI/src/com/android/documentsui/BackendFragment.java
new file mode 100644
index 0000000..2980e23
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/BackendFragment.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.google.android.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Display all known storage backends.
+ */
+public class BackendFragment extends Fragment {
+
+ // TODO: handle multiple accounts from single backend
+
+ private GridView mGridView;
+ private BackendAdapter mAdapter;
+
+ public static void show(FragmentManager fm) {
+ final BackendFragment fragment = new BackendFragment();
+
+ final FragmentTransaction ft = fm.beginTransaction();
+ ft.replace(R.id.directory, fragment);
+ ft.setBreadCrumbTitle("TOP");
+ ft.commitAllowingStateLoss();
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final Context context = inflater.getContext();
+
+ // Gather known storage providers
+ final List<ProviderInfo> providers = context.getPackageManager()
+ .queryContentProviders(null, -1, PackageManager.GET_META_DATA);
+ final List<ProviderInfo> backends = Lists.newArrayList();
+ for (ProviderInfo info : providers) {
+ if (info.metaData != null
+ && info.metaData.containsKey(DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) {
+ backends.add(info);
+ }
+ }
+
+ final View view = inflater.inflate(R.layout.fragment_backend, container, false);
+
+ mGridView = (GridView) view.findViewById(R.id.grid);
+ mGridView.setOnItemClickListener(mItemListener);
+
+ mAdapter = new BackendAdapter(context, backends);
+ mGridView.setAdapter(mAdapter);
+ mGridView.setNumColumns(GridView.AUTO_FIT);
+
+ return view;
+ }
+
+ private OnItemClickListener mItemListener = new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final ProviderInfo info = mAdapter.getItem(position);
+ ((DocumentsActivity) getActivity()).onBackendPicked(info);
+ }
+ };
+
+ public static class BackendAdapter extends ArrayAdapter<ProviderInfo> {
+ public BackendAdapter(Context context, List<ProviderInfo> list) {
+ super(context, android.R.layout.simple_list_item_1, list);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_backend, parent, false);
+ }
+
+ final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
+ final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
+
+ final PackageManager pm = parent.getContext().getPackageManager();
+ final ProviderInfo info = getItem(position);
+ icon.setImageDrawable(info.loadIcon(pm));
+ text1.setText(info.loadLabel(pm));
+
+ return convertView;
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ColumnAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/ColumnAdapter.java
new file mode 100644
index 0000000..092b5db
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/ColumnAdapter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.database.DataSetObserver;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Adapter that wraps an existing adapter, presenting its contents in multiple
+ * equally-sized horizontal columns.
+ */
+public class ColumnAdapter extends BaseAdapter {
+ private final ListAdapter mWrapped;
+ private final OnItemClickListener mListener;
+
+ private int mColumns = 1;
+
+ public interface OnItemClickListener {
+ public void onItemClick(ListAdapter adapter, int position);
+ }
+
+ public ColumnAdapter(ListAdapter wrapped, OnItemClickListener listener) {
+ mWrapped = Preconditions.checkNotNull(wrapped);
+ mListener = Preconditions.checkNotNull(listener);
+
+ if (!wrapped.areAllItemsEnabled()) {
+ throw new IllegalStateException("All items must be enabled");
+ }
+ if (wrapped.getViewTypeCount() > 1) {
+ throw new IllegalStateException("All items must be identical");
+ }
+ }
+
+ public static void prepare(ListView list) {
+ list.setItemsCanFocus(true);
+ }
+
+ public void setColumns(int columns) {
+ mColumns = columns;
+ notifyDataSetChanged();
+ }
+
+ private View.OnClickListener mItemListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final int position = (Integer) v.getTag();
+ mListener.onItemClick(mWrapped, position);
+ }
+ };
+
+ @Override
+ public int getCount() {
+ return (mWrapped.getCount() + mColumns - 1) / mColumns;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return position;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = new LinearLayout(parent.getContext());
+ }
+
+ final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ 0, LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.weight = 1f / mColumns;
+
+ final LinearLayout row = (LinearLayout) convertView;
+ final int first = position * mColumns;
+ final int last = mWrapped.getCount() - 1;
+
+ for (int i = 0; i < mColumns; i++) {
+ View convertItem = null;
+ if (i < row.getChildCount()) {
+ convertItem = row.getChildAt(i);
+ }
+
+ final int pos = first + i;
+ final int validPos = Math.min(pos, last);
+ final View item = mWrapped.getView(validPos, convertItem, row);
+ item.setTag(validPos);
+ item.setOnClickListener(mItemListener);
+ item.setFocusable(true);
+
+ if (pos == validPos) {
+ item.setVisibility(View.VISIBLE);
+ } else {
+ item.setVisibility(View.INVISIBLE);
+ }
+
+ if (convertItem == null) {
+ row.addView(item, params);
+ }
+ }
+
+ return convertView;
+ }
+
+ @Override
+ public void registerDataSetObserver(DataSetObserver observer) {
+ super.registerDataSetObserver(observer);
+ mWrapped.registerDataSetObserver(observer);
+ }
+
+ @Override
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ super.unregisterDataSetObserver(observer);
+ mWrapped.unregisterDataSetObserver(observer);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
new file mode 100644
index 0000000..531eaf3
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Loader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.DocumentColumns;
+import android.text.format.DateUtils;
+import android.util.SparseBooleanArray;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.MultiChoiceModeListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.CursorAdapter;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.android.documentsui.DocumentsActivity.DisplayState;
+import com.android.documentsui.DocumentsActivity.Document;
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+
+/**
+ * Display the documents inside a single directory.
+ */
+public class DirectoryFragment extends Fragment {
+
+ // TODO: show storage backend in item views when requested
+
+ private static final String TAG_SORT = "sort";
+
+ private ListView mListView;
+ private GridView mGridView;
+
+ private AbsListView mCurrentView;
+
+ private DocumentsAdapter mAdapter;
+ private LoaderCallbacks<Cursor> mCallbacks;
+
+ private int mFlags;
+
+ private static final String EXTRA_URI = "uri";
+
+ private static final int LOADER_DOCUMENTS = 2;
+
+ public static void show(FragmentManager fm, Uri uri, String displayName) {
+ final Bundle args = new Bundle();
+ args.putParcelable(EXTRA_URI, uri);
+
+ final DirectoryFragment fragment = new DirectoryFragment();
+ fragment.setArguments(args);
+
+ final FragmentTransaction ft = fm.beginTransaction();
+ ft.replace(R.id.directory, fragment);
+ ft.addToBackStack(displayName);
+ ft.setBreadCrumbTitle(displayName);
+ ft.commitAllowingStateLoss();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final Context context = inflater.getContext();
+
+ final View view = inflater.inflate(R.layout.fragment_directory, container, false);
+
+ mListView = (ListView) view.findViewById(R.id.list);
+ mListView.setOnItemClickListener(mItemListener);
+ mListView.setMultiChoiceModeListener(mMultiListener);
+
+ mGridView = (GridView) view.findViewById(R.id.grid);
+ mGridView.setOnItemClickListener(mItemListener);
+ mGridView.setMultiChoiceModeListener(mMultiListener);
+
+ mAdapter = new DocumentsAdapter(context);
+ updateMode();
+
+ // TODO: migrate flags query to loader
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ mFlags = getDocumentFlags(context, uri);
+
+ mCallbacks = new LoaderCallbacks<Cursor>() {
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ final DisplayState state = getDisplayState(DirectoryFragment.this);
+ final String sortOrder;
+ if (state.sortBy == DisplayState.SORT_BY_NAME) {
+ sortOrder = DocumentColumns.DISPLAY_NAME + " ASC";
+ } else if (state.sortBy == DisplayState.SORT_BY_DATE) {
+ sortOrder = DocumentColumns.LAST_MODIFIED + " DESC";
+ } else {
+ sortOrder = null;
+ }
+
+ final Uri contentsUri = DocumentsContract.buildContentsUri(uri);
+ return new CursorLoader(context, contentsUri, null, null, null, sortOrder);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mAdapter.swapCursor(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mAdapter.swapCursor(null);
+ }
+ };
+
+ return view;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
+
+ // TODO: clean up tracking of current directory
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ ((DocumentsActivity) getActivity()).onDirectoryChanged(uri, mFlags);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ getLoaderManager().destroyLoader(LOADER_DOCUMENTS);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.directory, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ final DisplayState state = getDisplayState(this);
+ menu.findItem(R.id.menu_grid).setVisible(state.mode != DisplayState.MODE_GRID);
+ menu.findItem(R.id.menu_list).setVisible(state.mode != DisplayState.MODE_LIST);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final DisplayState state = getDisplayState(this);
+ final int id = item.getItemId();
+ if (id == R.id.menu_grid) {
+ state.mode = DisplayState.MODE_GRID;
+ updateMode();
+ getFragmentManager().invalidateOptionsMenu();
+ return true;
+ } else if (id == R.id.menu_list) {
+ state.mode = DisplayState.MODE_LIST;
+ updateMode();
+ getFragmentManager().invalidateOptionsMenu();
+ return true;
+ } else if (id == R.id.menu_sort) {
+ SortFragment.show(this);
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateMode() {
+ final DisplayState state = getDisplayState(this);
+
+ mListView.setVisibility(state.mode == DisplayState.MODE_LIST ? View.VISIBLE : View.GONE);
+ mGridView.setVisibility(state.mode == DisplayState.MODE_GRID ? View.VISIBLE : View.GONE);
+
+ final int choiceMode;
+ if (state.allowMultiple) {
+ choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
+ } else {
+ choiceMode = ListView.CHOICE_MODE_NONE;
+ }
+
+ if (state.mode == DisplayState.MODE_GRID) {
+ mListView.setAdapter(null);
+ mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
+ mGridView.setAdapter(mAdapter);
+ mGridView.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.grid_width));
+ mGridView.setNumColumns(GridView.AUTO_FIT);
+ mGridView.setChoiceMode(choiceMode);
+ mCurrentView = mGridView;
+ } else if (state.mode == DisplayState.MODE_LIST) {
+ mGridView.setAdapter(null);
+ mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
+ mListView.setAdapter(mAdapter);
+ mListView.setChoiceMode(choiceMode);
+ mCurrentView = mListView;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ private void updateSortBy() {
+ getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
+ }
+
+ private OnItemClickListener mItemListener = new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final Cursor cursor = (Cursor) mAdapter.getItem(position);
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ final Document doc = Document.fromCursor(uri.getAuthority(), cursor);
+ ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
+ }
+ };
+
+ private MultiChoiceModeListener mMultiListener = new MultiChoiceModeListener() {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ if (item.getItemId() == R.id.menu_open) {
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions();
+ final ArrayList<Document> docs = Lists.newArrayList();
+
+ final int size = checked.size();
+ for (int i = 0; i < size; i++) {
+ if (checked.valueAt(i)) {
+ final Cursor cursor = (Cursor) mAdapter.getItem(checked.keyAt(i));
+ docs.add(Document.fromCursor(uri.getAuthority(), cursor));
+ }
+ }
+
+ ((DocumentsActivity) getActivity()).onDocumentsPicked(docs);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ // ignored
+ }
+
+ @Override
+ public void onItemCheckedStateChanged(
+ ActionMode mode, int position, long id, boolean checked) {
+ if (checked) {
+ final Cursor cursor = (Cursor) mAdapter.getItem(position);
+ final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
+
+ // Directories cannot be checked
+ if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
+ mCurrentView.setItemChecked(position, false);
+ }
+ }
+
+ mode.setTitle(getResources()
+ .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
+ }
+ };
+
+ private static DisplayState getDisplayState(Fragment fragment) {
+ return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
+ }
+
+ private class DocumentsAdapter extends CursorAdapter {
+ public DocumentsAdapter(Context context) {
+ super(context, null, false);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ final DisplayState state = getDisplayState(DirectoryFragment.this);
+ if (state.mode == DisplayState.MODE_LIST) {
+ return inflater.inflate(R.layout.item_doc_list, parent, false);
+ } else if (state.mode == DisplayState.MODE_GRID) {
+ return inflater.inflate(R.layout.item_doc_grid, parent, false);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final TextView title = (TextView) view.findViewById(android.R.id.title);
+ final TextView summary = (TextView) view.findViewById(android.R.id.summary);
+ final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
+
+ final String guid = getCursorString(cursor, DocumentColumns.GUID);
+ final String displayName = getCursorString(cursor, DocumentColumns.DISPLAY_NAME);
+ final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
+ final long lastModified = getCursorLong(cursor, DocumentColumns.LAST_MODIFIED);
+ final int flags = getCursorInt(cursor, DocumentColumns.FLAGS);
+
+ if ((flags & DocumentsContract.FLAG_SUPPORTS_THUMBNAIL) != 0) {
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ final Uri childUri = DocumentsContract.buildDocumentUri(uri.getAuthority(), guid);
+ icon.setImageURI(childUri);
+ } else {
+ icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(context, mimeType));
+ }
+
+ title.setText(displayName);
+ if (summary != null) {
+ summary.setText(DateUtils.getRelativeTimeSpanString(lastModified));
+ }
+ }
+ }
+
+ public static class SortFragment extends DialogFragment {
+ public static void show(DirectoryFragment parent) {
+ if (!parent.isAdded()) return;
+
+ final SortFragment dialog = new SortFragment();
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_SORT);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final DisplayState state = getDisplayState(this);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.menu_sort);
+ builder.setSingleChoiceItems(new CharSequence[] {
+ getText(R.string.sort_name),
+ getText(R.string.sort_date),
+ }, state.sortBy, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ state.sortBy = which;
+ ((DirectoryFragment) getTargetFragment()).updateSortBy();
+ dismiss();
+ }
+ });
+
+ return builder.create();
+ }
+ }
+
+ private static int getDocumentFlags(Context context, Uri uri) {
+ final Cursor cursor = context.getContentResolver().query(uri, new String[] {
+ DocumentColumns.FLAGS }, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ return getCursorInt(cursor, DocumentColumns.FLAGS);
+ } else {
+ return 0;
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public static String getCursorString(Cursor cursor, String columnName) {
+ return cursor.getString(cursor.getColumnIndex(columnName));
+ }
+
+ public static long getCursorLong(Cursor cursor, String columnName) {
+ return cursor.getLong(cursor.getColumnIndex(columnName));
+ }
+
+ public static int getCursorInt(Cursor cursor, String columnName) {
+ return cursor.getInt(cursor.getColumnIndex(columnName));
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
new file mode 100644
index 0000000..c45d2b4
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.DirectoryFragment.getCursorString;
+
+import android.app.ActionBar;
+import android.app.ActionBar.OnNavigationListener;
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.app.FragmentManager.BackStackEntry;
+import android.app.FragmentManager.OnBackStackChangedListener;
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.DocumentColumns;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class DocumentsActivity extends Activity {
+ private static final String TAG = "Documents";
+
+ // TODO: fragment to show recently opened documents
+ // TODO: pull actionbar icon from current backend
+
+ private static final int ACTION_OPEN = 1;
+ private static final int ACTION_CREATE = 2;
+
+ private int mAction;
+ private String[] mAcceptMimes;
+
+ private final DisplayState mDisplayState = new DisplayState();
+
+ private boolean mIgnoreNextNavigation;
+
+ private Uri mCurrentDir;
+ private boolean mCurrentSupportsCreate;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+ if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
+ mAction = ACTION_OPEN;
+ mDisplayState.allowMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
+ } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
+ mAction = ACTION_CREATE;
+ mDisplayState.allowMultiple = false;
+ }
+
+ if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
+ mAcceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
+ } else {
+ mAcceptMimes = new String[] { intent.getType() };
+ }
+
+ if (mimeMatches("image/*", mAcceptMimes)) {
+ mDisplayState.mode = DisplayState.MODE_GRID;
+ } else {
+ mDisplayState.mode = DisplayState.MODE_LIST;
+ }
+
+ setResult(Activity.RESULT_CANCELED);
+ setContentView(R.layout.activity);
+
+ getFragmentManager().addOnBackStackChangedListener(mStackListener);
+ BackendFragment.show(getFragmentManager());
+
+ updateActionBar();
+
+ if (mAction == ACTION_CREATE) {
+ final String mimeType = getIntent().getType();
+ final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
+ SaveFragment.show(getFragmentManager(), mimeType, title);
+ }
+ }
+
+ public void updateActionBar() {
+ final FragmentManager fm = getFragmentManager();
+ final ActionBar actionBar = getActionBar();
+
+ if (fm.getBackStackEntryCount() > 0) {
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+ actionBar.setDisplayShowHomeEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setTitle(null);
+ actionBar.setListNavigationCallbacks(mStackAdapter, mNavigationListener);
+ actionBar.setSelectedNavigationItem(mStackAdapter.getCount() - 1);
+ mIgnoreNextNavigation = true;
+
+ } else {
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+ actionBar.setDisplayShowHomeEnabled(false);
+ actionBar.setDisplayHomeAsUpEnabled(false);
+
+ if (mAction == ACTION_OPEN) {
+ actionBar.setTitle(R.string.title_open);
+ } else if (mAction == ACTION_CREATE) {
+ actionBar.setTitle(R.string.title_save);
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.activity, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+
+ final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
+ createDir.setVisible(mAction == ACTION_CREATE);
+ createDir.setEnabled(mCurrentSupportsCreate);
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int id = item.getItemId();
+ if (id == android.R.id.home) {
+ getFragmentManager().popBackStack();
+ updateActionBar();
+ } else if (id == R.id.menu_create_dir) {
+ // TODO: show dialog to create directory
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private OnBackStackChangedListener mStackListener = new OnBackStackChangedListener() {
+ @Override
+ public void onBackStackChanged() {
+ updateActionBar();
+ }
+ };
+
+ private BaseAdapter mStackAdapter = new BaseAdapter() {
+ @Override
+ public int getCount() {
+ return getFragmentManager().getBackStackEntryCount();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return getFragmentManager().getBackStackEntryAt(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getFragmentManager().getBackStackEntryAt(position).getId();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
+ }
+
+ final BackStackEntry entry = getFragmentManager().getBackStackEntryAt(position);
+ final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
+ text1.setText(entry.getBreadCrumbTitle());
+
+ return convertView;
+ }
+ };
+
+ private OnNavigationListener mNavigationListener = new OnNavigationListener() {
+ @Override
+ public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+ if (mIgnoreNextNavigation) {
+ mIgnoreNextNavigation = false;
+ return false;
+ }
+
+ getFragmentManager().popBackStack((int) itemId, 0);
+ return true;
+ }
+ };
+
+ public DisplayState getDisplayState() {
+ return mDisplayState;
+ }
+
+ public void onDirectoryChanged(Uri uri, int flags) {
+ mCurrentDir = uri;
+ mCurrentSupportsCreate = (flags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0;
+
+ if (mAction == ACTION_CREATE) {
+ final FragmentManager fm = getFragmentManager();
+ SaveFragment.get(fm).setSaveEnabled(mCurrentSupportsCreate);
+ }
+
+ invalidateOptionsMenu();
+ }
+
+ public void onBackendPicked(ProviderInfo info) {
+ final Uri uri = DocumentsContract.buildDocumentUri(
+ info.authority, DocumentsContract.ROOT_GUID);
+ final CharSequence displayName = info.loadLabel(getPackageManager());
+ DirectoryFragment.show(getFragmentManager(), uri, displayName.toString());
+ }
+
+ public void onDocumentPicked(Document doc) {
+ final FragmentManager fm = getFragmentManager();
+ if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(doc.mimeType)) {
+ // Nested directory picked, recurse using new fragment
+ DirectoryFragment.show(fm, doc.uri, doc.displayName);
+ } else if (mAction == ACTION_OPEN) {
+ // Explicit file picked, return
+ onFinished(doc.uri);
+ } else if (mAction == ACTION_CREATE) {
+ // Overwrite current filename
+ SaveFragment.get(fm).setDisplayName(doc.displayName);
+ }
+ }
+
+ public void onDocumentsPicked(List<Document> docs) {
+ final int size = docs.size();
+ final Uri[] uris = new Uri[size];
+ for (int i = 0; i < size; i++) {
+ uris[i] = docs.get(i).uri;
+ }
+ onFinished(uris);
+ }
+
+ public void onSaveRequested(String mimeType, String displayName) {
+ // TODO: create file, confirming before overwriting
+ final Uri uri = null;
+ onFinished(uri);
+ }
+
+ private void onFinished(Uri... uris) {
+ Log.d(TAG, "onFinished() " + Arrays.toString(uris));
+
+ final Intent intent = new Intent();
+ if (uris.length == 1) {
+ intent.setData(uris[0]);
+ } else if (uris.length > 1) {
+ final ContentResolver resolver = getContentResolver();
+ final ClipData clipData = new ClipData(null, mAcceptMimes, new ClipData.Item(uris[0]));
+ for (int i = 1; i < uris.length; i++) {
+ clipData.addItem(new ClipData.Item(uris[i]));
+ }
+ intent.setClipData(clipData);
+ }
+
+ intent.addFlags(
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+ if (mAction == ACTION_CREATE) {
+ intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ }
+
+ public static class DisplayState {
+ public int mode;
+ public int sortBy;
+ public boolean allowMultiple;
+
+ public static final int MODE_LIST = 0;
+ public static final int MODE_GRID = 1;
+
+ public static final int SORT_BY_NAME = 0;
+ public static final int SORT_BY_DATE = 1;
+ }
+
+ public static class Document {
+ public Uri uri;
+ public String mimeType;
+ public String displayName;
+
+ public static Document fromCursor(String authority, Cursor cursor) {
+ final Document doc = new Document();
+ final String guid = getCursorString(cursor, DocumentColumns.GUID);
+ doc.uri = DocumentsContract.buildDocumentUri(authority, guid);
+ doc.mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
+ doc.displayName = getCursorString(cursor, DocumentColumns.DISPLAY_NAME);
+ return doc;
+ }
+ }
+
+ public static boolean mimeMatches(String filter, String[] tests) {
+ for (String test : tests) {
+ if (mimeMatches(filter, test)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean mimeMatches(String filter, String test) {
+ if (filter.equals(test)) {
+ return true;
+ } else if ("*/*".equals(filter)) {
+ return true;
+ } else if (filter.endsWith("/*")) {
+ return filter.regionMatches(0, test, 0, filter.indexOf('/'));
+ } else {
+ return false;
+ }
+ }
+
+ public static Drawable resolveDocumentIcon(Context context, String mimeType) {
+ // TODO: allow backends to provide custom MIME icons
+ if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
+ return context.getResources().getDrawable(R.drawable.ic_dir);
+ } else {
+ final PackageManager pm = context.getPackageManager();
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setType(mimeType);
+
+ final ResolveInfo info = pm.resolveActivity(
+ intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (info != null) {
+ return info.loadIcon(pm);
+ } else {
+ return null;
+ }
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
new file mode 100644
index 0000000..19a1d2a
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+
+/**
+ * Display document title editor and save button.
+ */
+public class SaveFragment extends Fragment {
+ public static final String TAG = "SaveFragment";
+
+ private EditText mDisplayName;
+ private Button mSave;
+
+ private static final String EXTRA_MIME_TYPE = "mime_type";
+ private static final String EXTRA_DISPLAY_NAME = "display_name";
+
+ public static void show(FragmentManager fm, String mimeType, String displayName) {
+ final Bundle args = new Bundle();
+ args.putString(EXTRA_MIME_TYPE, mimeType);
+ args.putString(EXTRA_DISPLAY_NAME, displayName);
+
+ final SaveFragment fragment = new SaveFragment();
+ fragment.setArguments(args);
+
+ final FragmentTransaction ft = fm.beginTransaction();
+ ft.replace(R.id.save, fragment, TAG);
+ ft.commitAllowingStateLoss();
+ }
+
+ public static SaveFragment get(FragmentManager fm) {
+ return (SaveFragment) fm.findFragmentByTag(TAG);
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final Context context = inflater.getContext();
+
+ final View view = inflater.inflate(R.layout.fragment_save, container, false);
+
+ final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
+ icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(
+ context, getArguments().getString(EXTRA_MIME_TYPE)));
+
+ mDisplayName = (EditText) view.findViewById(android.R.id.title);
+ mDisplayName.setText(getArguments().getString(EXTRA_DISPLAY_NAME));
+
+ mSave = (Button) view.findViewById(android.R.id.button1);
+ mSave.setOnClickListener(mSaveListener);
+ mSave.setEnabled(false);
+
+ return view;
+ }
+
+ private View.OnClickListener mSaveListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final String mimeType = getArguments().getString(EXTRA_MIME_TYPE);
+ final String displayName = getArguments().getString(EXTRA_DISPLAY_NAME);
+ ((DocumentsActivity) getActivity()).onSaveRequested(mimeType, displayName);
+ }
+ };
+
+ public void setDisplayName(String displayName) {
+ getArguments().putString(EXTRA_DISPLAY_NAME, displayName);
+ mDisplayName.setText(displayName);
+ }
+
+ public void setSaveEnabled(boolean enabled) {
+ mSave.setEnabled(enabled);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
new file mode 100644
index 0000000..b15d123
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class TestActivity extends Activity {
+ private TextView mResult;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Context context = this;
+
+ final LinearLayout view = new LinearLayout(context);
+ view.setOrientation(LinearLayout.VERTICAL);
+
+ final CheckBox multiple = new CheckBox(context);
+ multiple.setText("ALLOW_MULTIPLE");
+ view.addView(multiple);
+
+ Button button;
+ button = new Button(context);
+ button.setText("OPEN_DOC */*");
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("*/*");
+ if (multiple.isChecked()) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ }
+ startActivityForResult(intent, 42);
+ }
+ });
+ view.addView(button);
+
+ button = new Button(context);
+ button.setText("OPEN_DOC image/*");
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("image/*");
+ if (multiple.isChecked()) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ }
+ startActivityForResult(intent, 42);
+ }
+ });
+ view.addView(button);
+
+ button = new Button(context);
+ button.setText("OPEN_DOC text/plain, application/msword");
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("*/*");
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
+ "text/plain", "application/msword" });
+ if (multiple.isChecked()) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ }
+ startActivityForResult(intent, 42);
+ }
+ });
+ view.addView(button);
+
+ button = new Button(context);
+ button.setText("CREATE_DOC text/plain");
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt");
+ startActivityForResult(intent, 42);
+ }
+ });
+ view.addView(button);
+
+ mResult = new TextView(context);
+ view.addView(mResult);
+
+ setContentView(view);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mResult.setText("resultCode=" + resultCode + ", data=" + String.valueOf(data));
+ }
+}
diff --git a/packages/ExternalStorageProvider/Android.mk b/packages/ExternalStorageProvider/Android.mk
new file mode 100644
index 0000000..db825ff4
--- /dev/null
+++ b/packages/ExternalStorageProvider/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := ExternalStorageProvider
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_PACKAGE)
diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml
new file mode 100644
index 0000000..37dc5b1
--- /dev/null
+++ b/packages/ExternalStorageProvider/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.externalstorage">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application android:label="@string/app_label">
+ <provider
+ android:name=".ExternalStorageProvider"
+ android:authorities="com.android.externalstorage"
+ android:grantUriPermissions="true"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_DOCUMENTS">
+ <meta-data
+ android:name="android.content.DOCUMENT_PROVIDER"
+ android:value="true" />
+ </provider>
+ </application>
+</manifest>
diff --git a/packages/ExternalStorageProvider/res/values/strings.xml b/packages/ExternalStorageProvider/res/values/strings.xml
new file mode 100644
index 0000000..4374cfc
--- /dev/null
+++ b/packages/ExternalStorageProvider/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="app_label">External Storage</string>
+</resources>
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
new file mode 100644
index 0000000..f75e3bd
--- /dev/null
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.externalstorage;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.provider.BaseColumns;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.DocumentColumns;
+import android.webkit.MimeTypeMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.google.android.collect.Lists;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+public class ExternalStorageProvider extends ContentProvider {
+ private static final String TAG = "ExternalStorage";
+
+ private static final String AUTHORITY = "com.android.externalstorage";
+
+ // TODO: support searching
+ // TODO: support multiple storage devices
+ // TODO: persist GUIDs across launches
+
+ private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ private static final int URI_DOCS_ID = 1;
+ private static final int URI_DOCS_ID_CONTENTS = 2;
+ private static final int URI_SEARCH = 3;
+
+ static {
+ sMatcher.addURI(AUTHORITY, "docs/#", URI_DOCS_ID);
+ sMatcher.addURI(AUTHORITY, "docs/#/contents", URI_DOCS_ID_CONTENTS);
+ sMatcher.addURI(AUTHORITY, "search", URI_SEARCH);
+ }
+
+ @GuardedBy("mFiles")
+ private ArrayList<File> mFiles = Lists.newArrayList();
+
+ @Override
+ public boolean onCreate() {
+ mFiles.clear();
+ mFiles.add(Environment.getExternalStorageDirectory());
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+
+ // TODO: support custom projections
+ projection = new String[] {
+ BaseColumns._ID,
+ DocumentColumns.DISPLAY_NAME, DocumentColumns.SIZE, DocumentColumns.GUID,
+ DocumentColumns.MIME_TYPE, DocumentColumns.LAST_MODIFIED, DocumentColumns.FLAGS };
+
+ final MatrixCursor cursor = new MatrixCursor(projection);
+ switch (sMatcher.match(uri)) {
+ case URI_DOCS_ID: {
+ final int id = Integer.parseInt(uri.getPathSegments().get(1));
+ synchronized (mFiles) {
+ includeFileLocked(cursor, id);
+ }
+ break;
+ }
+ case URI_DOCS_ID_CONTENTS: {
+ final int parentId = Integer.parseInt(uri.getPathSegments().get(1));
+ synchronized (mFiles) {
+ final File parent = mFiles.get(parentId);
+ for (File file : parent.listFiles()) {
+ final int id = findOrCreateFileLocked(file);
+ includeFileLocked(cursor, id);
+ }
+ }
+ break;
+ }
+ default: {
+ cursor.close();
+ throw new UnsupportedOperationException("Unsupported Uri " + uri);
+ }
+ }
+
+ return cursor;
+ }
+
+ private int findOrCreateFileLocked(File file) {
+ int id = mFiles.indexOf(file);
+ if (id == -1) {
+ id = mFiles.size();
+ mFiles.add(file);
+ }
+ return id;
+ }
+
+ private void includeFileLocked(MatrixCursor cursor, int id) {
+ final File file = mFiles.get(id);
+ int flags = 0;
+
+ if (file.isDirectory() && file.canWrite()) {
+ flags |= DocumentsContract.FLAG_SUPPORTS_CREATE;
+ }
+ if (file.canWrite()) {
+ flags |= DocumentsContract.FLAG_SUPPORTS_RENAME;
+ }
+
+ final String mimeType = getTypeLocked(id);
+ if (mimeType.startsWith("image/")) {
+ flags |= DocumentsContract.FLAG_SUPPORTS_THUMBNAIL;
+ }
+
+ cursor.addRow(new Object[] {
+ id, file.getName(), file.length(), id, mimeType, file.lastModified(), flags });
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ switch (sMatcher.match(uri)) {
+ case URI_DOCS_ID: {
+ final int id = Integer.parseInt(uri.getPathSegments().get(1));
+ synchronized (mFiles) {
+ return getTypeLocked(id);
+ }
+ }
+ default: {
+ throw new UnsupportedOperationException("Unsupported Uri " + uri);
+ }
+ }
+ }
+
+ private String getTypeLocked(int id) {
+ final File file = mFiles.get(id);
+
+ if (file.isDirectory()) {
+ return DocumentsContract.MIME_TYPE_DIRECTORY;
+ }
+
+ final int lastDot = file.getName().lastIndexOf('.');
+ if (lastDot >= 0) {
+ final String extension = file.getName().substring(lastDot + 1);
+ final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ if (mime != null) {
+ return mime;
+ }
+ }
+
+ return "application/octet-stream";
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ switch (sMatcher.match(uri)) {
+ case URI_DOCS_ID: {
+ final int id = Integer.parseInt(uri.getPathSegments().get(1));
+ synchronized (mFiles) {
+ final File file = mFiles.get(id);
+ // TODO: turn into thumbnail
+ return ParcelFileDescriptor.open(file, ContentResolver.modeToMode(uri, mode));
+ }
+ }
+ default: {
+ throw new UnsupportedOperationException("Unsupported Uri " + uri);
+ }
+ }
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/packages/FusedLocation/Android.mk b/packages/FusedLocation/Android.mk
index 318782f..7406eaf4 100644
--- a/packages/FusedLocation/Android.mk
+++ b/packages/FusedLocation/Android.mk
@@ -23,5 +23,6 @@
LOCAL_PACKAGE_NAME := FusedLocation
LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PACKAGE)
diff --git a/packages/InputDevices/Android.mk b/packages/InputDevices/Android.mk
index 37f2428..095655c 100644
--- a/packages/InputDevices/Android.mk
+++ b/packages/InputDevices/Android.mk
@@ -23,6 +23,7 @@
LOCAL_PACKAGE_NAME := InputDevices
LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PACKAGE)
diff --git a/packages/Keyguard/Android.mk b/packages/Keyguard/Android.mk
new file mode 100644
index 0000000..f6f441d
--- /dev/null
+++ b/packages/Keyguard/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files)
+
+LOCAL_JAVA_LIBRARIES := services
+
+LOCAL_PACKAGE_NAME := Keyguard
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+include $(BUILD_PACKAGE)
+
+#include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/Keyguard/AndroidManifest.xml b/packages/Keyguard/AndroidManifest.xml
new file mode 100644
index 0000000..7d77c48
--- /dev/null
+++ b/packages/Keyguard/AndroidManifest.xml
@@ -0,0 +1,49 @@
+<?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.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.keyguard"
+ android:sharedUserId="android.uid.systemui"
+ coreApp="true">
+ <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17"/>
+ <uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.STATUS_BAR" />
+ <uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.BIND_APPWIDGET" />
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
+ <uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" />
+ <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
+
+ <application android:label="@string/app_name"
+ android:process="com.android.systemui"
+ android:persistent="true" >
+
+ <service android:name=".KeyguardService"
+ android:exported="true" />
+
+ </application>
+</manifest>
diff --git a/packages/Keyguard/NOTICE b/packages/Keyguard/NOTICE
new file mode 100644
index 0000000..33ff961
--- /dev/null
+++ b/packages/Keyguard/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2012, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/packages/Keyguard/proguard.flags b/packages/Keyguard/proguard.flags
new file mode 100644
index 0000000..fb74b64
--- /dev/null
+++ b/packages/Keyguard/proguard.flags
@@ -0,0 +1,27 @@
+-keep public class * {
+ public void setBackgroundAlpha(float);
+ public float getBackgroundAlpha();
+ public void setContentAlpha(float);
+ public float getContentAlpha();
+ public void setAlpha(float);
+ public float getAlpha();
+ public void setAlpha(int);
+ public int getAlpha();
+ public void setRotationX(float);
+ public float getRotationX();
+ public void setRotationY(float);
+ public float getRotationY();
+ public void setPivotX(float);
+ public float getPivotX();
+ public void setPivotY(float);
+ public float getPivotY();
+ public void setScaleX(float);
+ public float getScaleX();
+ public void setScaleY(float);
+ public float getScaleY();
+ public void setTranslationX(float);
+ public float getTranslationX();
+ public void setTranslationY(float);
+ public float getTranslationY();
+}
+
diff --git a/core/res/res/anim/keyguard_action_assist_enter.xml b/packages/Keyguard/res/anim/keyguard_action_assist_enter.xml
similarity index 100%
rename from core/res/res/anim/keyguard_action_assist_enter.xml
rename to packages/Keyguard/res/anim/keyguard_action_assist_enter.xml
diff --git a/core/res/res/anim/keyguard_action_assist_exit.xml b/packages/Keyguard/res/anim/keyguard_action_assist_exit.xml
similarity index 100%
rename from core/res/res/anim/keyguard_action_assist_exit.xml
rename to packages/Keyguard/res/anim/keyguard_action_assist_exit.xml
diff --git a/core/res/res/anim/keyguard_security_animate_in.xml b/packages/Keyguard/res/anim/keyguard_security_animate_in.xml
similarity index 100%
rename from core/res/res/anim/keyguard_security_animate_in.xml
rename to packages/Keyguard/res/anim/keyguard_security_animate_in.xml
diff --git a/core/res/res/anim/keyguard_security_animate_out.xml b/packages/Keyguard/res/anim/keyguard_security_animate_out.xml
similarity index 100%
rename from core/res/res/anim/keyguard_security_animate_out.xml
rename to packages/Keyguard/res/anim/keyguard_security_animate_out.xml
diff --git a/packages/Keyguard/res/anim/keyguard_security_fade_in.xml b/packages/Keyguard/res/anim/keyguard_security_fade_in.xml
new file mode 100644
index 0000000..c66c604
--- /dev/null
+++ b/packages/Keyguard/res/anim/keyguard_security_fade_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/decelerate_quad"
+ android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@integer/kg_security_fade_duration" />
+
+
diff --git a/packages/Keyguard/res/anim/keyguard_security_fade_out.xml b/packages/Keyguard/res/anim/keyguard_security_fade_out.xml
new file mode 100644
index 0000000..6465b35
--- /dev/null
+++ b/packages/Keyguard/res/anim/keyguard_security_fade_out.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/accelerate_quad"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@integer/kg_security_fade_duration"
+/>
diff --git a/packages/Keyguard/res/anim/lock_screen_enter.xml b/packages/Keyguard/res/anim/lock_screen_enter.xml
new file mode 100644
index 0000000..4344cf9
--- /dev/null
+++ b/packages/Keyguard/res/anim/lock_screen_enter.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/accelerate_cubic">
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@integer/config_activityDefaultDur" />
+</set>
diff --git a/packages/Keyguard/res/anim/lock_screen_exit.xml b/packages/Keyguard/res/anim/lock_screen_exit.xml
new file mode 100644
index 0000000..c75b3cc
--- /dev/null
+++ b/packages/Keyguard/res/anim/lock_screen_exit.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="top"
+ android:shareInterpolator="false">
+ <scale
+ android:fromXScale="1.0" android:toXScale="1.10"
+ android:fromYScale="1.0" android:toYScale="1.10"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:fillEnabled="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/accelerate_quint"
+ android:duration="@android:integer/config_shortAnimTime" />
+ <alpha
+ android:fromAlpha="1.0" android:toAlpha="0"
+ android:fillEnabled="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/accelerate_quad"
+ android:duration="@android:integer/config_shortAnimTime"/>
+</set>
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_action_assist_generic_activated.png b/packages/Keyguard/res/drawable-hdpi/ic_action_assist_generic_activated.png
new file mode 100644
index 0000000..c0e2098
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/ic_action_assist_generic_activated.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_action_assist_generic_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_action_assist_generic_normal.png
new file mode 100644
index 0000000..a852e2c
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/ic_action_assist_generic_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_facial_backup.png b/packages/Keyguard/res/drawable-hdpi/ic_facial_backup.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_facial_backup.png
rename to packages/Keyguard/res/drawable-hdpi/ic_facial_backup.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_input_delete.png b/packages/Keyguard/res/drawable-hdpi/ic_input_delete.png
new file mode 100644
index 0000000..5d638bd
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/ic_input_delete.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_alarm.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_alarm.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_alarm.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_alarm.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_camera_activated.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_camera_activated.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_camera_activated.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_camera_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_camera_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_camera_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_camera_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_camera_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_emergencycall_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_emergencycall_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_emergencycall_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_emergencycall_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_emergencycall_pressed.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_emergencycall_pressed.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_emergencycall_pressed.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_emergencycall_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_glowdot.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_glowdot.png
new file mode 100644
index 0000000..983c45e
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_glowdot.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_google_activated.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_google_activated.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_google_activated.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_google_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_google_focused.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_google_focused.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_google_focused.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_google_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_google_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_google_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_google_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_google_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_handle_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_handle_pressed.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_handle_pressed.png
new file mode 100644
index 0000000..58a5f16
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_ime.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_ime.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_ime.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_ime.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_lock_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_lock_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_lock_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_lock_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_lock_pressed.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_lock_pressed.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_lock_pressed.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_lock_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_player_background.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_player_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_search_activated.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_search_activated.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_search_activated.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_search_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_search_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_search_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_search_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_search_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_silent_activated.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_silent_activated.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_silent_activated.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_silent_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_silent_focused.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_silent_focused.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_silent_focused.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_silent_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_silent_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_silent_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_silent_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_silent_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_sim.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_sim.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_sim.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_sim.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_soundon_activated.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_soundon_activated.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_soundon_activated.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_soundon_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_soundon_focused.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_soundon_focused.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_soundon_focused.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_soundon_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_soundon_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_soundon_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_soundon_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_soundon_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_unlock_activated.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_unlock_activated.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_unlock_activated.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_unlock_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_unlock_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_unlock_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_lockscreen_unlock_normal.png
rename to packages/Keyguard/res/drawable-hdpi/ic_lockscreen_unlock_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_media_next.png b/packages/Keyguard/res/drawable-hdpi/ic_media_next.png
new file mode 100644
index 0000000..6e27b81
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/ic_media_next.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_media_play.png b/packages/Keyguard/res/drawable-hdpi/ic_media_play.png
new file mode 100644
index 0000000..2746d17
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/ic_media_play.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_media_previous.png b/packages/Keyguard/res/drawable-hdpi/ic_media_previous.png
new file mode 100644
index 0000000..85b3766
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/ic_media_previous.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/intro_bg.png b/packages/Keyguard/res/drawable-hdpi/intro_bg.png
similarity index 100%
rename from core/res/res/drawable-hdpi/intro_bg.png
rename to packages/Keyguard/res/drawable-hdpi/intro_bg.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/kg_add_widget.png b/packages/Keyguard/res/drawable-hdpi/kg_add_widget.png
new file mode 100644
index 0000000..7456705
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/kg_add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_add_widget_disabled.png b/packages/Keyguard/res/drawable-hdpi/kg_add_widget_disabled.png
similarity index 100%
rename from core/res/res/drawable-hdpi/kg_add_widget_disabled.png
rename to packages/Keyguard/res/drawable-hdpi/kg_add_widget_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_add_widget_pressed.png b/packages/Keyguard/res/drawable-hdpi/kg_add_widget_pressed.png
similarity index 100%
rename from core/res/res/drawable-hdpi/kg_add_widget_pressed.png
rename to packages/Keyguard/res/drawable-hdpi/kg_add_widget_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png b/packages/Keyguard/res/drawable-hdpi/kg_bouncer_bg_white.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png
rename to packages/Keyguard/res/drawable-hdpi/kg_bouncer_bg_white.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_grip.9.png b/packages/Keyguard/res/drawable-hdpi/kg_security_grip.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/kg_security_grip.9.png
rename to packages/Keyguard/res/drawable-hdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_lock.png b/packages/Keyguard/res/drawable-hdpi/kg_security_lock.png
similarity index 100%
rename from core/res/res/drawable-hdpi/kg_security_lock.png
rename to packages/Keyguard/res/drawable-hdpi/kg_security_lock.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/kg_security_lock_focused.png b/packages/Keyguard/res/drawable-hdpi/kg_security_lock_focused.png
new file mode 100644
index 0000000..9a82799
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/kg_security_lock_focused.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/kg_security_lock_normal.png b/packages/Keyguard/res/drawable-hdpi/kg_security_lock_normal.png
new file mode 100644
index 0000000..d608707
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/kg_security_lock_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/kg_security_lock_pressed.png b/packages/Keyguard/res/drawable-hdpi/kg_security_lock_pressed.png
new file mode 100644
index 0000000..7ca995d
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/kg_security_lock_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_widget_bg_padded.9.png b/packages/Keyguard/res/drawable-hdpi/kg_widget_bg_padded.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/kg_widget_bg_padded.9.png
rename to packages/Keyguard/res/drawable-hdpi/kg_widget_bg_padded.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_widget_delete_drop_target.png b/packages/Keyguard/res/drawable-hdpi/kg_widget_delete_drop_target.png
similarity index 100%
rename from core/res/res/drawable-hdpi/kg_widget_delete_drop_target.png
rename to packages/Keyguard/res/drawable-hdpi/kg_widget_delete_drop_target.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/lockscreen_protection_pattern.png b/packages/Keyguard/res/drawable-hdpi/lockscreen_protection_pattern.png
similarity index 100%
rename from core/res/res/drawable-hdpi/lockscreen_protection_pattern.png
rename to packages/Keyguard/res/drawable-hdpi/lockscreen_protection_pattern.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png b/packages/Keyguard/res/drawable-hdpi/sym_keyboard_return_holo.png
similarity index 100%
rename from core/res/res/drawable-hdpi/sym_keyboard_return_holo.png
rename to packages/Keyguard/res/drawable-hdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-ldpi/ic_input_delete.png b/packages/Keyguard/res/drawable-ldpi/ic_input_delete.png
new file mode 100644
index 0000000..d7eff17
--- /dev/null
+++ b/packages/Keyguard/res/drawable-ldpi/ic_input_delete.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-ldpi/ic_media_next.png b/packages/Keyguard/res/drawable-ldpi/ic_media_next.png
new file mode 100644
index 0000000..99927fd
--- /dev/null
+++ b/packages/Keyguard/res/drawable-ldpi/ic_media_next.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-ldpi/ic_media_play.png b/packages/Keyguard/res/drawable-ldpi/ic_media_play.png
new file mode 100644
index 0000000..e7c1972
--- /dev/null
+++ b/packages/Keyguard/res/drawable-ldpi/ic_media_play.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-ldpi/ic_media_previous.png b/packages/Keyguard/res/drawable-ldpi/ic_media_previous.png
new file mode 100644
index 0000000..df04322
--- /dev/null
+++ b/packages/Keyguard/res/drawable-ldpi/ic_media_previous.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_action_assist_generic_activated.png b/packages/Keyguard/res/drawable-mdpi/ic_action_assist_generic_activated.png
new file mode 100644
index 0000000..f88f7e1
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/ic_action_assist_generic_activated.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_action_assist_generic_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_action_assist_generic_normal.png
new file mode 100644
index 0000000..7426994
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/ic_action_assist_generic_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_facial_backup.png b/packages/Keyguard/res/drawable-mdpi/ic_facial_backup.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_facial_backup.png
rename to packages/Keyguard/res/drawable-mdpi/ic_facial_backup.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_input_delete.png b/packages/Keyguard/res/drawable-mdpi/ic_input_delete.png
new file mode 100644
index 0000000..47c8708
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/ic_input_delete.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_alarm.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_alarm.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_alarm.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_alarm.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_camera_activated.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_camera_activated.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_camera_activated.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_camera_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_camera_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_camera_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_camera_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_camera_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_emergencycall_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_emergencycall_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_emergencycall_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_emergencycall_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_emergencycall_pressed.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_emergencycall_pressed.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_emergencycall_pressed.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_emergencycall_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_glowdot.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_glowdot.png
new file mode 100644
index 0000000..056c3f17
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_glowdot.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_google_activated.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_google_activated.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_google_activated.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_google_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_google_focused.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_google_focused.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_google_focused.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_google_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_google_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_google_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_google_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_google_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_handle_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_handle_pressed.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_handle_pressed.png
new file mode 100644
index 0000000..0187a02
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_ime.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_ime.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_ime.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_ime.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_lock_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_lock_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_lock_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_lock_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_lock_pressed.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_lock_pressed.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_lock_pressed.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_lock_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_player_background.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_player_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_search_activated.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_search_activated.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_search_activated.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_search_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_search_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_search_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_search_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_search_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_silent_activated.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_silent_activated.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_silent_activated.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_silent_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_silent_focused.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_silent_focused.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_silent_focused.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_silent_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_silent_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_silent_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_silent_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_silent_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_sim.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_sim.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_sim.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_sim.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_soundon_activated.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_soundon_activated.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_soundon_activated.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_soundon_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_soundon_focused.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_soundon_focused.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_soundon_focused.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_soundon_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_soundon_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_soundon_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_soundon_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_soundon_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_unlock_activated.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_unlock_activated.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_unlock_activated.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_unlock_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_unlock_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_unlock_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_lockscreen_unlock_normal.png
rename to packages/Keyguard/res/drawable-mdpi/ic_lockscreen_unlock_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_media_next.png b/packages/Keyguard/res/drawable-mdpi/ic_media_next.png
new file mode 100644
index 0000000..fcd73d9
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/ic_media_next.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_media_play.png b/packages/Keyguard/res/drawable-mdpi/ic_media_play.png
new file mode 100644
index 0000000..7966bbc
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/ic_media_play.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_media_previous.png b/packages/Keyguard/res/drawable-mdpi/ic_media_previous.png
new file mode 100644
index 0000000..b653d05
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/ic_media_previous.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/intro_bg.png b/packages/Keyguard/res/drawable-mdpi/intro_bg.png
similarity index 100%
rename from core/res/res/drawable-mdpi/intro_bg.png
rename to packages/Keyguard/res/drawable-mdpi/intro_bg.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/kg_add_widget.png b/packages/Keyguard/res/drawable-mdpi/kg_add_widget.png
new file mode 100644
index 0000000..1cab0d9
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/kg_add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_add_widget_disabled.png b/packages/Keyguard/res/drawable-mdpi/kg_add_widget_disabled.png
similarity index 100%
rename from core/res/res/drawable-mdpi/kg_add_widget_disabled.png
rename to packages/Keyguard/res/drawable-mdpi/kg_add_widget_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_add_widget_pressed.png b/packages/Keyguard/res/drawable-mdpi/kg_add_widget_pressed.png
similarity index 100%
rename from core/res/res/drawable-mdpi/kg_add_widget_pressed.png
rename to packages/Keyguard/res/drawable-mdpi/kg_add_widget_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png b/packages/Keyguard/res/drawable-mdpi/kg_bouncer_bg_white.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png
rename to packages/Keyguard/res/drawable-mdpi/kg_bouncer_bg_white.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_grip.9.png b/packages/Keyguard/res/drawable-mdpi/kg_security_grip.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/kg_security_grip.9.png
rename to packages/Keyguard/res/drawable-mdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_lock.png b/packages/Keyguard/res/drawable-mdpi/kg_security_lock.png
similarity index 100%
rename from core/res/res/drawable-mdpi/kg_security_lock.png
rename to packages/Keyguard/res/drawable-mdpi/kg_security_lock.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/kg_security_lock_focused.png b/packages/Keyguard/res/drawable-mdpi/kg_security_lock_focused.png
new file mode 100644
index 0000000..c3608f9
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/kg_security_lock_focused.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/kg_security_lock_normal.png b/packages/Keyguard/res/drawable-mdpi/kg_security_lock_normal.png
new file mode 100644
index 0000000..7957c79
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/kg_security_lock_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/kg_security_lock_pressed.png b/packages/Keyguard/res/drawable-mdpi/kg_security_lock_pressed.png
new file mode 100644
index 0000000..41715f5
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/kg_security_lock_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_widget_bg_padded.9.png b/packages/Keyguard/res/drawable-mdpi/kg_widget_bg_padded.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/kg_widget_bg_padded.9.png
rename to packages/Keyguard/res/drawable-mdpi/kg_widget_bg_padded.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_widget_delete_drop_target.png b/packages/Keyguard/res/drawable-mdpi/kg_widget_delete_drop_target.png
similarity index 100%
rename from core/res/res/drawable-mdpi/kg_widget_delete_drop_target.png
rename to packages/Keyguard/res/drawable-mdpi/kg_widget_delete_drop_target.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/lockscreen_protection_pattern.png b/packages/Keyguard/res/drawable-mdpi/lockscreen_protection_pattern.png
similarity index 100%
rename from core/res/res/drawable-mdpi/lockscreen_protection_pattern.png
rename to packages/Keyguard/res/drawable-mdpi/lockscreen_protection_pattern.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png b/packages/Keyguard/res/drawable-mdpi/sym_keyboard_return_holo.png
similarity index 100%
rename from core/res/res/drawable-mdpi/sym_keyboard_return_holo.png
rename to packages/Keyguard/res/drawable-mdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-hdpi/ic_lockscreen_handle_normal.png b/packages/Keyguard/res/drawable-sw600dp-hdpi/ic_lockscreen_handle_normal.png
similarity index 100%
rename from core/res/res/drawable-sw600dp-hdpi/ic_lockscreen_handle_normal.png
rename to packages/Keyguard/res/drawable-sw600dp-hdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-sw600dp-hdpi/ic_lockscreen_handle_pressed.png b/packages/Keyguard/res/drawable-sw600dp-hdpi/ic_lockscreen_handle_pressed.png
new file mode 100644
index 0000000..728fc67
--- /dev/null
+++ b/packages/Keyguard/res/drawable-sw600dp-hdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-mdpi/ic_lockscreen_handle_normal.png b/packages/Keyguard/res/drawable-sw600dp-mdpi/ic_lockscreen_handle_normal.png
similarity index 100%
rename from core/res/res/drawable-sw600dp-mdpi/ic_lockscreen_handle_normal.png
rename to packages/Keyguard/res/drawable-sw600dp-mdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-sw600dp-mdpi/ic_lockscreen_handle_pressed.png b/packages/Keyguard/res/drawable-sw600dp-mdpi/ic_lockscreen_handle_pressed.png
new file mode 100644
index 0000000..c7da024
--- /dev/null
+++ b/packages/Keyguard/res/drawable-sw600dp-mdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-xhdpi/ic_lockscreen_handle_normal.png b/packages/Keyguard/res/drawable-sw600dp-xhdpi/ic_lockscreen_handle_normal.png
similarity index 100%
rename from core/res/res/drawable-sw600dp-xhdpi/ic_lockscreen_handle_normal.png
rename to packages/Keyguard/res/drawable-sw600dp-xhdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-sw600dp-xhdpi/ic_lockscreen_handle_pressed.png b/packages/Keyguard/res/drawable-sw600dp-xhdpi/ic_lockscreen_handle_pressed.png
new file mode 100644
index 0000000..534c10b
--- /dev/null
+++ b/packages/Keyguard/res/drawable-sw600dp-xhdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_action_assist_generic_activated.png b/packages/Keyguard/res/drawable-xhdpi/ic_action_assist_generic_activated.png
new file mode 100644
index 0000000..500b157
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/ic_action_assist_generic_activated.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_action_assist_generic_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_action_assist_generic_normal.png
new file mode 100644
index 0000000..d0e4cf3
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/ic_action_assist_generic_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_facial_backup.png b/packages/Keyguard/res/drawable-xhdpi/ic_facial_backup.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_facial_backup.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_facial_backup.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_input_delete.png b/packages/Keyguard/res/drawable-xhdpi/ic_input_delete.png
new file mode 100644
index 0000000..8b822d9
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/ic_input_delete.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_alarm.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_alarm.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_alarm.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_alarm.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_camera_activated.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_camera_activated.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_camera_activated.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_camera_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_camera_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_camera_normal.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_camera_normal.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_camera_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_emergencycall_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_emergencycall_normal.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_emergencycall_normal.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_emergencycall_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_emergencycall_pressed.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_emergencycall_pressed.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_emergencycall_pressed.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_emergencycall_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_glowdot.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_glowdot.png
new file mode 100644
index 0000000..cbd039a
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_glowdot.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_google_activated.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_google_activated.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_google_activated.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_google_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_google_focused.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_google_focused.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_google_focused.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_google_focused.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_google_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_google_normal.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_google_normal.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_google_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_handle_normal.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png
new file mode 100644
index 0000000..2d28009
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_ime.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_ime.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_ime.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_ime.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_lock_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_lock_normal.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_lock_normal.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_lock_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_lock_pressed.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_lock_pressed.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_lock_pressed.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_lock_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_player_background.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_player_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_silent_activated.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_silent_activated.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_silent_activated.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_silent_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_silent_focused.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_silent_focused.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_silent_focused.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_silent_focused.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_silent_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_silent_normal.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_silent_normal.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_silent_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_sim.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_sim.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_sim.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_sim.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_soundon_activated.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_soundon_activated.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_soundon_activated.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_soundon_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_soundon_focused.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_soundon_focused.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_soundon_focused.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_soundon_focused.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_soundon_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_soundon_normal.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_soundon_normal.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_soundon_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_unlock_activated.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_unlock_activated.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_unlock_activated.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_unlock_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_unlock_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_unlock_normal.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_lockscreen_unlock_normal.png
rename to packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_unlock_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_media_next.png b/packages/Keyguard/res/drawable-xhdpi/ic_media_next.png
new file mode 100644
index 0000000..4def965
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/ic_media_next.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_media_play.png b/packages/Keyguard/res/drawable-xhdpi/ic_media_play.png
new file mode 100644
index 0000000..ccfef18
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/ic_media_play.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_media_previous.png b/packages/Keyguard/res/drawable-xhdpi/ic_media_previous.png
new file mode 100644
index 0000000..c4472ae
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/ic_media_previous.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/intro_bg.png b/packages/Keyguard/res/drawable-xhdpi/intro_bg.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/intro_bg.png
rename to packages/Keyguard/res/drawable-xhdpi/intro_bg.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/kg_add_widget.png b/packages/Keyguard/res/drawable-xhdpi/kg_add_widget.png
new file mode 100644
index 0000000..d71905f
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/kg_add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_add_widget_disabled.png b/packages/Keyguard/res/drawable-xhdpi/kg_add_widget_disabled.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/kg_add_widget_disabled.png
rename to packages/Keyguard/res/drawable-xhdpi/kg_add_widget_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_add_widget_pressed.png b/packages/Keyguard/res/drawable-xhdpi/kg_add_widget_pressed.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/kg_add_widget_pressed.png
rename to packages/Keyguard/res/drawable-xhdpi/kg_add_widget_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png b/packages/Keyguard/res/drawable-xhdpi/kg_bouncer_bg_white.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png
rename to packages/Keyguard/res/drawable-xhdpi/kg_bouncer_bg_white.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_grip.9.png b/packages/Keyguard/res/drawable-xhdpi/kg_security_grip.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/kg_security_grip.9.png
rename to packages/Keyguard/res/drawable-xhdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_lock.png b/packages/Keyguard/res/drawable-xhdpi/kg_security_lock.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/kg_security_lock.png
rename to packages/Keyguard/res/drawable-xhdpi/kg_security_lock.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/kg_security_lock_focused.png b/packages/Keyguard/res/drawable-xhdpi/kg_security_lock_focused.png
new file mode 100644
index 0000000..db22016
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/kg_security_lock_focused.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/kg_security_lock_normal.png b/packages/Keyguard/res/drawable-xhdpi/kg_security_lock_normal.png
new file mode 100644
index 0000000..17ebb5f
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/kg_security_lock_normal.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/kg_security_lock_pressed.png b/packages/Keyguard/res/drawable-xhdpi/kg_security_lock_pressed.png
new file mode 100644
index 0000000..186b6ff
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/kg_security_lock_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_widget_bg_padded.9.png b/packages/Keyguard/res/drawable-xhdpi/kg_widget_bg_padded.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/kg_widget_bg_padded.9.png
rename to packages/Keyguard/res/drawable-xhdpi/kg_widget_bg_padded.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_widget_delete_drop_target.png b/packages/Keyguard/res/drawable-xhdpi/kg_widget_delete_drop_target.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/kg_widget_delete_drop_target.png
rename to packages/Keyguard/res/drawable-xhdpi/kg_widget_delete_drop_target.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/lockscreen_protection_pattern.png b/packages/Keyguard/res/drawable-xhdpi/lockscreen_protection_pattern.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/lockscreen_protection_pattern.png
rename to packages/Keyguard/res/drawable-xhdpi/lockscreen_protection_pattern.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png b/packages/Keyguard/res/drawable-xhdpi/sym_keyboard_return_holo.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png
rename to packages/Keyguard/res/drawable-xhdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable/ic_action_assist_generic.xml b/packages/Keyguard/res/drawable/ic_action_assist_generic.xml
new file mode 100644
index 0000000..60f5d5d
--- /dev/null
+++ b/packages/Keyguard/res/drawable/ic_action_assist_generic.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:state_enabled="true"
+ android:state_active="false"
+ android:state_focused="false"
+ android:drawable="@drawable/ic_action_assist_generic_normal" />
+
+ <item
+ android:state_enabled="true"
+ android:state_active="true"
+ android:state_focused="false"
+ android:drawable="@drawable/ic_action_assist_generic_activated" />
+
+ <item
+ android:state_enabled="true"
+ android:state_active="false"
+ android:state_focused="true"
+ android:drawable="@drawable/ic_action_assist_generic_activated" />
+
+</selector>
diff --git a/core/res/res/drawable/ic_lockscreen_camera.xml b/packages/Keyguard/res/drawable/ic_lockscreen_camera.xml
similarity index 100%
rename from core/res/res/drawable/ic_lockscreen_camera.xml
rename to packages/Keyguard/res/drawable/ic_lockscreen_camera.xml
diff --git a/core/res/res/drawable/ic_lockscreen_handle.xml b/packages/Keyguard/res/drawable/ic_lockscreen_handle.xml
similarity index 100%
rename from core/res/res/drawable/ic_lockscreen_handle.xml
rename to packages/Keyguard/res/drawable/ic_lockscreen_handle.xml
diff --git a/packages/Keyguard/res/drawable/ic_lockscreen_outerring.xml b/packages/Keyguard/res/drawable/ic_lockscreen_outerring.xml
new file mode 100644
index 0000000..75bea70
--- /dev/null
+++ b/packages/Keyguard/res/drawable/ic_lockscreen_outerring.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval"
+ >
+ <size android:height="@dimen/keyguard_lockscreen_outerring_diameter"
+ android:width="@dimen/keyguard_lockscreen_outerring_diameter" />
+ <solid android:color="#00000000" />
+ <stroke android:color="#1affffff" android:width="2dp" />
+</shape>
diff --git a/core/res/res/drawable/ic_lockscreen_silent.xml b/packages/Keyguard/res/drawable/ic_lockscreen_silent.xml
similarity index 100%
rename from core/res/res/drawable/ic_lockscreen_silent.xml
rename to packages/Keyguard/res/drawable/ic_lockscreen_silent.xml
diff --git a/core/res/res/drawable/ic_lockscreen_soundon.xml b/packages/Keyguard/res/drawable/ic_lockscreen_soundon.xml
similarity index 100%
rename from core/res/res/drawable/ic_lockscreen_soundon.xml
rename to packages/Keyguard/res/drawable/ic_lockscreen_soundon.xml
diff --git a/core/res/res/drawable/ic_lockscreen_unlock.xml b/packages/Keyguard/res/drawable/ic_lockscreen_unlock.xml
similarity index 100%
rename from core/res/res/drawable/ic_lockscreen_unlock.xml
rename to packages/Keyguard/res/drawable/ic_lockscreen_unlock.xml
diff --git a/core/res/res/drawable/ic_lockscreen_unlock_phantom.xml b/packages/Keyguard/res/drawable/ic_lockscreen_unlock_phantom.xml
similarity index 100%
rename from core/res/res/drawable/ic_lockscreen_unlock_phantom.xml
rename to packages/Keyguard/res/drawable/ic_lockscreen_unlock_phantom.xml
diff --git a/core/res/res/drawable/keyguard_add_widget_button.xml b/packages/Keyguard/res/drawable/keyguard_add_widget_button.xml
similarity index 100%
rename from core/res/res/drawable/keyguard_add_widget_button.xml
rename to packages/Keyguard/res/drawable/keyguard_add_widget_button.xml
diff --git a/core/res/res/drawable/keyguard_expand_challenge_handle.xml b/packages/Keyguard/res/drawable/keyguard_expand_challenge_handle.xml
similarity index 100%
rename from core/res/res/drawable/keyguard_expand_challenge_handle.xml
rename to packages/Keyguard/res/drawable/keyguard_expand_challenge_handle.xml
diff --git a/core/res/res/drawable/lockscreen_emergency_button.xml b/packages/Keyguard/res/drawable/lockscreen_emergency_button.xml
similarity index 100%
rename from core/res/res/drawable/lockscreen_emergency_button.xml
rename to packages/Keyguard/res/drawable/lockscreen_emergency_button.xml
diff --git a/core/res/res/drawable/lockscreen_forgot_password_button.xml b/packages/Keyguard/res/drawable/lockscreen_forgot_password_button.xml
similarity index 100%
rename from core/res/res/drawable/lockscreen_forgot_password_button.xml
rename to packages/Keyguard/res/drawable/lockscreen_forgot_password_button.xml
diff --git a/packages/Keyguard/res/layout-land/keyguard_host_view.xml b/packages/Keyguard/res/layout-land/keyguard_host_view.xml
new file mode 100644
index 0000000..eeb9ee7
--- /dev/null
+++ b/packages/Keyguard/res/layout-land/keyguard_host_view.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the host view that generally contains two sub views: the widget view
+ and the security view. -->
+<com.android.keyguard.KeyguardHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_host_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <com.android.keyguard.MultiPaneChallengeLayout
+ android:id="@+id/multi_pane_challenge"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipChildren="false">
+
+ <include layout="@layout/keyguard_widget_remove_drop_target"
+ android:id="@+id/keyguard_widget_pager_delete_target"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center_horizontal"
+ androidprv:layout_childType="pageDeleteDropTarget" />
+
+ <include layout="@layout/keyguard_widget_pager"
+ android:id="@+id/app_widget_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_centerWithinArea="0.55"
+ androidprv:layout_childType="widget"
+ androidprv:layout_maxWidth="480dp"
+ androidprv:layout_maxHeight="480dp" />
+ <include layout="@layout/keyguard_multi_user_selector"/>
+
+ <View android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_childType="scrim"
+ android:background="#99000000" />
+
+ <com.android.keyguard.KeyguardSecurityContainer
+ android:id="@+id/keyguard_security_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ androidprv:layout_childType="challenge"
+ androidprv:layout_centerWithinArea="0.55">
+ <com.android.keyguard.KeyguardSecurityViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingLeft="@dimen/keyguard_security_view_margin"
+ android:paddingTop="@dimen/keyguard_security_view_margin"
+ android:paddingRight="@dimen/keyguard_security_view_margin"
+ android:paddingBottom="@dimen/keyguard_security_view_margin"
+ android:gravity="center">
+ </com.android.keyguard.KeyguardSecurityViewFlipper>
+ </com.android.keyguard.KeyguardSecurityContainer>
+
+ </com.android.keyguard.MultiPaneChallengeLayout>
+</com.android.keyguard.KeyguardHostView>
+
diff --git a/core/res/res/layout-land/keyguard_status_area.xml b/packages/Keyguard/res/layout-land/keyguard_status_area.xml
similarity index 100%
rename from core/res/res/layout-land/keyguard_status_area.xml
rename to packages/Keyguard/res/layout-land/keyguard_status_area.xml
diff --git a/packages/Keyguard/res/layout-land/keyguard_widget_pager.xml b/packages/Keyguard/res/layout-land/keyguard_widget_pager.xml
new file mode 100644
index 0000000..da31065
--- /dev/null
+++ b/packages/Keyguard/res/layout-land/keyguard_widget_pager.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the selector widget that allows the user to select an action. -->
+<com.android.keyguard.KeyguardWidgetCarousel
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingLeft="25dp"
+ android:paddingRight="25dp"
+ android:paddingTop="25dp"
+ android:paddingBottom="25dp"
+ android:clipToPadding="false"
+ androidprv:pageSpacing="10dp">
+</com.android.keyguard.KeyguardWidgetCarousel>
diff --git a/packages/Keyguard/res/layout-port/keyguard_host_view.xml b/packages/Keyguard/res/layout-port/keyguard_host_view.xml
new file mode 100644
index 0000000..8498dcf
--- /dev/null
+++ b/packages/Keyguard/res/layout-port/keyguard_host_view.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the host view that generally contains two sub views: the widget view
+ and the security view. -->
+<com.android.keyguard.KeyguardHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_host_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <com.android.keyguard.SlidingChallengeLayout
+ android:id="@+id/sliding_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ androidprv:layout_childType="pageDeleteDropTarget">
+ <include layout="@layout/keyguard_widget_remove_drop_target"
+ android:id="@+id/keyguard_widget_pager_delete_target"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center_horizontal" />
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_childType="widgets">
+ <include layout="@layout/keyguard_widget_pager"
+ android:id="@+id/app_widget_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <View android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_childType="scrim"
+ android:background="#99000000" />
+
+ <com.android.keyguard.KeyguardSecurityContainer
+ android:id="@+id/keyguard_security_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ androidprv:layout_childType="challenge"
+ android:padding="0dp"
+ android:gravity="bottom|center_horizontal">
+ <com.android.keyguard.KeyguardSecurityViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingTop="@dimen/keyguard_security_view_margin"
+ android:gravity="center">
+ </com.android.keyguard.KeyguardSecurityViewFlipper>
+ </com.android.keyguard.KeyguardSecurityContainer>
+
+ <ImageButton
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/kg_widget_pager_bottom_padding"
+ androidprv:layout_childType="expandChallengeHandle"
+ android:focusable="true"
+ android:background="@null"
+ android:src="@drawable/keyguard_expand_challenge_handle"
+ android:scaleType="center"
+ android:contentDescription="@string/keyguard_accessibility_expand_lock_area" />
+
+ </com.android.keyguard.SlidingChallengeLayout>
+</com.android.keyguard.KeyguardHostView>
+
diff --git a/core/res/res/layout-port/keyguard_status_area.xml b/packages/Keyguard/res/layout-port/keyguard_status_area.xml
similarity index 100%
rename from core/res/res/layout-port/keyguard_status_area.xml
rename to packages/Keyguard/res/layout-port/keyguard_status_area.xml
diff --git a/packages/Keyguard/res/layout-port/keyguard_widget_pager.xml b/packages/Keyguard/res/layout-port/keyguard_widget_pager.xml
new file mode 100644
index 0000000..d0a07ca
--- /dev/null
+++ b/packages/Keyguard/res/layout-port/keyguard_widget_pager.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the selector widget that allows the user to select an action. -->
+<com.android.keyguard.KeyguardWidgetPager
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/app_widget_container"
+ android:paddingLeft="25dp"
+ android:paddingRight="25dp"
+ android:paddingTop="25dp"
+ android:paddingBottom="@dimen/kg_widget_pager_bottom_padding"
+ android:clipToPadding="false"
+ androidprv:pageSpacing="10dp">
+</com.android.keyguard.KeyguardWidgetPager>
diff --git a/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml b/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml
new file mode 100644
index 0000000..77bc9b5
--- /dev/null
+++ b/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the host view that generally contains two sub views: the widget view
+ and the security view. -->
+<com.android.keyguard.KeyguardHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_host_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <com.android.keyguard.MultiPaneChallengeLayout
+ android:id="@+id/multi_pane_challenge"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:orientation="vertical">
+
+ <include layout="@layout/keyguard_widget_remove_drop_target"
+ android:id="@+id/keyguard_widget_pager_delete_target"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center_horizontal"
+ androidprv:layout_childType="pageDeleteDropTarget" />
+
+ <include layout="@layout/keyguard_widget_pager"
+ android:id="@+id/app_widget_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_centerWithinArea="0.5"
+ androidprv:layout_childType="widget"
+ androidprv:layout_maxWidth="480dp"
+ androidprv:layout_maxHeight="480dp" />
+
+ <include layout="@layout/keyguard_multi_user_selector"/>
+
+ <View android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_childType="scrim"
+ android:background="#99000000" />
+
+ <com.android.keyguard.KeyguardSecurityContainer
+ android:id="@+id/keyguard_security_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ androidprv:layout_centerWithinArea="0.5"
+ androidprv:layout_childType="challenge"
+ android:layout_gravity="center_horizontal|bottom">
+ <com.android.keyguard.KeyguardSecurityViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingLeft="@dimen/keyguard_security_view_margin"
+ android:paddingTop="@dimen/keyguard_security_view_margin"
+ android:paddingRight="@dimen/keyguard_security_view_margin"
+ android:paddingBottom="@dimen/keyguard_security_view_margin"
+ android:gravity="center">
+ </com.android.keyguard.KeyguardSecurityViewFlipper>
+ </com.android.keyguard.KeyguardSecurityContainer>
+
+ </com.android.keyguard.MultiPaneChallengeLayout>
+</com.android.keyguard.KeyguardHostView>
diff --git a/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml b/packages/Keyguard/res/layout-sw600dp/keyguard_glow_pad_container.xml
similarity index 100%
rename from core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
rename to packages/Keyguard/res/layout-sw600dp/keyguard_glow_pad_container.xml
diff --git a/packages/Keyguard/res/layout/keyguard_account_view.xml b/packages/Keyguard/res/layout/keyguard_account_view.xml
new file mode 100644
index 0000000..766effa
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_account_view.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<com.android.keyguard.KeyguardAccountView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_account_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:orientation="vertical">
+
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+
+ <EditText
+ android:id="@+id/login"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="7dip"
+ android:layout_marginEnd="7dip"
+ android:layout_alignParentTop="true"
+ android:hint="@string/kg_login_username_hint"
+ android:inputType="textEmailAddress"
+ />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/login"
+ android:layout_toLeftOf="@+id/ok"
+ android:layout_marginTop="15dip"
+ android:layout_marginStart="7dip"
+ android:layout_marginEnd="7dip"
+ android:inputType="textPassword"
+ android:hint="@string/kg_login_password_hint"
+ android:nextFocusRight="@+id/ok"
+ android:nextFocusDown="@+id/ok"
+ />
+
+ <!-- ok below password, aligned to right of screen -->
+ <Button
+ android:id="@+id/ok"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="7dip"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/login"
+ android:text="@string/kg_login_submit_button"
+ />
+
+ </RelativeLayout>
+
+ <!-- no room for ECA on this screen right now
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal" />
+ -->
+
+</com.android.keyguard.KeyguardAccountView>
diff --git a/packages/Keyguard/res/layout/keyguard_add_widget.xml b/packages/Keyguard/res/layout/keyguard_add_widget.xml
new file mode 100644
index 0000000..01b616c
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_add_widget.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<com.android.keyguard.KeyguardWidgetFrame
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_add_widget"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@string/keyguard_accessibility_widget_empty_slot"
+ >
+ <ImageView
+ android:id="@+id/keyguard_add_widget_view"
+ android:clickable="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:padding="24dp"
+ android:src="@drawable/keyguard_add_widget_button"
+ android:contentDescription="@string/keyguard_accessibility_add_widget"/>
+ </FrameLayout>
+</com.android.keyguard.KeyguardWidgetFrame>
diff --git a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
new file mode 100644
index 0000000..de673ec
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
+<com.android.keyguard.EmergencyCarrierArea
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:layout_gravity="center_horizontal"
+ android:layout_alignParentBottom="true"
+ android:clickable="true">
+
+ <com.android.keyguard.CarrierText
+ android:id="@+id/carrier_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@dimen/kg_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-10dip"
+ style="?android:attr/buttonBarStyle"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:weightSum="2">
+
+ <com.android.keyguard.EmergencyButton
+ android:id="@+id/emergency_call_button"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableLeft="@drawable/lockscreen_emergency_button"
+ android:text="@string/kg_emergency_call_label"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@dimen/kg_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary"
+ android:drawablePadding="8dip" />
+
+ <Button android:id="@+id/forgot_password_button"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableLeft="@drawable/lockscreen_forgot_password_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="@dimen/kg_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:drawablePadding="8dip"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+</com.android.keyguard.EmergencyCarrierArea>
diff --git a/core/res/res/layout/keyguard_emergency_carrier_area_empty.xml b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area_empty.xml
similarity index 100%
rename from core/res/res/layout/keyguard_emergency_carrier_area_empty.xml
rename to packages/Keyguard/res/layout/keyguard_emergency_carrier_area_empty.xml
diff --git a/packages/Keyguard/res/layout/keyguard_face_unlock_view.xml b/packages/Keyguard/res/layout/keyguard_face_unlock_view.xml
new file mode 100644
index 0000000..94c68a5
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_face_unlock_view.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the screen that allows the user to unlock by showing their face. -->
+<com.android.keyguard.KeyguardFaceUnlockView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_face_unlock_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:contentDescription="@string/keyguard_accessibility_face_unlock">
+
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <FrameLayout
+ android:id="@+id/keyguard_bouncer_frame"
+ android:background="@drawable/kg_bouncer_bg_white"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ >
+ <com.android.internal.widget.FaceUnlockView
+ android:id="@+id/face_unlock_area_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:background="@drawable/intro_bg"
+ android:gravity="center">
+
+ <View
+ android:id="@+id/spotlightMask"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/facelock_spotlight_mask"
+ />
+
+ <ImageButton
+ android:id="@+id/face_unlock_cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="5dip"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentEnd="true"
+ android:background="#00000000"
+ android:src="@drawable/ic_facial_backup"
+ />
+ </com.android.internal.widget.FaceUnlockView>
+ </FrameLayout>
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal" />
+</com.android.keyguard.KeyguardFaceUnlockView>
diff --git a/packages/Keyguard/res/layout/keyguard_glow_pad_container.xml b/packages/Keyguard/res/layout/keyguard_glow_pad_container.xml
new file mode 100644
index 0000000..376d0e9
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_glow_pad_container.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <include layout="@layout/keyguard_glow_pad_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginBottom="-80dp"/>
+</merge>
\ No newline at end of file
diff --git a/packages/Keyguard/res/layout/keyguard_glow_pad_view.xml b/packages/Keyguard/res/layout/keyguard_glow_pad_view.xml
new file mode 100644
index 0000000..3a466dd
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_glow_pad_view.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the selector widget that allows the user to select an action. -->
+<com.android.internal.widget.multiwaveview.GlowPadView
+ xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/glow_pad_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="horizontal"
+ android:gravity="@integer/kg_selector_gravity"
+ android:contentDescription="@string/keyguard_accessibility_slide_area"
+
+ prvandroid:targetDrawables="@array/lockscreen_targets_unlock_only"
+ prvandroid:targetDescriptions="@array/lockscreen_target_descriptions_unlock_only"
+ prvandroid:directionDescriptions="@array/lockscreen_direction_descriptions"
+ prvandroid:handleDrawable="@drawable/ic_lockscreen_handle"
+ prvandroid:outerRingDrawable="@drawable/ic_lockscreen_outerring"
+ prvandroid:outerRadius="@dimen/glowpadview_target_placement_radius"
+ prvandroid:innerRadius="@dimen/glowpadview_inner_radius"
+ prvandroid:snapMargin="@dimen/glowpadview_snap_margin"
+ prvandroid:firstItemOffset="@integer/kg_glowpad_rotation_offset"
+ prvandroid:magneticTargets="true"
+ prvandroid:feedbackCount="1"
+ prvandroid:vibrationDuration="20"
+ prvandroid:glowRadius="@dimen/glowpadview_glow_radius"
+ prvandroid:pointDrawable="@drawable/ic_lockscreen_glowdot"
+ prvandroid:allowScaling="true" />
diff --git a/packages/Keyguard/res/layout/keyguard_message_area.xml b/packages/Keyguard/res/layout/keyguard_message_area.xml
new file mode 100644
index 0000000..a709e98
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_message_area.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
+<com.android.keyguard.KeyguardMessageArea
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:id="@+id/keyguard_message_area"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearance"
+ android:textSize="@dimen/kg_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary"
+ android:clickable="true" />
+
diff --git a/packages/Keyguard/res/layout/keyguard_message_area_large.xml b/packages/Keyguard/res/layout/keyguard_message_area_large.xml
new file mode 100644
index 0000000..ab6246d
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_message_area_large.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
+<com.android.keyguard.KeyguardMessageArea
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:id="@+id/keyguard_message_area"
+ android:maxLines="4"
+ android:textAppearance="?android:attr/textAppearance"
+ android:textSize="@dimen/kg_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary" />
+
diff --git a/packages/Keyguard/res/layout/keyguard_multi_user_avatar.xml b/packages/Keyguard/res/layout/keyguard_multi_user_avatar.xml
new file mode 100644
index 0000000..41b0be9
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_multi_user_avatar.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<com.android.keyguard.KeyguardMultiUserAvatar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/keyguard_avatar_size"
+ android:layout_height="@dimen/keyguard_avatar_size"
+ android:background="#00000000"
+ android:gravity="center_horizontal">
+ <ImageView
+ android:id="@+id/keyguard_user_avatar"
+ android:scaleType="center"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"/>
+ <TextView
+ android:id="@+id/keyguard_user_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:gravity="center"
+ android:textSize="@dimen/keyguard_avatar_name_size"
+ android:textColor="#ffffff"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:paddingLeft="2dp"
+ android:paddingRight="2dp" />
+</com.android.keyguard.KeyguardMultiUserAvatar>
diff --git a/packages/Keyguard/res/layout/keyguard_multi_user_selector.xml b/packages/Keyguard/res/layout/keyguard_multi_user_selector.xml
new file mode 100644
index 0000000..c1d5326
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_multi_user_selector.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<com.android.keyguard.KeyguardMultiUserSelectorView
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ androidprv:layout_childType="userSwitcher"
+ android:id="@+id/keyguard_user_selector"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:contentDescription="@string/keyguard_accessibility_user_selector"
+ android:visibility="gone">
+
+ <com.android.keyguard.KeyguardLinearLayout
+ android:id="@+id/keyguard_users_grid"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_marginBottom="@dimen/keyguard_muliuser_selector_margin"
+ android:layout_height="@dimen/keyguard_avatar_size"
+ android:layout_gravity="center|bottom" />
+
+</com.android.keyguard.KeyguardMultiUserSelectorView>
diff --git a/packages/Keyguard/res/layout/keyguard_password_view.xml b/packages/Keyguard/res/layout/keyguard_password_view.xml
new file mode 100644
index 0000000..d8012bf
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_password_view.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<com.android.keyguard.KeyguardPasswordView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_password_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:gravity="bottom"
+ android:contentDescription="@string/keyguard_accessibility_password_unlock"
+ >
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ />
+
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <!-- Password entry field -->
+ <!-- Note: the entire container is styled to look like the edit field,
+ since the backspace/IME switcher looks better inside -->
+ <FrameLayout
+ android:id="@+id/keyguard_bouncer_frame"
+ android:background="@drawable/kg_bouncer_bg_white"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ >
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:background="#70000000"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ >
+
+ <EditText android:id="@+id/passwordEntry"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center_horizontal"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
+ android:singleLine="true"
+ android:textStyle="normal"
+ android:inputType="textPassword"
+ android:textSize="36sp"
+ android:background="@null"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="#ffffffff"
+ android:imeOptions="flagForceAscii|actionDone"
+ />
+
+ <ImageView android:id="@+id/switch_ime_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_lockscreen_ime"
+ android:clickable="true"
+ android:padding="8dip"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone"
+ />
+
+ </LinearLayout>
+ </FrameLayout>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ />
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal" />
+
+</com.android.keyguard.KeyguardPasswordView>
diff --git a/packages/Keyguard/res/layout/keyguard_pattern_view.xml b/packages/Keyguard/res/layout/keyguard_pattern_view.xml
new file mode 100644
index 0000000..0c9380c
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_pattern_view.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the screen that shows the 9 circle unlock widget and instructs
+ the user how to unlock their device, or make an emergency call. This
+ is the portrait layout. -->
+<com.android.keyguard.KeyguardPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_pattern_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:gravity="center_horizontal"
+ android:contentDescription="@string/keyguard_accessibility_pattern_unlock">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center">
+
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <FrameLayout
+ android:id="@+id/keyguard_bouncer_frame"
+ android:background="@drawable/kg_bouncer_bg_white"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ >
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPatternView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_marginEnd="8dip"
+ android:layout_marginBottom="4dip"
+ android:layout_marginStart="8dip"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center"
+ android:contentDescription="@string/keyguard_accessibility_pattern_area" />
+ </FrameLayout>
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal" />
+ </LinearLayout>
+ </FrameLayout>
+
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/Keyguard/res/layout/keyguard_pin_view.xml b/packages/Keyguard/res/layout/keyguard_pin_view.xml
new file mode 100644
index 0000000..00c6a21
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_pin_view.xml
@@ -0,0 +1,224 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.keyguard.KeyguardPINView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:orientation="vertical"
+ android:contentDescription="@string/keyguard_accessibility_pin_unlock"
+ >
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+ <LinearLayout
+ android:id="@+id/keyguard_bouncer_frame"
+ android:background="@drawable/kg_bouncer_bg_white"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ android:layout_weight="1"
+ android:layoutDirection="ltr"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ >
+ <TextView android:id="@+id/pinEntry"
+ android:editable="true"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
+ android:singleLine="true"
+ android:cursorVisible="false"
+ android:background="@null"
+ android:textAppearance="@style/TextAppearance.NumPadKey"
+ android:imeOptions="flagForceAscii|actionDone"
+ />
+ <ImageButton android:id="@+id/delete_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_input_delete"
+ android:clickable="true"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:contentDescription="@string/keyboardview_keycode_delete"
+ />
+ </LinearLayout>
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="1dp"
+ android:background="#55FFFFFF"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key1"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="1"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key2"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="2"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key3"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="3"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key4"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="4"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key5"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="5"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key6"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="6"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ >
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key7"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="7"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key8"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="8"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key9"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="9"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <Space
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key0"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="0"
+ />
+ <ImageButton
+ android:id="@+id/key_enter"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:paddingRight="30dp"
+ android:src="@drawable/sym_keyboard_return_holo"
+ android:contentDescription="@string/keyboardview_keycode_enter"
+ />
+ </LinearLayout>
+ </LinearLayout>
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal" />
+
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/Keyguard/res/layout/keyguard_selector_view.xml b/packages/Keyguard/res/layout/keyguard_selector_view.xml
new file mode 100644
index 0000000..6cb5e67
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_selector_view.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the selector widget that allows the user to select an action. -->
+<com.android.keyguard.KeyguardSelectorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_selector_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="420dp"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ android:contentDescription="@string/keyguard_accessibility_slide_unlock">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:gravity="center">
+
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <View
+ android:id="@+id/keyguard_selector_view_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:background="@drawable/kg_bouncer_bg_white"/>
+
+ <include layout="@layout/keyguard_glow_pad_container" />
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_gravity="bottom|center_horizontal" />
+ </FrameLayout>
+
+</com.android.keyguard.KeyguardSelectorView>
+
diff --git a/packages/Keyguard/res/layout/keyguard_sim_pin_view.xml b/packages/Keyguard/res/layout/keyguard_sim_pin_view.xml
new file mode 100644
index 0000000..eccac19
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_sim_pin_view.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<!-- This is the SIM PIN view that allows the user to enter a SIM PIN to unlock the device. -->
+<com.android.keyguard.KeyguardSimPinView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_sim_pin_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:gravity="center_horizontal">
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_lockscreen_sim"/>
+
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+ <LinearLayout
+ android:id="@+id/keyguard_bouncer_frame"
+ android:background="@drawable/kg_bouncer_bg_white"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ android:layout_weight="1"
+ android:layoutDirection="ltr"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ >
+ <TextView android:id="@+id/pinEntry"
+ android:editable="true"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
+ android:singleLine="true"
+ android:cursorVisible="false"
+ android:background="@null"
+ android:textAppearance="@style/TextAppearance.NumPadKey"
+ android:imeOptions="flagForceAscii|actionDone"
+ />
+ <ImageButton android:id="@+id/delete_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_input_delete"
+ android:clickable="true"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:contentDescription="@string/keyboardview_keycode_delete"
+ />
+ </LinearLayout>
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="1dp"
+ android:background="#55FFFFFF"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key1"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="1"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key2"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="2"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key3"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="3"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key4"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="4"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key5"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="5"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key6"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="6"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ >
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key7"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="7"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key8"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="8"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key9"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="9"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <Space
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key0"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="0"
+ />
+ <ImageButton
+ android:id="@+id/key_enter"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:paddingRight="30dp"
+ android:src="@drawable/sym_keyboard_return_holo"
+ android:contentDescription="@string/keyboardview_keycode_enter"
+ />
+ </LinearLayout>
+ </LinearLayout>
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal" />
+
+</com.android.keyguard.KeyguardSimPinView>
diff --git a/packages/Keyguard/res/layout/keyguard_sim_puk_view.xml b/packages/Keyguard/res/layout/keyguard_sim_puk_view.xml
new file mode 100644
index 0000000..fe37203
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_sim_puk_view.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<!-- This is the SIM PUK view that allows the user to recover their device by entering the
+ carrier-provided PUK code and entering a new SIM PIN for it. -->
+<com.android.keyguard.KeyguardSimPukView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_sim_puk_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:gravity="center_horizontal">
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_lockscreen_sim"/>
+
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+ <LinearLayout
+ android:id="@+id/keyguard_bouncer_frame"
+ android:background="@drawable/kg_bouncer_bg_white"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ android:layout_weight="1"
+ android:layoutDirection="ltr"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ >
+ <TextView android:id="@+id/pinEntry"
+ android:editable="true"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
+ android:singleLine="true"
+ android:cursorVisible="false"
+ android:background="@null"
+ android:textAppearance="@style/TextAppearance.NumPadKey"
+ android:imeOptions="flagForceAscii|actionDone"
+ />
+ <ImageButton android:id="@+id/delete_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_input_delete"
+ android:clickable="true"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:contentDescription="@string/keyboardview_keycode_delete"
+ />
+ </LinearLayout>
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="1dp"
+ android:background="#55FFFFFF"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key1"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="1"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key2"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="2"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key3"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="3"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key4"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="4"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key5"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="5"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key6"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="6"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ >
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key7"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="7"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key8"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="8"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key9"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="9"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <Space
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+ <view class="com.android.keyguard.NumPadKey"
+ android:id="@+id/key0"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/pinEntry"
+ androidprv:digit="0"
+ />
+ <ImageButton
+ android:id="@+id/key_enter"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:paddingRight="30dp"
+ android:src="@drawable/sym_keyboard_return_holo"
+ android:contentDescription="@string/keyboardview_keycode_enter"
+ />
+ </LinearLayout>
+ </LinearLayout>
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal" />
+</com.android.keyguard.KeyguardSimPukView>
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
new file mode 100644
index 0000000..2304d9f
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<com.android.keyguard.KeyguardWidgetFrame
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/keyguard_status_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:gravity="center_horizontal">
+
+ <com.android.keyguard.KeyguardStatusView
+ android:id="@+id/keyguard_status_view_face_palm"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal|top"
+ android:contentDescription="@string/keyguard_accessibility_status">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
+ android:orientation="vertical"
+ android:focusable="true">
+ <com.android.keyguard.ClockView
+ android:id="@+id/clock_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/kg_status_line_font_right_margin"
+ android:layout_gravity="right">
+
+ <TextView android:id="@+id/clock_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="@dimen/kg_status_clock_font_size"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="#ffffffff"
+ android:drawablePadding="2dip"
+ />
+
+ </com.android.keyguard.ClockView>
+
+ <include layout="@layout/keyguard_status_area" />
+ </LinearLayout>
+
+ </com.android.keyguard.KeyguardStatusView>
+</com.android.keyguard.KeyguardWidgetFrame>
diff --git a/packages/Keyguard/res/layout/keyguard_transport_control_view.xml b/packages/Keyguard/res/layout/keyguard_transport_control_view.xml
new file mode 100644
index 0000000..7e36f9f
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_transport_control_view.xml
@@ -0,0 +1,112 @@
+<?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.
+-->
+
+<!-- This is a view to control music playback in keyguard. -->
+<com.android.keyguard.KeyguardTransportControlView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:id="@+id/keyguard_transport_control">
+
+ <!-- FrameLayout used as scrim to show between album art and buttons -->
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:foreground="@drawable/ic_lockscreen_player_background"
+ android:contentDescription="@string/keygaurd_accessibility_media_controls">
+ <!-- Use ImageView for its cropping features; otherwise could be android:background -->
+ <ImageView
+ android:id="@+id/albumart"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="fill"
+ android:scaleType="centerCrop"
+ android:adjustViewBounds="false"
+ />
+ </FrameLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom">
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
+ android:gravity="center_horizontal"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="5dip">
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <ImageView
+ android:id="@+id/btn_prev"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_media_previous"
+ android:clickable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="10dip"
+ android:contentDescription="@string/keyguard_accessibility_transport_prev_description"/>
+ </FrameLayout>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <ImageView
+ android:id="@+id/btn_play"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:clickable="true"
+ android:src="@drawable/ic_media_play"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="10dip"
+ android:contentDescription="@string/keyguard_accessibility_transport_play_description"/>
+ </FrameLayout>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <ImageView
+ android:id="@+id/btn_next"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:clickable="true"
+ android:src="@drawable/ic_media_next"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="10dip"
+ android:contentDescription="@string/keyguard_accessibility_transport_next_description"/>
+ </FrameLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+</com.android.keyguard.KeyguardTransportControlView>
diff --git a/core/res/res/layout/keyguard_widget_remove_drop_target.xml b/packages/Keyguard/res/layout/keyguard_widget_remove_drop_target.xml
similarity index 100%
rename from core/res/res/layout/keyguard_widget_remove_drop_target.xml
rename to packages/Keyguard/res/layout/keyguard_widget_remove_drop_target.xml
diff --git a/packages/Keyguard/res/values-af/activitystrings.xml b/packages/Keyguard/res/values-af/activitystrings.xml
new file mode 100644
index 0000000..f6e8d5e
--- /dev/null
+++ b/packages/Keyguard/res/values-af/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardToetsAktiwiteit"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"VerenigdeKamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Geen sekuriteit nie"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Wagwoord"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Patroon"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM PIN"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Kies legstuk…"</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"opSkermAfgeskakel"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"opSkermAangeskakel"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doenKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifieerOntsluit"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-af/strings.xml b/packages/Keyguard/res/values-af/strings.xml
new file mode 100644
index 0000000..911fbed
--- /dev/null
+++ b/packages/Keyguard/res/values-af/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Voer PIN-kode in"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Voer PUK en nuwe PIN-kode in"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-kode"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nuwe PIN-kode"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Raak om wagwoord in te voer"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Voer wagwoord in om te ontsluit"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Voer PIN in om te ontsluit"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Verkeerde PIN-kode."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Om te ontsluit, druk Kieslys dan 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maksimum gesigontsluit-pogings oorskry"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Gelaai"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Laai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Koppel jou herlaaier."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Druk kieslys om te ontsluit."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Netwerk gesluit"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Geen SIM-kaart nie"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Geen SIM-kaart in tablet nie."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Geen SIM-kaart in foon nie."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Steek \'n SIM-kaart in."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Die SIM-kaart is weg of nie leesbaar nie. Steek \'n SIM-kaart in."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Onbruikbare SIM-kaart."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Jou SIM-kaart is permanent gedeaktiveer."\n" Kontak jou draadlose diensverskaffer vir \'n ander SIM-kaart."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kaart is gesluit."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kaart is PUK-geslote."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Ontsluit tans SIM-kaart…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Legstuk %2$d van %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Voeg legstuk by."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Leeg"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Ontsluitruimte uitgevou."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Ontsluitruimte ingevou."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>-legstuk."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Gebruikerkieser"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Media-kontroles"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Herordening van legstuk begin."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Herordening van legstuk beëindig."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Legstuk <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> uitgevee."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Vou ontsluitruimte uit."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Sleep-ontsluit."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Patroon ontsluit."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Gesigslot."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN ontsluit."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Wagwoord ontsluit."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Patroonarea."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Sleep-area."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Vorigesnit-knoppie"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Volgendesnit-knoppie"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Laatwag-knoppie"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Speel-knoppie"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Stop-knoppie"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Kanselleer"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Vee uit"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Klaar"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Modus verander"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Invoersleutel"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Ontsluit"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Stil"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Klank aan"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Soek"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Gly op vir <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Gly af vir <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Gly links vir <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Gly regs vir <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g> ."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Noodoproep"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Het jy die patroon vergeet?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Verkeerde patroon"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Verkeerde wagwoord"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Verkeerde PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Probeer weer oor <xliff:g id="NUMBER">%d</xliff:g> sekondes."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Teken jou patroon"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Voer SIM-PIN in"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Voer PIN in"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Voer wagwoord in"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Kontak diensverskaffer vir details."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Voer die gewenste PIN-kode in"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Bevestig gewenste PIN-kode"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Ontsluit tans SIM-kaart…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Verkeerde PIN-kode."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Tik \'n PIN in wat 4 tot 8 syfers lank is."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK-kode moet 8 of meer syfers wees."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Voer weer die korrekte PUK-kode in. Herhaalde pogings sal die SIM permanent deaktiveer."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-kodes stem nie ooreen nie"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Te veel patroonpogings"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Om te ontsluit, meld met jou Google-rekening aan."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Gebruikernaam (e-pos)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Wagwoord"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Meld aan"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ongeldige gebruikernaam of wagwoord."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Het jy jou gebruikernaam of wagwoord vergeet?"\n"Besoek "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Kontroleer tans rekening..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jy het jou PIN <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd ingetik. "\n\n"Probeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer jou wagwoord verkeerdelik getik. "\n\n"Probeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. "\n\n"Probeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik gepoog om die tablet te ontsluit. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal die tablet na die fabrieksverstek teruggestel word en al die gebruikerdata sal verlore wees."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik gepoog om die foon te ontsluit. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal die foon na die fabrieksverstek teruggestel word en al die gebruikerdata sal verlore wees."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Jy het <xliff:g id="NUMBER">%d</xliff:g> keer verkeerdelik gepoog om die tablet te ontsluit. Die tablet sal nou na fabrieksverstek teruggestel word."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Jy het <xliff:g id="NUMBER">%d</xliff:g> keer verkeerdelik gepoog om die foon te ontsluit. Die foon sal nou na fabrieksverstek teruggestel word."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou tablet te ontsluit deur middel van \'n e-posrekening."\n\n" Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur middel van \'n e-posrekening."\n\n" Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwyder"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Vorigesnit-knoppie"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Volgendesnit-knoppie"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Laatwag-knoppie"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Speel-knoppie"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Stop-knoppie"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Geen diens nie."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-am/activitystrings.xml b/packages/Keyguard/res/values-am/activitystrings.xml
new file mode 100644
index 0000000..a6c7449
--- /dev/null
+++ b/packages/Keyguard/res/values-am/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"ምንም ደህንነት የለም"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"ፒን"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"የይለፍ ቃል"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"ሥርዓተ ጥለት"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"የሲም ፒን"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"የሲም ፒዩኬ"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"ንዑስ ፕሮግራም ይምረጡ..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-am/strings.xml b/packages/Keyguard/res/values-am/strings.xml
new file mode 100644
index 0000000..325163d
--- /dev/null
+++ b/packages/Keyguard/res/values-am/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"ፒን ኮድ ተይብ"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"PUK እና አዲስ ፒን ተይብ"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"የPUK ኮድ"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"አዲስ Pin ኮድ"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"የይለፍ ቃል ለመተየብ ንካ"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"ለመክፈት የይለፍ ቃል ተይብ"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"ለመክፈት ፒን ተይብ"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ትክክል ያልሆነ PIN ኮድ።"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"ለመክፈት፣ምናሌ ተጫን ከዛ 0"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"የመጨረሻውን የገጽ ክፈት ሙከራዎችን አልፏል"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"ባትሪ ሞልቷል"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"ባትሪ በመሙላት ላይ፣ <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"የኃይል መሙያዎን ይሰኩ።"</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"ለመክፈት ምናሌውን ይጫኑ።"</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"አውታረ መረብ ተቆልፏል"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"ምንም ሲም ካርድ የለም"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"በጡባዊ ውስጥ ምንም ሲም ካርድ የለም።"</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"በስልኩ ውስጥ ምንም ሲም ካርድ የለም።"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"ሲም ካርድ ያስገቡ።"</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"ሲም ካርዱ ጠፍቷል ወይም መነበብ አይችልም። እባክዎ ሲም ሲም ካርድ ያስገቡ።"</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"የማይሰራ ሲም ካርድ።"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"ሲም ካርድዎ እስከመጨረሻው ተሰናክሏል።"\n" ሌላ ሲም ካርድ ለማግኘት ከገመድ አልባ አገልግሎት አቅራቢዎ ጋር ይገናኙ።"</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"ሲም ካርድ ተዘግቷል።"</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"ሲም ካርድ በፒዩኬ ተዘግቷል።"</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"ሲም ካርዱን በመክፈት ላይ…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s። ምግብር %2$d ከ%3$d።"</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"ንዑስ ፕሮግራም አክል"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"ባዶ"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"የመክፈቻ አካባቢ ተስፋፍቷል።"</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"የመክፈቻ አካባቢ ተሰብስቧል።"</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"የ<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ንዑስ ፕሮግራም።"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"ተጠቃሚ መራጭ"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"ሁኔታ"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"ካሜራ"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"የሚዲያ መቆጣጠሪያዎች"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"የንዑስ ፕሮግራም ዳግም መደርደር ተጀምሯል።"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"የንዑስ ፕሮግራም ዳግም መደርደር አብቅቷል።"</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"ንዑስ ፕሮግራም <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ተሰርዟል።"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"የመክፈቻ አካባቢውን አስፋፋ።"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"በማንሸራተት ክፈት።"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"በስርዓተ-ጥለት መክፈት።"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"በፊት መክፈት።"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"በፒን መክፈት።"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"በይለፍ ቃል መክፈት።"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"የስርዓተ-ጥለት አካባቢ።"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"የማንሸራተቻ አካባቢ።"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"የቀዳሚ ትራክ አዝራር"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"የቀጣይ ትራክ አዝራር"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"ለአፍታ አቁም አዝራር"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"የአጫውት አዝራር"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"አቁም አዝራር"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ተወው"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ሰርዝ"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"ተከናውኗል"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ሞድ ለውጥ"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"ቀይር"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"አስገባ"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"ክፈት"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"ካሜራ"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"ፀጥታ"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"ድምፅ አብራ"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"ፈልግ"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ላይ አንሸራትት።"</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ታች አንሸራትት።"</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ግራ አንሸራትት።"</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ቀኝ አንሸራትት።"</string>
+ <string name="user_switched" msgid="3768006783166984410">"የአሁኑ ተጠቃሚ <xliff:g id="NAME">%1$s</xliff:g>።"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"የአደጋ ጊዜ ጥሪ"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"ስርዓተ ጥለቱን እርሳ"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"የተሳሳተ ስርዓተ ጥለት"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"የተሳሳተ ይለፍ ቃል"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"የተሳሳተ ፒን"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"በ<xliff:g id="NUMBER">%d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"ስርዓተ ጥለትዎን ይሳሉ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"የሲም ፒን ያስገቡ"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"ፒን ያስገቡ"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"የይለፍ ቃል ያስገቡ"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ለዝርዝር ድምጸ ተያያዥ ሞደምን ያግኙ።"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"የተፈለገውን የፒን ኮድ ያስገቡ"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"የተፈለገውን የፒን ኮድ ያረጋግጡ"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"ሲም ካርዱን በመክፈት ላይ…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"ትክክል ያልሆነ ፒን ኮድ።"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"ከ4 እስከ 8 ቁጥሮች የያዘ ፒን ይተይቡ።"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"የPUK ኮድ 8 ወይም ከዚያ በላይ ቁጥሮች ሊኖረው ይገባል።"</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"ትክክለኛውን የPUK ኮድ እንደገና ያስገቡ። ተደጋጋሚ ሙከራዎች ሲም ካርዱን እስከመጨረሻው ያሰናክሉታል።"</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"ፒን ኮዶች አይገጣጠሙም"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"በጣም ብዙ የስርዓተ ጥለት ሙከራዎች"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"ለመክፈት በGoogle መለያዎ ይግቡ።"</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"የተጠቃሚ ስም (ኢሜይል)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"የይለፍ ቃል"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"ግባ"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"ልክ ያልሆነ የተጠቃሚ ስም ወይም የይለፍ ቃል።"</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"የተጠቃሚ ስምዎን ወይም የይለፍ ቃልዎን ረሱት?"\n<b>"google.com/accounts/recovery"</b>"ይጎብኙ።"</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"መለያውን በማረጋገጥ ላይ…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ፒንዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልተየቡም። "\n\n"በ<xliff:g id="NUMBER_1">%d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"የይለፍ ቃልዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ተይበዋል።"\n\n"በ<xliff:g id="NUMBER_1">%d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"የመክፈቻ ስርዓተ ጥለትዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉትም። "\n\n" ከ<xliff:g id="NUMBER_1">%d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ጡባዊ ቱኮውን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ጡባዊ ቱኮው በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ስልኩን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ስልኩ በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"ጡባዊ ቱኮዎን <xliff:g id="NUMBER">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ጡባዊ ቱኮዎ አሁን በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመራል።"</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"ስልኩን <xliff:g id="NUMBER">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ስልኩ አሁን በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመራል።"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉትም። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው ጡባዊ ቱኮዎን እንዲከፍቱ ይጠየቃሉ።"\n\n" ከ<xliff:g id="NUMBER_2">%d</xliff:g> ከሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉትም። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው ስልክዎን እንዲከፍቱ ይጠየቃሉ።"\n\n"እባክዎ ከ<xliff:g id="NUMBER_2">%d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"አስወግድ"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"የቀዳሚ ትራክ አዝራር"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"የቀጣይ ትራክ አዝራር"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"ለአፍታ አቁም አዝራር"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"የአጫውት አዝራር"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"አቁም አዝራር"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"ከአገልግሎት መስጫ ክልል ውጪ።"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ar/activitystrings.xml b/packages/Keyguard/res/values-ar/activitystrings.xml
new file mode 100644
index 0000000..f77d8f00
--- /dev/null
+++ b/packages/Keyguard/res/values-ar/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"بدون تأمين"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"رقم التعريف الشخصي"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"كلمة المرور"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"نقش"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"رقم التعريف الشخصي لبطاقة SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"رمز PUK لبطاقة SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"جارٍ اختيار أداة..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ar/strings.xml b/packages/Keyguard/res/values-ar/strings.xml
new file mode 100644
index 0000000..21f2f09
--- /dev/null
+++ b/packages/Keyguard/res/values-ar/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"اكتب رمز رقم التعريف الشخصي"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"اكتب رمز PUK ورمز رقم التعريف الشخصي الجديد"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"رمز PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"رمز رقم التعريف الشخصي الجديد"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"المس لكتابة كلمة المرور"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"اكتب كلمة المرور لإلغاء التأمين"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"اكتب رقم التعريف الشخصي لإلغاء التأمين"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"رقم التعريف الشخصي غير صحيح."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"لإلغاء التأمين، اضغط على \"القائمة\" ثم على 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"تم تجاوز الحد الأقصى لعدد محاولات تأمين الجهاز بالوجه"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"تم الشحن"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"جارٍ الشحن، <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"توصيل جهاز الشحن."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"اضغط على \"القائمة\" لإلغاء القفل."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"الشبكة مؤمّنة"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"ليست هناك بطاقة SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"ليست هناك بطاقة SIM في الجهاز اللوحي."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"ليست هناك بطاقة SIM في الهاتف."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"أدخل بطاقة SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"بطاقة SIM مفقودة أو غير قابلة للقراءة. أدخل بطاقة SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"بطاقة SIM غير قابلة للاستخدام."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"تم تعطيل بطاقة SIM بشكل دائم."\n" اتصل بمقدم خدمة اللاسلكي للحصول على بطاقة SIM أخرى."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"بطاقة SIM مؤمّنة."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"بطاقة SIM مؤمّنة بكود PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"جارٍ إلغاء تأمين بطاقة SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. الأداة %2$d من %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"إضافة أداة."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"فارغة"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"تم توسيع منطقة إلغاء القفل."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"تم تصغير منطقة إلغاء القفل."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"أداة <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"محدد المستخدم"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"الحالة"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"الكاميرا"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"أدوات التحكم في الوسائط"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"بدأت إعادة ترتيب الأدوات."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"انتهت إعادة ترتيب الأدوات."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"تم حذف أداة <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"توسيع منطقة إلغاء القفل."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"إلغاء القفل باستخدام التمرير."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"إلغاء القفل باستخدام النقش."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"تأمين الجهاز بالوجه."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"إلغاء القفل باستخدام رقم التعريف الشخصي."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"إلغاء القفل باستخدام كلمة المرور."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"منطقة النقش."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"منطقة التمرير."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"زر المقطع الصوتي السابق"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"زر المقطع الصوتي التالي"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"زر الإيقاف المؤقت"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"زر التشغيل"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"زر الإيقاف"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ب ت ث"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"إلغاء"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"حذف"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"تم"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"تغيير الوضع"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"العالي"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"إلغاء تأمين"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"الكاميرا"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"صامت"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"تشغيل الصوت"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"بحث"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"تمرير لأعلى لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"تمرير لأسفل لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"تمرير لليسار لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"تمرير لليمين لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"المستخدم الحالي <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"الاتصال بالطوارئ"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"نسيت النقش"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"نقش خاطئ"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"كلمة مرور خاطئة"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"رقم تعريف شخصي خاطئ"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"حاول مرة أخرى خلال <xliff:g id="NUMBER">%d</xliff:g> ثانية."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"ارسم نقشك"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"أدخل رقم التعريف الشخصي لبطاقة SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"أدخل رقم التعريف الشخصي"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"أدخل كلمة المرور"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"بطاقة SIM معطلة الآن. أدخل رمز PUK للمتابعة. اتصل بمشغل شبكة الجوال للاطلاع على التفاصيل."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"إدخال رمز رقم التعريف الشخصي المراد"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"تأكيد رمز رقم التعريف الشخصي المراد"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"جارٍ إلغاء تأمين بطاقة SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"رقم التعريف الشخصي غير صحيح."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"اكتب رقم التعريف الشخصي المكون من 4 إلى 8 أرقام."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"يجب أن يتضمن رمز PUK 8 أرقام أو أكثر."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"أعد إدخال رمز PUK الصحيح. وستؤدي المحاولات المتكررة إلى تعطيل بطاقة SIM نهائيًا."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"لا يتطابق رمزا رقم التعريف الشخصي"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"محاولات النقش كثيرة جدًا"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"لإلغاء التأمين، سجّل الدخول بحسابك في Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"اسم المستخدم (البريد إلكتروني)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"كلمة المرور"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"تسجيل الدخول"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"اسم مستخدم غير صحيح أو كلمة مرور غير صالحة."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"هل نسيت اسم المستخدم أو كلمة المرور؟"\n"انتقل إلى "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"جارٍ فحص الحساب…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"لقد كتبت رقم التعريف الشخصي بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. "\n\n"أعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"لقد كتبت كلمة المرور بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. "\n\n"أعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"لقد رسمت نقش إلغاء التأمين بطريقة غير صحيحة <xliff:g id="NUMBER_0">%d</xliff:g> مرة. "\n\n"أعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"لقد حاولت إلغاء تأمين الجهاز اللوحي بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة تعيين الجهاز اللوحي على الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"لقد حاولت إلغاء تأمين الهاتف بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة تعيين الهاتف على الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"لقد حاولت إلغاء تأمين الجهاز اللوحي بشكل غير صحيح <xliff:g id="NUMBER">%d</xliff:g> مرة. سيتم الآن إعادة تعيين الجهاز اللوحي على الإعدادات الافتراضية للمصنع."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"لقد حاولت إلغاء تأمين الهاتف بشكل غير صحيح <xliff:g id="NUMBER">%d</xliff:g> مرة. سيتم الآن إعادة تعيين الهاتف على الإعدادات الافتراضية للمصنع."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستطالَب بإلغاء تأمين الجهاز اللوحي باستخدام معلومات حساب بريد إلكتروني."\n\n" أعد المحاولة خلال <xliff:g id="NUMBER_2">%d</xliff:g> ثانية."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الهاتف باستخدام حساب بريد إلكتروني لإلغاء تأمين الهاتف."\n\n" أعد المحاولة خلال <xliff:g id="NUMBER_2">%d</xliff:g> ثانية."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"إزالة"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"زر المقطع الصوتي السابق"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"زر المقطع الصوتي التالي"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"زر الإيقاف المؤقت"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"زر التشغيل"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"زر الإيقاف"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"لا تتوفر خدمة"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-be/activitystrings.xml b/packages/Keyguard/res/values-be/activitystrings.xml
new file mode 100644
index 0000000..ccefe40
--- /dev/null
+++ b/packages/Keyguard/res/values-be/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Аховы няма"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN-код"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Пароль"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Шаблон"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN-код SIM-карты"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK-код SIM-карты"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Выбар вiджэта..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-be/strings.xml b/packages/Keyguard/res/values-be/strings.xml
new file mode 100644
index 0000000..c7ddeb7
--- /dev/null
+++ b/packages/Keyguard/res/values-be/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Увядзіце PIN-код"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Увядзіце PUK-код і новы PIN-код"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Новы PIN-код"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Дакраніцеся, каб увесці пароль"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Увядзіце пароль для разблакавання"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Каб разблакаваць, увядзіце PIN-код"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Няправільны PIN-код."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Каб разблакаваць, націсніце \"Меню\", затым 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Перавышана максімальная колькасць спроб разблакоўкі праз Фэйскантроль"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Зараджаны"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Зарадка, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Падключыце зарадную прыладу."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Націсніце кнопку \"Меню\", каб разблакіраваць."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Сетка заблакiраваная"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Няма SIM-карты"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"У планшэце няма SIM-карты."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"У тэлефоне няма SIM-карты."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Устаўце SIM-карту."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-карта адсутнічае ці не чытаецца. Устаўце SIM-карту."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM-карту немагчыма выкарыстоўваць."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Ваша SIM-карта была адключана назаўсёды."\n" Звяжыцеся з аператарам бесправадной сувязі, каб атрымаць іншую SIM-карту."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-карта заблакiраваная."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-карта заблакiравана PUK-кодам."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Разблакiраванне SIM-карты..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. ВIджэт %2$d з %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Дадаць віджэт"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Пусты"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Вобласць разблакіроўкі разгарнута."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Вобласць разблакіроўкі згарнута."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Віджэт <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Селектар карыстальнiка"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Стан"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Камера"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Налады мультымедыя"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Змяненне парадку віджэтаў пачалося."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Змяненне парадку віджэтаў скончылася."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Віджэт <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> выдалены."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Разгарнуць вобласць разблакіроўкі."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Разблакiроўка слайда."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Узор разблакiроўкі."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Фэйскантроль"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN-код разблакiроўкі."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Пароль разблакiроўкі."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Вобласць узора."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Вобласць слайда."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Кнопка папярэдняй кампазiцыі"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Кнопка наступнай кампазiцыі"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Кнопка паўзы"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Кнопка прайгравання"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Кнопка спынення"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"Alt"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Адмена"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Выдаліць"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Гатова"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Змена рэжыму"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Разблакаваць"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Камера"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Ціхі рэжым"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Гук уключаны"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Пошук"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Правядзіце пальцам уверх, каб атрымаць <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Правядзіце пальцам уніз, каб атрымаць <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Правядзіце пальцам улева, каб атрымаць <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Правядзіце пальцам управа, каб атрымаць <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Бягучы карыстальнік <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Экстранны выклік"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Забылі ключ"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Няправільна ключ"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Няправiльны пароль"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Няправільны PIN-код"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Паўтарыце спробу праз <xliff:g id="NUMBER">%d</xliff:g> с."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Намалюйце ключ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Увядзіце PIN-код SIM-карты"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Увядзіце PIN-код"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Увядзіце пароль"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM-карта зараз адключана. Увядзіце PUK-код, каб працягнуць. Звяжыцеся са сваiм аператарам, каб атрымаць дадатковую iнфармацыю."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Увядзіце жаданы PIN-код"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Пацвердзіце жадан PIN-код"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Разблакiроўка SIM-карты..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Няправільны PIN-код."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Увядзіце PIN-код, які змяшчае ад 4 да 8 лічбаў."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK-код павінен утрымлiваць 8 лiчбаў і больш."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Паўторна увядзіце правільны PUK-код. Неаднаразовыя спробы назаўжды адключаць SIM-карту."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-коды не супадаюць"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Занадта шмат спроб паўтарыць шаблон!"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Каб разблакiраваць, увайдзіце ў свой уліковы запіс Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Імя карыстальніка (электронная пошта)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Пароль"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Увайсцi"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Няправільнае імя карыстальніка ці пароль."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Забыліся на імя карыстальніка або пароль?"\n"Наведайце "<b>"google.com/accounts/recovery"</b></string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Праверка ўлiковага запiсу..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Вы няправільна ўвялі PIN-код пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Паўтарыце спробу праз <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Вы няправільна ўвялі пароль пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Паўтарыце спробу праз <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Паўтарыце спробу праз <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Вы няправільна спрабавалі разблакiраваць планшэт некалькi разоў (<xliff:g id="NUMBER_0">%d</xliff:g>). Пасля яшчэ некалькiх спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) ён будзе скінуты да заводскіх налад i карыстальнiцкiя дадзеныя будуць згубленыя."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Вы няправільна спрабавалі разблакiраваць планшэт некалькi разоў (<xliff:g id="NUMBER_0">%d</xliff:g>). Пасля яшчэ некалькiх спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) ён будзе скінуты да завадскіх налад i карыстальнiцкiя дадзеныя будуць згубленыя."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Вы няправільна спрабавалі разблакiраваць планшэт некалькi разоў (<xliff:g id="NUMBER">%d</xliff:g>). Цяпер ён будзе скінуты да завадскіх налад."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Вы няправільна спрабавалі разблакiраваць тэлефон некалькi разоў (<xliff:g id="NUMBER">%d</xliff:g>). Цяпер ён будзе скінуты да завадскіх налад."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Выдалiць"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Кнопка папярэдняй кампазiцыі"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Кнопка наступнай кампазiцыі"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Кнопка паўзы"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Кнопка прайгравання"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Кнопка спынення"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Не абслугоўваецца."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-bg/activitystrings.xml b/packages/Keyguard/res/values-bg/activitystrings.xml
new file mode 100644
index 0000000..807bcf2
--- /dev/null
+++ b/packages/Keyguard/res/values-bg/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Без защита"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"ПИН код"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Парола"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Фигура"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"ПИН код за SIM карта"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK код за SIM карта"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Избиране на приспособление..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-bg/strings.xml b/packages/Keyguard/res/values-bg/strings.xml
new file mode 100644
index 0000000..8abfd9a
--- /dev/null
+++ b/packages/Keyguard/res/values-bg/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Въведете ПИН кода"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Въведете PUK и новия ПИН код"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK код"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Нов ПИН код"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Докоснете и въведете парола"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Въведете парола, за да отключите"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Въведете ПИН, за да отключите"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неправилен ПИН код."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"За да отключите, натиснете „Меню“ и после 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Максималният брой опити за отключване с лице е надвишен"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Заредена"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Зарежда се, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Свържете зарядното си устройство."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Натиснете иконата за меню, за да отключите."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Мрежата е заключена"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Няма SIM карта"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"В таблета няма SIM карта."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"В телефона няма SIM карта."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Поставете SIM карта."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM картата липсва или е нечетима. Поставете SIM карта."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Неизползваема SIM карта."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM картата ви е деактивирана за постоянно."\n"За да получите друга, се свържете с доставчика на безжичната си услуга."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM картата е заключена."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM картата е заключена с PUK код."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM картата се отключва…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Приспособление %2$d от %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Добавяне на приспособление."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Празно"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Областта за отключване е разгъната."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Областта за отключване е свита."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Приспособление за <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Инструмент за избор на потребители"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Състояние"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Камера"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Контроли за мултимедията"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Пренареждането на приспособленията започна."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Пренареждането на приспособленията завърши."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Приспособлението за <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> е изтрито."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Разгъване на областта за отключване."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Отключване с плъзгане."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Отключване с фигура."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Отключване с лице."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Отключване с ПИН код."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Отключване с парола."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Област на фигурата."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Област на плъзгане."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Бутон за предишния запис"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Бутон за следващия запис"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Бутон за пауза"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Бутон за пускане"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Бутон за спиране"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"АБВ"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Отказ"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Изтриване"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Готово"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Промяна на режима"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Отключване"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Камера"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Тих режим"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Включване на звука"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Търсене"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Плъзнете нагоре за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Плъзнете надолу за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Плъзнете наляво за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Плъзнете надясно за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Текущ потребител <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Спешно обаждане"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Забравена фигура"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Грешна фигура"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Грешна парола"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Грешен ПИН код"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Опитайте отново след <xliff:g id="NUMBER">%d</xliff:g> секунди."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Начертайте фигурата си"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Въведете ПИН кода за SIM картата"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Въведете ПИН код"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Въведете паролата"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM картата вече е деактивирана. Въведете PUK кода, за да продължите. Свържете се с оператора за подробности."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Въведете желания ПИН код"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Потвърдете желания ПИН код"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM картата се отключва…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Неправилен ПИН код."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Въведете ПИН код с четири до осем цифри."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK кодът трябва да е с 8 или повече цифри."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Въведете отново правилния PUK код. Многократните опити ще деактивират за постоянно SIM картата."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"ПИН кодовете не съвпадат"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Опитите за фигурата са твърде много"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"За да отключите, влезте с профила си в Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Потребителско име (имейл)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Парола"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Вход"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Невалидно потребителско име или парола."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Забравили сте потребителското име или паролата си?"\n"Посетете "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Профилът се проверява…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Въведохте неправилно ПИН кода си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. "\n\n"Опитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Въведохте неправилно паролата си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. "\n\n"Опитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. "\n\n"Опитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Направихте опит да отключите неправилно таблета <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Направихте опит да отключите неправилно телефона <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Направихте опит да отключите неправилно таблета <xliff:g id="NUMBER">%d</xliff:g> пъти. Сега ще бъдат възстановени стандартните му фабрични настройки."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Направихте опит да отключите неправилно телефона <xliff:g id="NUMBER">%d</xliff:g> пъти. Сега ще бъдат възстановени стандартните му фабрични настройки."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдете помолени да отключите таблета посредством имейл адрес."\n\n" Опитайте отново след <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдете помолени да отключите телефона посредством имейл адрес."\n\n" Опитайте отново след <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Премахване"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Бутон за предишния запис"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Бутон за следващия запис"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Бутон за пауза"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Бутон за пускане"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Бутон за спиране"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Няма покритие."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ca/activitystrings.xml b/packages/Keyguard/res/values-ca/activitystrings.xml
new file mode 100644
index 0000000..c18b9bb
--- /dev/null
+++ b/packages/Keyguard/res/values-ca/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"No hi ha seguretat"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Contrasenya"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Patró"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN de la SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK de la SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Tria un widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ca/strings.xml b/packages/Keyguard/res/values-ca/strings.xml
new file mode 100644
index 0000000..7daa32e
--- /dev/null
+++ b/packages/Keyguard/res/values-ca/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Introdueix el codi PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Introdueix el codi PUK i el codi PIN nou"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Codi PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Codi PIN nou"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Toca per introduir contrasenya"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Introdueix la contrasenya per desbloquejar"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Introdueix la contrasenya per desbloquejar"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Codi PIN incorrecte."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Per desbloquejar-lo, premeu Menú i després 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"S\'ha superat el nombre màxim d\'intents de desbloqueig facial"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Carregada"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"S\'està carregant, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Connecta el carregador."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Prem Menú per desbloquejar."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Xarxa bloquejada"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"No hi ha cap targeta SIM."</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"No hi ha cap targeta SIM a la tauleta."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"No hi ha cap targeta SIM al telèfon."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Insereix una targeta SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Falta la targeta SIM o no es pot llegir. Insereix-ne una."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Targeta SIM no utilitzable."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"La targeta SIM s\'ha desactivat permanentment."\n" Contacta amb el teu proveïdor de serveis sense fil per obtenir-ne una altra."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La targeta SIM està bloquejada."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La targeta SIM està bloquejada pel PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"S\'està desbloquejant la targeta SIM..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Afegeix un widget"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Buit"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"S\'ha ampliat l\'àrea de desbloqueig."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"L\'àrea de desbloqueig està replegada."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget de <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Selector d\'usuaris"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Estat"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Càmera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Controls multimèdia"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"S\'ha iniciat la reorganització del widget."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Ha finalitzat la reorganització del widget."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"S\'ha suprimit el widget de <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Amplia l\'àrea de desbloqueig."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Desbloqueig lliscant el dit"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueig mitjançant patró"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueig facial"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueig mitjançant PIN"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueig mitjançant contrasenya"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Àrea de patró"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Àrea per lliscar el dit"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Botó de pista anterior"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Botó de pista següent"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Botó de pausa"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Botó de reproducció"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Botó de parada"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel·la"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Suprimeix"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Fet"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Canvi de mode"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Maj"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Retorn"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Desbloqueja"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Càmera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Silenci"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Activa el so"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Cerca"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Fes lliscar el dit cap amunt per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Fes lliscar el dit cap avall per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Fes lliscar el dit cap a l\'esquerra per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Fes lliscar el dit cap a la dreta per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Usuari actual: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Trucada d\'emergència"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Patró oblidat"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Patró incorrecte"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Contrasenya incorrecta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorrecte"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER">%d</xliff:g> segons."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dibuixa el patró"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introdueix el PIN de la SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Introdueix el PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Introdueix la contrasenya"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"La SIM està desactivada. Introdueix el codi PUK per continuar. Contacta amb l\'operador de telefonia mòbil per obtenir detalls."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Introdueix el codi PIN"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirma el codi PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"S\'està desbloquejant la targeta SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Codi PIN incorrecte."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Escriu un PIN que tingui de 4 a 8 números."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"El codi PUK ha de tenir 8 números o més."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Torna a introduir el codi PUK correcte. Els intents repetits faran que es desactivi la SIM de manera permanent."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Els codis PIN no coincideixen"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Massa intents incorrectes"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Per desbloquejar el telèfon, inicia la sessió amb el compte de Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nom d\'usuari (correu electrònic)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Contrasenya"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Inicia la sessió"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nom d\'usuari o contrasenya no vàlids."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Has oblidat el teu nom d\'usuari o la contrasenya?"\n"Visita "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"S\'està comprovant el compte…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has escrit malament el PIN <xliff:g id="NUMBER_0">%d</xliff:g> vegades. "\n\n"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%d</xliff:g> vegades. "\n\n"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has dibuixat el patró de desbloqueig de manera incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> vegades. "\n\n"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. D\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, la tauleta es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. D\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, el telèfon es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. Ara la tauleta es restablirà a la configuració predeterminada de fàbrica."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. Ara el telèfon es restablirà a la configuració predeterminada de fàbrica."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Després de <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, se\'t demanarà que desbloquegis la tauleta amb un compte de correu electrònic."\n\n" Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Després de <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, se\'t demanarà que desbloquegis el telèfon amb un compte de correu electrònic."\n\n" Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Elimina"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Botó de pista anterior"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Botó de pista següent"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Botó de pausa"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Botó de reproducció"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Botó de parada"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Sense servei."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-cs/activitystrings.xml b/packages/Keyguard/res/values-cs/activitystrings.xml
new file mode 100644
index 0000000..354176e
--- /dev/null
+++ b/packages/Keyguard/res/values-cs/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Žádné zabezpečení"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Heslo"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Gesto"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN SIM karty"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK SIM karty"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Zvolte widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-cs/strings.xml b/packages/Keyguard/res/values-cs/strings.xml
new file mode 100644
index 0000000..2f956c7
--- /dev/null
+++ b/packages/Keyguard/res/values-cs/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Zadejte kód PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Zadejte kód PUK a nový kód PIN."</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Kód PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nový kód PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Dotykem zadáte heslo"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Zadejte heslo pro odemknutí"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Zadejte kód PIN pro odemknutí"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Nesprávný kód PIN."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Chcete-li telefon odemknout, stiskněte Menu a poté 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Překročili jste maximální povolený počet pokusů o odemknutí obličejem."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Nabito"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Nabíjení, <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Připojte dobíjecí zařízení."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Telefon odemknete stisknutím tlačítka Menu."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Síť je blokována"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Není vložena SIM karta."</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"V tabletu není SIM karta."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"V telefonu není SIM karta."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Vložte SIM kartu."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM karta chybí nebo je nečitelná. Vložte SIM kartu."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Nepoužitelná SIM karta."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Vaše SIM karta byla natrvalo zablokována."\n" Požádejte svého poskytovatele bezdrátových služeb o další SIM kartu."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM karta je zablokována."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM karta je zablokována pomocí kódu PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Odblokování SIM karty…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d z %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Přidat widget"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Prázdné"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Oblast odemknutí byla rozšířena."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Oblast odemknutí byla sbalena."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Výběr uživatele"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Stav"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Fotoaparát"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Ovládání médií"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Přeuspořádání widgetů bylo zahájeno."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Přeuspořádání widgetů bylo dokončeno."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> byl smazán."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Rozšířit oblast odemknutí"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Odemknutí přejetím prstem."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Odemknutí gestem."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Odemknutí obličejem."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Odemknutí kódem PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Odemknutí heslem."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Oblast pro zadání bezpečnostního gesta."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Oblast pro přejetí prstem."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Tlačítko Předchozí stopa"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Tlačítko Další stopa"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Tlačítko Pozastavit"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Tlačítko Přehrát"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Tlačítko Zastavit"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"Alt"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Zrušit"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Smazat"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Hotovo"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Změna režimu"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Odemknout"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Fotoaparát"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Tichý"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Zapnout zvuk"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Vyhledávání"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Přejeďte prstem nahoru: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Přejeďte prstem dolů: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Přejeďte prstem doleva: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Přejeďte prstem doprava: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
+ <string name="user_switched" msgid="3768006783166984410">"Aktuální uživatel je <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Tísňové volání"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Zapomenuté gesto"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Nesprávné gesto"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nesprávné heslo"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Nesprávný kód PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Zkuste to znovu za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Nakreslete gesto"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Zadejte kód PIN SIM karty"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Zadejte kód PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Zadejte heslo"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM karta byla deaktivována. Chcete-li pokračovat, je třeba zadat kód PUK. Podrobné informace získáte od operátora."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Zadejte požadovaný kód PIN."</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potvrďte požadovaný kód PIN."</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Odblokování SIM karty..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Nesprávný kód PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Zadejte kód PIN o délce 4–8 číslic."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Minimální délka kódu PUK je 8 číslic."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Znovu zadejte správný kód PUK. Opakovanými pokusy SIM kartu trvale deaktivujete."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Kódy PIN se neshodují."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Příliš mnoho pokusů o nakreslení gesta"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Chcete-li telefon odemknout, přihlaste se pomocí svého účtu Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Uživatelské jméno (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Heslo"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Přihlásit se"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Neplatné uživatelské jméno nebo heslo."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zapomněli jste uživatelské jméno nebo heslo?"\n"Přejděte na stránku "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Kontrola účtu…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste zadali nesprávný kód PIN. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně zadali heslo. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste zadali nesprávné bezpečnostní gesto. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Již jste se <xliff:g id="NUMBER_0">%d</xliff:g>krát pokusili odemknout tablet nesprávným způsobem. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech se v tabletu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Již jste se <xliff:g id="NUMBER_0">%d</xliff:g>krát pokusili odemknout telefon nesprávným způsobem. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech se v telefonu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Již jste se <xliff:g id="NUMBER">%d</xliff:g>krát pokusili odemknout tablet nesprávným způsobem. V tabletu se nyní obnoví výchozí tovární nastavení."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Již jste se <xliff:g id="NUMBER">%d</xliff:g>krát pokusili odemknout telefon nesprávným způsobem. V telefonu se nyní obnoví výchozí tovární nastavení."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%d</xliff:g>dalších neúspěšných pokusech budete požádáni o odemčení tabletu pomocí e-mailového účtu."\n\n" Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech budete požádáni o odemčení telefonu pomocí e-mailového účtu."\n\n" Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odebrat"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Tlačítko Předchozí stopa"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Tlačítko Další stopa"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Tlačítko Pozastavit"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Tlačítko Přehrát"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Tlačítko Zastavit"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Žádný signál."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-da/activitystrings.xml b/packages/Keyguard/res/values-da/activitystrings.xml
new file mode 100644
index 0000000..af07ba5
--- /dev/null
+++ b/packages/Keyguard/res/values-da/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Ingen sikkerhed"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"Pinkode"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Adgangskode"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Mønster"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"Pinkode til SIM-kort"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK-kode til SIM-kort"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Vælg widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-da/strings.xml b/packages/Keyguard/res/values-da/strings.xml
new file mode 100644
index 0000000..4a36c93
--- /dev/null
+++ b/packages/Keyguard/res/values-da/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Indtast pinkode"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Indtast PUK- og pinkode"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-kode"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Ny pinkode"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Tryk for at angive adgangskode"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Indtast adgangskoden for at låse op"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Indtast pinkode for at låse op"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Forkert pinkode."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Tryk på Menu og dernæst på 0 for at låse op."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Det maksimale antal forsøg på at bruge Ansigtslås er overskredet"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Opladet"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Oplader, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Tilslut din oplader."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Tryk på Menu for at låse op."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Netværket er låst"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Intet SIM-kort"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Der er ikke noget SIM-kort i tabletten."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Der er ikke noget SIM-kort i telefonen."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Indsæt et SIM-kort."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-kortet mangler eller kan ikke læses. Indsæt et SIM-kort."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Ubrugeligt SIM-kort."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Dit SIM-kort er blevet permanent deaktiveret."\n"Kontakt din tjenesteudbyder for at få et nyt SIM-kort."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kortet er låst."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kort er låst med PUK-koden."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-kortet låses op…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d af %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Tilføj widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tom"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Oplåsningsområdet er udvidet."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Oplåsningsområdet er skjult."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget til <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Brugervælger"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Mediekontrolelementer"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Omrokering af widgets er påbegyndt."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Omrokering af widgets er afsluttet."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widgetten <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> er slettet."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Udvid oplåsningsområdet."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Lås op ved at stryge."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Lås op med mønster."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Lås op med ansigt."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Lås op med pinkode."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Lås op med adgangskode."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Mønsterområde."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Strygeområde."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Knap til forrige nummer"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Knap til næste nummer"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Pause-knap"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Afspil-knap"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Stop-knap"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuller"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Slet"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Udført"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Ændring af tilstand"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Angiv"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Lås op"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Lydløs"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Lyd slået til"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Søgning"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Glid op for at <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Glid ned for at <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Glid til venstre for at <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Glid til højre for at <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Nuværende bruger <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Nødopkald"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Glemt mønster"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Forkert mønster"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Forkert adgangskode"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Forkert pinkode"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Prøv igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Tegn dit mønster"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Indtast pinkode til SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Indtast pinkode"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Angiv adgangskode"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM-kortet er nu deaktiveret. Indtast PUK-koden for at fortsætte. Kontakt mobiloperatøren for at få flere oplysninger."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Indtast den ønskede pinkode"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Bekræft den ønskede pinkode"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-kortet låses op…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Forkert pinkode."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Indtast en pinkode på mellem 4 og 8 tal."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK-koden skal være på 8 tal eller mere."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Indtast den korrekte PUK-kode. Gentagne forsøg vil permanent deaktivere SIM-kortet."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Pinkoderne stemmer ikke overens"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"For mange forsøg på at tegne mønstret korrekt"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Lås op ved at logge ind med din Google-konto."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Brugernavn (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Adgangskode"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Log ind"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ugyldigt brugernavn eller ugyldig adgangskode."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glemt dit brugernavn eller din adgangskode?"\n"Gå til "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Kontoen kontrolleres…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har indtastet en forkert pinkode <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har indtastet din adgangskode forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg nulstilles tabletten til fabriksindstillingerne, og alle brugerdata mistes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg, nulstilles telefonen til fabriksindstillingerne, og alle brugerdata mistes."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER">%d</xliff:g> gange. Tabletten nulstilles til fabriksindstillingerne."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER">%d</xliff:g> gange. Telefonen nulstilles til fabriksindstillingerne."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg vil du blive bedt om at låse din tablet op ved hjælp af en e-mailkonto"\n\n" Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg til vil du blive bedt om at låse din telefon op ved hjælp af en e-mailkonto."\n\n" Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Fjern"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Knap til forrige nummer"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Knap til næste nummer"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Pause-knap"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Afspil-knap"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Stop-knap"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Ingen dækning."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-de/activitystrings.xml b/packages/Keyguard/res/values-de/activitystrings.xml
new file mode 100644
index 0000000..d8e9272
--- /dev/null
+++ b/packages/Keyguard/res/values-de/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Keine Sicherheit"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Passwort"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Muster"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN für SIM-Karte"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK für SIM-Karte"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Widget auswählen..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-de/strings.xml b/packages/Keyguard/res/values-de/strings.xml
new file mode 100644
index 0000000..ad4f8ab
--- /dev/null
+++ b/packages/Keyguard/res/values-de/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"PIN-Code eingeben"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"PUK und neuen PIN-Code eingeben"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-Code"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Neuer PIN-Code"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Zur Passworteingabe berühren"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Passwort zum Entsperren eingeben"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"PIN zum Entsperren eingeben"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Falscher PIN-Code"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Drücken Sie zum Entsperren die Menütaste und dann auf \"0\"."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Die maximal zulässige Anzahl an Face Unlock-Versuchen wurde überschritten."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Aufgeladen"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Akku wird aufgeladen (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Bitte Ladegerät anschließen"</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Zum Entsperren die Menütaste drücken"</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Netzwerk gesperrt"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Keine SIM-Karte"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Keine SIM-Karte im Tablet"</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Keine SIM-Karte im Telefon"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Legen Sie eine SIM-Karte ein."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-Karte fehlt oder ist nicht lesbar. Bitte legen Sie eine SIM-Karte ein."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM-Karte unbrauchbar"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Ihre SIM-Karte wurde dauerhaft deaktiviert."\n" Wenden Sie sich an Ihren Mobilfunkanbieter, um eine andere SIM-Karte zu erhalten."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-Karte ist gesperrt."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-Karte ist gesperrt. PUK-Eingabe erforderlich."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-Karte wird entsperrt…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d von %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Widget hinzufügen"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Leer"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Entsperr-Bereich maximiert"</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Entsperr-Bereich mminimiert"</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Nutzerauswahl"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Mediensteuerelemente"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Neuordnung der Widgets gestartet"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Neuordnung der Widgets abgeschlossen"</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> gelöscht"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Entsperr-Bereich maximieren"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Entsperrung mit Fingerbewegung"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Entsperrung mit Muster"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Face Unlock"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Entsperrung mit PIN"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Entsperrung mit Passwort"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Bereich für Muster"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Bereich für Fingerbewegung"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Schaltfläche für vorherigen Titel"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Schaltfläche für nächsten Titel"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Schaltfläche für Pause"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Schaltfläche für Wiedergabe"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Schaltfläche für Stopp"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Abbrechen"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Löschen"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Fertig"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Modusänderung"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Umschalttaste"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Eingabetaste"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Entsperren"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Lautlos"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Ton ein"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Suche"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Zum <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> nach oben schieben"</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Zum <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> nach unten schieben"</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Zum <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> nach links schieben"</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Zum <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> nach rechts schieben"</string>
+ <string name="user_switched" msgid="3768006783166984410">"Aktueller Nutzer <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Notruf"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Muster vergessen"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Falsches Muster"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Falsches Passwort"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Falsche PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Versuchen Sie es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Muster zeichnen"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM-PIN eingeben"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"PIN eingeben"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Passwort eingeben"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Die SIM-Karte ist jetzt deaktiviert. Geben Sie den PUK-Code ein, um fortzufahren. Weitere Informationen erhalten Sie von Ihrem Mobilfunkanbieter."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Gewünschten PIN-Code eingeben"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Gewünschten PIN-Code bestätigen"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-Karte wird entsperrt…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Falscher PIN-Code"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Geben Sie eine 4- bis 8-stellige PIN ein."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Der PUK-Code muss mindestens 8 Ziffern betragen."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Geben Sie den richtigen PUK-Code ein. Bei wiederholten Versuchen wird die SIM-Karte dauerhaft deaktiviert."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-Codes stimmen nicht überein"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Zu viele Musterversuche"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Melden Sie sich zum Entsperren mit Ihrem Google-Konto an."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nutzername (E-Mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Passwort"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Anmelden"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ungültiger Nutzername oder ungültiges Passwort"</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nutzernamen oder Passwort vergessen?"\n"Besuchen Sie "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Konto wird geprüft…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Sie haben Ihre PIN <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben."\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben."\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. "\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Das Tablet wird nun auf die Werkseinstellungen zurückgesetzt."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Das Telefon wird nun auf die Werkseinstellungen zurückgesetzt."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Tablet mithilfe eines E-Mail-Kontos zu entsperren."\n\n" Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe eines E-Mail-Kontos zu entsperren."\n\n" Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Entfernen"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Schaltfläche für vorherigen Titel"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Schaltfläche für nächsten Titel"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Schaltfläche für Pause"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Schaltfläche für Wiedergabe"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Schaltfläche für Stopp"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Kein Dienst"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-el/activitystrings.xml b/packages/Keyguard/res/values-el/activitystrings.xml
new file mode 100644
index 0000000..3941f4f
--- /dev/null
+++ b/packages/Keyguard/res/values-el/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Χωρίς ασφάλεια"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Κωδικός πρόσβασης"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Μοτίβο"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"Κωδικός PIN κάρτας SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"Κωδικός PUK κάρτας SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Επιλογή γραφικού στοιχείου…"</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-el/strings.xml b/packages/Keyguard/res/values-el/strings.xml
new file mode 100644
index 0000000..4069b9e
--- /dev/null
+++ b/packages/Keyguard/res/values-el/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Πληκτρολογήστε τον κωδικό αριθμό PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Πληκτρολογήστε τον κωδικό PUK και τον νέο κωδικό PIN"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Κωδικός PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Νέος κωδικός PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Αγγίξτε για εισαγ. κωδ. πρόσβ."</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Πληκτρολογήστε τον κωδικό πρόσβασης για ξεκλείδωμα"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Πληκτρολογήστε τον αριθμό PIN για ξεκλείδωμα"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Εσφαλμένος κωδικός PIN."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Για ξεκλείδωμα, πατήστε το πλήκτρο Menu και, στη συνέχεια, το πλήκτρο 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Έγινε υπέρβαση του μέγιστου αριθμού προσπαθειών Face Unlock"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Φορτίστηκε"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Φόρτιση, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Συνδέστε τον φορτιστή."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Το δίκτυο κλειδώθηκε"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Δεν υπάρχει κάρτα SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Δεν υπάρχει κάρτα SIM στο tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Δεν υπάρχει κάρτα SIM στο τηλέφωνο."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Τοποθετήστε μια κάρτα SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Η κάρτα SIM δεν υπάρχει ή δεν είναι δυνατή η ανάγνωσή της. Τοποθετήστε μια κάρτα SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Η κάρτα SIM δεν μπορεί να χρησιμοποιηθεί."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Η κάρτα SIM έχει απενεργοποιηθεί οριστικά."\n" Επικοινωνήστε με τον παροχέα υπηρεσιών ασύρματου δικτύου για να λάβετε μια νέα κάρτα SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Η κάρτα SIM είναι κλειδωμένη."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Η κάρτα SIM είναι κλειδωμένη με κωδικό PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Ξεκλείδωμα κάρτας SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Γραφικό στοιχείο %2$d από %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Προσθήκη γραφικού στοιχείου"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Κενή"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Ανάπτυξη της περιοχής ξεκλειδώματος."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Σύμπτυξη της περιοχής ξεκλειδώματος."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Γραφικό στοιχείο <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Επιλογέας χρήστη"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Κατάσταση"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Φωτογραφική μηχανή"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Στοιχεία ελέγχου μέσων"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Έχει ξεκινήσει η αναδιάταξη των γραφικών στοιχείων."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Έχει ολοκληρωθεί η αναδιάταξη των γραφικών στοιχείων."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Το γραφικό στοιχείο <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> έχει διαγραφεί."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Ανάπτυξη περιοχής ξεκλειδώματος."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Ξεκλείδωμα ολίσθησης."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Ξεκλείδωμα μοτίβου."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Face unlock."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Ξεκλείδωμα κωδικού ασφαλείας"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Ξεκλείδωμα κωδικού πρόσβασης."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Περιοχή μοτίβου."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Περιοχή ολίσθησης"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Κουμπί προηγούμενου κομματιού"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Κουμπί επόμενου κομματιού"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Κουμπί παύσης"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Κουμπί αναπαραγωγής"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Κουμπί διακοπής"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ΑΒΓ"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Ακύρωση"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Διαγραφή"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Τέλος"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Αλλαγή τρόπου"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Ξεκλείδωμα"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Φωτογραφική μηχανή"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Αθόρυβο"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Ενεργοποίηση ήχου"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Αναζήτηση"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Κύλιση προς τα επάνω για <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Κύλιση προς τα κάτω για <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Κύλιση προς τα αριστερά για <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Κύλιση προς τα δεξιά για <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Τρέχων χρήστης <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Κλήσεις επείγουσας ανάγκης"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Ξεχάσατε το μοτίβο"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Εσφαλμένο μοτίβο"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Εσφαλμένος κωδικός πρόσβασης"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Εσφαλμένος κωδικός PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Δοκιμάστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Σχεδιάστε το μοτίβο σας"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Εισαγωγή PIN SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Πληκτρολογήστε το PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Εισαγάγετε κωδικό πρόσβασης"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Η κάρτα SIM είναι απενεργοποιημένη αυτή τη στιγμή. Εισαγάγετε τον κωδικό PUK για να συνεχίσετε. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας σας για λεπτομέρειες."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Εισαγάγετε τον απαιτούμενο κωδικό PIN"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Επιβεβαιώστε τον απαιτούμενο κωδικό PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Ξεκλείδωμα κάρτας SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Εσφαλμένος κωδικός PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Πληκτρολογήστε έναν αριθμό PIN που να αποτελείται από 4 έως 8 αριθμούς."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Ο κωδικός PUK θα πρέπει να περιέχει τουλάχιστον 8 αριθμούς."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Εισαγάγετε ξανά τον κωδικό PUK. Οι επαναλαμβανόμενες προσπάθειες θα απενεργοποιήσουν οριστικά την κάρτα SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Δεν υπάρχει αντιστοιχία των κωδικών PIN"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Πάρα πολλές προσπάθειες μοτίβου"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Για ξεκλείδωμα, συνδεθείτε με τον λογαριασμό σας Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Όνομα χρήστη (διεύθυνση ηλεκτρονικού ταχυδρομείου)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Κωδικός πρόσβασης"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Σύνδεση"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Μη έγκυρο όνομα χρήστη ή κωδικός πρόσβασης."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ξεχάσατε το όνομα χρήστη ή τον κωδικό πρόσβασής σας;"\n"Επισκεφτείτε τη διεύθυνση "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Έλεγχος λογαριασμού…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Έχετε πληκτρολογήσει εσφαλμένα τον κωδικό σας PIN <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Δοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Δοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Δοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλετπα."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες, το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το τηλέφωνο <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες, το τηλέφωνο θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER">%d</xliff:g> φορές. Το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το τηλέφωνο <xliff:g id="NUMBER">%d</xliff:g> φορές. Το τηλέφωνο θα επαναφερθεί στις εργοστασιακές ρυθμίσεις."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το tablet σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου."\n\n" Δοκιμάστε να συνδεθείτε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου."\n\n" Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Κατάργηση"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Κουμπί προηγούμενου κομματιού"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Κουμπί επόμενου κομματιού"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Κουμπί παύσης"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Κουμπί αναπαραγωγής"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Κουμπί διακοπής"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Καμία υπηρεσία."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-en-rGB/activitystrings.xml b/packages/Keyguard/res/values-en-rGB/activitystrings.xml
new file mode 100644
index 0000000..88e806e
--- /dev/null
+++ b/packages/Keyguard/res/values-en-rGB/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"No security"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Password"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Pattern"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM PIN"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Choose widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-en-rGB/strings.xml b/packages/Keyguard/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..d8b8df4
--- /dev/null
+++ b/packages/Keyguard/res/values-en-rGB/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Type PIN code"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Type PUK and new PIN code"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK code"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"New PIN Code"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Touch to type password"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Type password to unlock"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Type PIN to unlock"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Charged"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Charging, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Connect your charger."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Press Menu to unlock."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Network locked"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"No SIM card"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"No SIM card in tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"No SIM card in phone."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Insert a SIM card."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"The SIM card is missing or not readable. Insert a SIM card."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Unusable SIM card."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Your SIM card has been permanently disabled."\n" Contact your wireless service provider for another SIM card."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM card is locked."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM card is PUK-locked."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Unlocking SIM card…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d of %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Add widget"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Empty"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Unlock area expanded."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Unlock area collapsed."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> widget."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"User selector"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Camera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Media controls"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Widget reordering started."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Widget reordering ended."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> deleted."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Expand unlock area."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Slide unlock."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Pattern unlock."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Face unlock."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin unlock."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Password unlock."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Pattern area."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Slide area."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Previous track button"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Next track button"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Pause button"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Play button"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Stop button"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Done"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Mode change"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Unlock"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Camera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Silent"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Sound on"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Search"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Slide up for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Slide down for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Slide left for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Slide right for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Current user <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Emergency call"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Forgot Pattern"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Wrong Pattern"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Wrong Password"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Wrong PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Draw your pattern"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Enter SIM PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Enter PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Enter Password"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM is now disabled. Enter PUK code to continue. Contact carrier for details."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Enter desired PIN code"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirm desired PIN code"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Unlocking SIM card…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Incorrect PIN code."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Type a PIN that is 4 to 8 numbers."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK code should be 8 numbers or more."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Re-enter the correct PUK code. Repeated attempts will permanently disable the SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN codes do not match"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Too many pattern attempts"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"To unlock, sign in with your Google account."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Username (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Password"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Sign in"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Invalid username or password."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Forgot your username or password?"\n"Visit "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Checking account…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER">%d</xliff:g> times. The tablet will now be reset to factory default."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER">%d</xliff:g> times. The phone will now be reset to factory default."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using an email account."\n\n" Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account."\n\n" Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remove"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Previous track button"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Next track button"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Pause button"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Play button"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Stop button"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"No service."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-es-rUS/activitystrings.xml b/packages/Keyguard/res/values-es-rUS/activitystrings.xml
new file mode 100644
index 0000000..20117c4
--- /dev/null
+++ b/packages/Keyguard/res/values-es-rUS/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Sin seguridad"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Contraseña"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Patrón"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN de tarjeta SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK de tarjeta SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Elegir widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-es-rUS/strings.xml b/packages/Keyguard/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..f5d2986
--- /dev/null
+++ b/packages/Keyguard/res/values-es-rUS/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Ingresa el código PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Escribe el código PUK y un nuevo código PIN."</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Código PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nuevo código PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Toca para ingresar la contraseña"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Ingresar contraseña para desbloquear"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Ingresa el PIN para desbloquear"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, presiona el menú y luego 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Se superó el máximo de intentos permitido para el desbloqueo facial del dispositivo."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Cargada"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Conecta tu cargador."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Presiona Menú para desbloquear."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Bloqueada para la red"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"No se insertó ninguna tarjeta SIM."</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"No se insertó ninguna tarjeta SIM en la tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"No se insertó ninguna tarjeta SIM en el dispositivo."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Inserta una tarjeta SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Falta la tarjeta SIM o esta no se puede leer. Inserta una tarjeta SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Tarjeta SIM inutilizable"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Tu tarjeta SIM se inhabilitó de forma permanente."\n" Comunícate con tu proveedor de servicios inalámbricos para obtener otra tarjeta SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La tarjeta SIM está bloqueada."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La tarjeta SIM está bloqueada por código PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Desbloqueando tarjeta SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d"</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Agregar widget"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vacío"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Área desbloqueada expandida."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"El área desbloqueada se contrajo."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> widget"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Selector de usuarios"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Estado"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Cámara"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Controles de medios"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Se comenzaron a reordenar los widgets."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Se terminaron de reordenar los widgets."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> eliminado"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Expandir el área desbloqueada"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Desbloqueo por deslizamiento"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueo por patrón"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueo facial"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueo por PIN"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueo por contraseña"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área de patrón"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área de deslizamiento"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Botón de pista anterior"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Botón de pista siguiente"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Botón de pausa"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Botón de reproducción"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Botón de detención"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eliminar"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Listo"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Cambio de modo"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Mayúscula"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Ingresar"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Desbloquear"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Cámara"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Silencioso"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Sonido activado"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Buscar"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Desliza el dedo hacia arriba para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Desliza el dedo hacia abajo para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Desliza el dedo hacia la izquierda para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Desliza el dedo hacia la derecha para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Realizar llamada de emergencia"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"¿Olvidaste el patrón?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Patrón incorrecto"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Contraseña incorrecta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorrecto"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Vuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dibuja tu patrón."</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ingresa el PIN de la tarjeta SIM."</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ingresa el PIN."</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ingresa tu contraseña."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"La tarjeta SIM está inhabilitada. Para continuar, ingresa el código PUK. Si quieres obtener más información, ponte en contacto con el proveedor."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Ingresa el código PIN deseado"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirmar código PIN deseado"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando tarjeta SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorrecto"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Escribe un PIN que tenga de cuatro a ocho números."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"El código PUK debe tener ocho números como mínimo."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Vuelve a ingresar el código PUK correcto. Si ingresas un código incorrecto varias veces, se inhabilitará la tarjeta SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Los códigos PIN no coinciden."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Demasiados intentos incorrectos de ingresar el patrón"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear, accede con tu cuenta de Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nombre de usuario (correo)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Contraseña"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Acceder"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nombre de usuario o contraseña incorrectos"</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"¿Olvidaste tu nombre de usuario o contraseña?"\n"Accede a "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Comprobando la cuenta…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escribiste incorrectamente tu PIN <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escribiste incorrectamente tu contraseña <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Intentaste desbloquear la tablet <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica de la tablet y se pierdan todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica del dispositivo y se pierdan todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Intentaste desbloquear la tablet <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo lograste. Se restablecerán los valores predeterminados de fábrica de la tablet."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo lograste. Se restablecerán los valores predeterminados de fábrica del dispositivo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu tablet mediante el uso de una cuenta de correo."\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu dispositivo mediante el uso de una cuenta de correo."\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminar"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Botón de pista anterior"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Botón de pista siguiente"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Botón de pausa"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Botón de reproducción"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Botón de detención"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Sin servicio"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-es/activitystrings.xml b/packages/Keyguard/res/values-es/activitystrings.xml
new file mode 100644
index 0000000..34899cc
--- /dev/null
+++ b/packages/Keyguard/res/values-es/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Sin seguridad"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Contraseña"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Patrón"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN de tarjeta SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK de tarjeta SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Seleccionar widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-es/strings.xml b/packages/Keyguard/res/values-es/strings.xml
new file mode 100644
index 0000000..0fce71a
--- /dev/null
+++ b/packages/Keyguard/res/values-es/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Introduce el código PIN."</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Introduce el código PUK y un nuevo código PIN."</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Código PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nuevo código PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Toca para introducir contraseña"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Introduce la contraseña para desbloquear."</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Introduce el código PIN para desbloquear."</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear el teléfono, pulsa la tecla de menú y, a continuación, pulsa 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Se ha superado el número máximo de intentos de desbloqueo facial."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Cargado"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Conecta el cargador."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Ve al menú para desbloquear la pantalla."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Bloqueada para la red"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Falta la tarjeta SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"No se ha insertado ninguna tarjeta SIM en el tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"No se ha insertado ninguna tarjeta SIM en el teléfono."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Inserta una tarjeta SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Falta la tarjeta SIM o no se puede leer. Introduce una tarjeta SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Tarjeta SIM inutilizable"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Tu tarjeta SIM se ha inhabilitado permanentemente."\n" Para obtener otra, ponte en contacto con tu proveedor de servicios de telefonía."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La tarjeta SIM está bloqueada."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La tarjeta SIM está bloqueada con el código PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Desbloqueando tarjeta SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d"</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Añadir widget"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vacío"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Área de desbloqueo ampliada"</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Área de desbloqueo contraída"</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget de <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Selector de usuarios"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Estado"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Cámara"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Controles multimedia"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Se ha empezado a cambiar el orden de los widgets."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Se ha terminado de cambiar el orden de los widgets."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> eliminado"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Ampliar área de desbloqueo"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Desbloqueo deslizando el dedo"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueo por patrón"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueo facial"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueo por PIN"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueo por contraseña"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área de patrón"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área para deslizar"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Botón de canción anterior"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Botón de siguiente canción"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Botón de pausa"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Botón de reproducción"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Botón para detener la reproducción"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eliminar"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Listo"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Cambio de modo"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Mayús"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Intro"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Desbloquear"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Cámara"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Silencio"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Sonido activado"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Buscar"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Desliza el dedo hacia arriba para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Desliza el dedo hacia abajo para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Desliza el dedo hacia la izquierda para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Desliza el dedo hacia la derecha para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Llamada de emergencia"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"¿Has olvidado el patrón?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"El patrón es incorrecto"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Contraseña incorrecta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorrecto"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Inténtalo de nuevo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dibuja tu patrón de desbloqueo."</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduce el PIN de la tarjeta SIM."</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Introduce el PIN."</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Escribe tu contraseña."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"La tarjeta SIM está inhabilitada. Para continuar, introduce el código PUK. Si quieres obtener más información, ponte en contacto con el operador"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Introduce el código PIN deseado"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirma el código PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando tarjeta SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorrecto"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Introduce un código PIN con una longitud comprendida entre cuatro y ocho dígitos."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"El código PUK debe tener ocho números como mínimo."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Vuelve a introducir el código PUK correcto. Si introduces un código incorrecto varias veces, se inhabilitará la tarjeta SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Los códigos PIN no coinciden."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Demasiados intentos incorrectos de crear el patrón"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear el teléfono, inicia sesión con tu cuenta de Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nombre de usuario (correo electrónico)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Contraseña"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Iniciar sesión"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"El nombre de usuario o la contraseña no son válidos."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Si has olvidado tu nombre de usuario o tu contraseña,"\n"accede a la página "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Comprobando cuenta…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has introducido un código PIN incorrecto <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Inténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has introducido una contraseña incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Inténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar tu patrón de desbloqueo. "\n\n"Inténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Has intentado desbloquear el tablet <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo has conseguido. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo has conseguido. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Has intentado desbloquear el tablet <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo has conseguido. Se restablecerán los datos de fábrica del dispositivo."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo has conseguido. Se restablecerán los datos de fábrica del dispositivo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el tablet."\n\n" Inténtalo de nuevo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el teléfono."\n\n" Inténtalo de nuevo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminar"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Botón de canción anterior"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Botón de siguiente canción"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Botón de pausa"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Botón de reproducción"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Botón para detener la reproducción"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Sin servicio"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-et/activitystrings.xml b/packages/Keyguard/res/values-et/activitystrings.xml
new file mode 100644
index 0000000..eb71afc
--- /dev/null
+++ b/packages/Keyguard/res/values-et/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Turvamata"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN-kood"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Parool"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Muster"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM-i PIN-kood"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM-i PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Vidina valimine ..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-et/strings.xml b/packages/Keyguard/res/values-et/strings.xml
new file mode 100644
index 0000000..d856c96
--- /dev/null
+++ b/packages/Keyguard/res/values-et/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Sisestage PIN-kood"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Sisestage PUK-kood ja uus PIN-kood"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-kood"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Uus PIN-kood"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Puudutage parooli sisestamiseks"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Avamiseks sisestage parool"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Avamiseks sisestage PIN-kood"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Vale PIN-kood."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Avamiseks vajutage menüüklahvi, seejärel klahvi 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maksimaalne teenusega Face Unlock avamise katsete arv on ületatud"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Laetud"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Laadimine, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Ühendage laadija."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Avamiseks vajutage menüüklahvi."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Võrk on suletud"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"SIM-kaarti pole"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Tahvelarvutis pole SIM-kaarti."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Telefonis pole SIM-kaarti."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Sisestage SIM-kaart."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-kaart puudub või on loetamatu. Sisestage SIM-kaart."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Kasutamiskõlbmatu SIM-kaart."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM-kaart on jäädavalt keelatud."\n" Uue SIM-kaardi saamiseks võtke ühendust oma mobiilsideoperaatoriga."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kaart on lukus."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kaart on PUK-lukus."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-kaardi avamine ..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Vidin %2$d/%3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Vidina lisamine."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tühi"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Avamisala on laiendatud."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Avamisala on ahendatud."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Vidin <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Kasutaja valija"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Olek"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kaamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Meedia juhtnupud"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Vidina ümberkorraldamine algas."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Vidina ümberkorraldamine lõppes."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Vidin <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> on kustutatud."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Avamisala laiendamine."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Lohistamisega avamine."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Mustriga avamine."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Näoga avamine."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN-koodiga avamine."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Parooliga avamine."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Mustri ala."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Lohistamisala."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Nupp Eelmine lugu"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Nupp Järgmine lugu"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Nupp Peata"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Nupp Esita"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Nupp Peata"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Tühista"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Kustuta"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Valmis"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Režiimi muutmine"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Tõstuklahv"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Sisestusklahv"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Luku avamine"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kaamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Hääletu"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Heli on sees"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Otsing"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Lohistage üles: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Lohistage alla: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Lohistage vasakule: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Lohistage paremale: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Praegune kasutaja <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Hädaabikõne"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Unustasin mustri"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Vale muster"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Vale parool"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Vale PIN-kood"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Proovige uuesti <xliff:g id="NUMBER">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Joonistage oma muster"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Sisestage SIM-i PIN-kood"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Sisestage PIN-kood"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Sisestage parool"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM on nüüd keelatud. Jätkamiseks sisestage PUK-kood. Üksikasju küsige operaatorilt."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Sisestage soovitud PIN-kood"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Kinnitage soovitud PIN-kood"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-kaardi avamine ..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Vale PIN-kood."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Sisestage 4–8-numbriline PIN-kood."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK-koodi pikkus peab olema vähemalt 8 numbrit."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Sisestage uuesti õige PUK-kood. Korduvkatsete korral keelatakse SIM jäädavalt."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-koodid ei ole vastavuses"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Liiga palju mustrikatseid"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Avamiseks logige sisse oma Google\'i kontoga."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Kasutajanimi (e-post)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Parool"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Logi sisse"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Vale kasutajanimi või parool."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Kas unustasite kasutajanime või parooli?"\n"Külastage aadressi "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Konto kontrollimine ..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olete PIN-koodi <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud."\n\n"Proovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olete parooli <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud. "\n\n"Proovige uuesti <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti."\n\n"Proovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Olete üritanud <xliff:g id="NUMBER_0">%d</xliff:g> korda tahvelarvutit valesti avada. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset lähtestatakse tahvelarvuti tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Olete üritanud <xliff:g id="NUMBER_0">%d</xliff:g> korda telefoni valesti avada. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset lähtestatakse telefon tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Olete püüdnud tahvelarvutit <xliff:g id="NUMBER">%d</xliff:g> korda valesti avada. Tahvelarvuti lähtestatakse nüüd tehase vaikeseadetele."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Olete püüdnud telefoni <xliff:g id="NUMBER">%d</xliff:g> korda valesti avada. Telefon lähtestatakse nüüd tehase vaikeseadetele."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset palutakse teil tahvelarvuti avada meilikontoga."\n\n" Proovige uuesti <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset palutakse teil telefon avada meilikontoga."\n\n" Proovige uuesti <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eemalda"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Nupp Eelmine lugu"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Nupp Järgmine lugu"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Nupp Peata"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Nupp Esita"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Nupp Peata"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Teenus puudub."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-fa/activitystrings.xml b/packages/Keyguard/res/values-fa/activitystrings.xml
new file mode 100644
index 0000000..735af8d
--- /dev/null
+++ b/packages/Keyguard/res/values-fa/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"عدم وجود امنیت"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"پین"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"گذرواژه"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"الگو"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"پین سیم کارت"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK سیم کارت"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"انتخاب ابزارک..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-fa/strings.xml b/packages/Keyguard/res/values-fa/strings.xml
new file mode 100644
index 0000000..61ea08c
--- /dev/null
+++ b/packages/Keyguard/res/values-fa/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"پین کد را وارد کنید"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"PUK و پین کد جدید را تایپ کنید"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"کد PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"پین کد جدید"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"برای تایپ گذرواژه لمس کنید"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"برای بازکردن قفل، گذرواژه را وارد کنید"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"برای بازکردن قفل، پین را تایپ کنید"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"پین کد اشتباه است."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"برای بازگشایی قفل، منو را فشار دهید و سپس 0 را فشار دهید."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"دفعات تلاش برای Face Unlock از حداکثر مجاز بیشتر شد"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"شارژ شد"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"شارژ، <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"شارژر خود را وصل کنید."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"برای بازگشایی قفل روی منو فشار دهید."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"شبکه قفل شد"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"سیم کارت موجود نیست"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"سیم کارتی در رایانه لوحی نیست."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"سیم کارت در تلفن نیست."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"سیم کارت را وارد کنید."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"سیم کارت موجود نیست یا قابل خواندن نیست. یک سیم کارت وارد کنید."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"سیم کارت غیرقابل استفاده است."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"سیم کارت شما به طور دائم غیر فعال شده است. "\n"برای داشتن سیم کارت دیگر با ارائهدهنده سرویس بیسیم خود تماس بگیرید."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"سیم کارت قفل شد."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"سیم کارت با PUK قفل شده است."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"درحال بازگشایی قفل سیم کارت..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. ابزارک %2$d از %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"ابزارک اضافه کنید."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"خالی"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"منطقه بازگشایی گسترده شد."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"منطقه بازگشایی کوچک شد."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"ابزارک <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"انتخابگر کاربر"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"وضعیت"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"دوربین"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"کنترلهای رسانه"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"مرتب سازی مجدد ابزارک آغاز شد."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"مرتبسازی مجدد ابزارک به پایان رسید."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"ابزارک <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> حذف شد."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"گسترده کردن منطقه بازگشایی شده."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"باز کردن قفل با کشیدن انگشت روی صفحه."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"باز کردن قفل با الگو."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"باز کردن قفل با چهره."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"باز کردن قفل با پین."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"باز کردن قفل با گذرواژه."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"ناحیه الگو."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"ناحیه کشیدن انگشت روی صفحه."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"دکمه تراک قبلی"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"دکمه تراک بعدی"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"دکمه توقف موقت"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"دکمه پخش"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"دکمه توقف"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"لغو"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"انجام شد"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"تغییر حالت"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"بازکردن قفل"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"دوربین"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"ساکت"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"صدا روشن"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"جستجو"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"لغزاندن به بالا برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"لغزاندن به پایین برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"لغزاندن به چپ برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"لغزاندن به راست برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"کاربر کنونی <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"تماس اضطراری"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"الگو را فراموش کردهاید"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"الگوی اشتباه"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"گذرواژه اشتباه"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"پین اشتباه"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"پس از <xliff:g id="NUMBER">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"الگوی خود را رسم کنید"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"پین سیم کارت را وارد کنید"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"پین را وارد کنید"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"گذرواژه را وارد کنید"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"اکنون سیم کارت غیرفعال است. پین کد را برای ادامه وارد کنید. برای جزئیات با شرکت مخابراتی خود تماس بگیرید."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"پین کد دلخواه را وارد کنید"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"تأیید پین کد دلخواه"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"بازگشایی قفل سیم کارت..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"پین کد اشتباه است."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"یک پین ۴ تا ۸ رقمی را تایپ کنید."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"پین کد باید ۸ عدد یا بیشتر باشد."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"پین کد صحیح را دوباره وارد کنید. تلاشهای مکرر بهطور دائم سیم کارت را غیرفعال خواهد کرد."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"پین کدها منطبق نیستند"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"تلاشهای زیادی برای کشیدن الگو صورت گرفته است"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"برای بازگشایی قفل، با حساب Google خود وارد سیستم شوید."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"نام کاربری (ایمیل)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"گذرواژه"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"ورود به سیستم"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"نام کاربری یا گذرواژه نامعتبر."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"نام کاربری یا گذرواژه خود را فراموش کردید؟"\n"از "<b>"google.com/accounts/recovery"</b>" بازدید کنید."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"درحال بررسی حساب..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"پین خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردید. "\n\n"پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"گذرواژه خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردید. "\n\n"پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدید. "\n\n"لطفاً پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"شما به اشتباه <xliff:g id="NUMBER_0">%d</xliff:g> بار اقدام به باز کردن قفل رایانه لوحی کردهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، رایانهٔ لوحی به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"شما به اشتباه <xliff:g id="NUMBER_0">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، تلفن به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل رایانه لوحی کردهاید. رایانه لوحی اکنون به پیشفرض کارخانه بازنشانی میشود."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. این تلفن اکنون به پیشفرض کارخانه بازنشانی میشود."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدهاید. بعد از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل رایانه لوحی خود را باز کنید."\n\n" لطفاً پس از <xliff:g id="NUMBER_2">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل تلفن خود را باز کنید."\n\n" لطفاً پس از <xliff:g id="NUMBER_2">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"حذف"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"دکمه تراک قبلی"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"دکمه تراک بعدی"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"دکمه توقف موقت"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"دکمه پخش"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"دکمه توقف"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"خدماتی وجود ندارد."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-fi/activitystrings.xml b/packages/Keyguard/res/values-fi/activitystrings.xml
new file mode 100644
index 0000000..6e0a5a9
--- /dev/null
+++ b/packages/Keyguard/res/values-fi/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Ei suojausta"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN-koodi"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Salasana"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Kuvio"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM-kortin PIN-koodi"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM-kortin PUK-koodi"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Valitse widget…"</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-fi/strings.xml b/packages/Keyguard/res/values-fi/strings.xml
new file mode 100644
index 0000000..8064993
--- /dev/null
+++ b/packages/Keyguard/res/values-fi/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Anna PIN-koodi"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Anna PUK-koodi ja uusi PIN-koodi"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-koodi"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Uusi PIN-koodi"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Kosketa ja anna salasana"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Poista lukitus antamalla salasana"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Poista lukitus antamalla PIN-koodi"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN-koodi väärin."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Poista lukitus painamalla Valikko-painiketta ja 0-näppäintä."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Face Unlock -yrityksiä tehty suurin sallittu määrä."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Täynnä"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Ladataan (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Kytke laturi."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Poista lukitus painamalla Valikko-painiketta."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Verkko lukittu"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Ei SIM-korttia"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Tablet-laitteessa ei ole SIM-korttia."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Puhelimessa ei ole SIM-korttia."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Aseta SIM-kortti."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-korttia ei löydy tai sitä ei voi lukea. Aseta SIM-kortti."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM-kortti ei kelpaa."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM-kortti on poistettu pysyvästi käytöstä."\n" Ota yhteyttä operaattoriisi ja hanki uusi SIM-kortti."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kortti on lukittu."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kortti on PUK-lukittu."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-kortin lukitusta poistetaan…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d/%3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Lisää widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tyhjä"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Lukituksen poiston alue laajennettu."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Lukituksen poiston alue tiivistetty."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>-widget."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Käyttäjävalitsin"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Tila"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Mediaohjaimet"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Widgetien järjestely aloitettu."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Widgetien järjestely päättyi."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> poistettu."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Laajenna lukituksen poiston aluetta."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Lukituksen poisto liu\'uttamalla."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Lukituksen poisto salasanalla."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Face Unlock"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Lukituksen poisto PIN-koodilla."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Lukituksen poisto salasanalla."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Kuvioalue."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Liu\'utusalue."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Edellinen kappale -painike"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Seuraava kappale -painike"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Tauko-painike"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Toista-painike"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Keskeytä-painike"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Peruuta"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Poista"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Valmis"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Tilan muutos"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Poista lukitus"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Äänetön"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Ääni käytössä"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Haku"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Liu\'uta ylös ja <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Liu\'uta alas ja <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Liu\'uta vasemmalle ja <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Liu\'uta oikealle ja <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Nykyinen käyttäjä: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Hätäpuhelu"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Unohtunut kuvio"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Väärä kuvio"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Väärä salasana"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Väärä PIN-koodi"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Yritä uudelleen <xliff:g id="NUMBER">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Piirrä kuvio"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Anna SIM-kortin PIN-koodi"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Anna PIN-koodi"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Anna salasana"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM-kortti on nyt poistettu käytöstä. Jatka antamalla PUK-koodi. Saat lisätietoja ottamalla yhteyttä operaattoriin."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Anna haluamasi PIN-koodi"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Vahvista haluamasi PIN-koodi"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-kortin lukitusta poistetaan…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Virheellinen PIN-koodi."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Anna 4–8-numeroinen PIN-koodi."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK-koodissa tulee olla vähintään 8 numeroa."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Anna uudelleen oikea PUK-koodi. Jos teet liian monta yritystä, SIM-kortti poistetaan käytöstä pysyvästi."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-koodit eivät täsmää"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Liikaa kuvionpiirtoyrityksiä"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Poista lukitus kirjautumalla sisään Google-tililläsi."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Käyttäjänimi (sähköposti)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Salasana"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Kirjaudu sisään"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Virheellinen käyttäjänimi tai salasana."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Unohditko käyttäjänimesi tai salasanasi?"\n"Käy osoitteessa "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Tarkistetaan tiliä..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olet kirjoittanut PIN-koodin väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. "\n\n"Yritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olet kirjoittanut salasanan väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. "\n\n"Yritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. "\n\n"Yritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%d</xliff:g> epäonnistunutta yritystä, tablet-laitteeseen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%d</xliff:g> epäonnistunutta yritystä, puhelimeen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER">%d</xliff:g> kertaa. Laitteeseen palautetaan nyt tehdasasetukset."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER">%d</xliff:g> kertaa. Puhelimeen palautetaan nyt tehdasasetukset."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan tablet-laitteesi lukitus sähköpostitilin avulla."\n\n" Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan puhelimesi lukitus sähköpostitilin avulla."\n\n" Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Poista"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Edellinen kappale -painike"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Seuraava kappale -painike"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Tauko-painike"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Toista-painike"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Keskeytä-painike"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Ei yhteyttä."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-fr/activitystrings.xml b/packages/Keyguard/res/values-fr/activitystrings.xml
new file mode 100644
index 0000000..dc79842
--- /dev/null
+++ b/packages/Keyguard/res/values-fr/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Aucune sécurité"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"Code PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Mot de passe"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Schéma"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"Code PIN de la carte SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"Clé PUK de la carte SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Sélectionner un widget"</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-fr/strings.xml b/packages/Keyguard/res/values-fr/strings.xml
new file mode 100644
index 0000000..ee2f07b
--- /dev/null
+++ b/packages/Keyguard/res/values-fr/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Saisissez le code PIN."</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Saisissez la clé PUK et le nouveau code PIN."</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Code PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nouveau code PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Appuyez pour saisir mot passe"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Saisissez le mot de passe pour déverrouiller le clavier."</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Saisissez le code PIN pour déverrouiller le clavier."</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Le code PIN est erroné."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Pour déverrouiller le clavier, appuyez sur \"Menu\" puis sur 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Nombre maximal autorisé de tentatives Face Unlock atteint."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Chargé"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"En charge (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Branchez votre chargeur."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Réseau verrouillé"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Aucune carte SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Aucune carte SIM n\'est insérée dans la tablette."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Aucune carte SIM n\'est insérée dans le téléphone."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Insérez une carte SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Carte SIM absente ou illisible. Veuillez insérer une carte SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Carte SIM inutilisable."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Votre carte SIM a été définitivement désactivée."\n" Veuillez contacter votre opérateur de téléphonie mobile pour en obtenir une autre."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La carte SIM est verrouillée."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La carte SIM est verrouillée par clé PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Déverrouillage de la carte SIM en cours…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d sur %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Ajouter un widget"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vide"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Zone de déverrouillage développée."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Zone de déverrouillage réduite."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Sélecteur d\'utilisateur"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"État"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Caméra"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Commandes multimédias"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Début de la réorganisation des widgets"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Réorganisation des widgets terminée."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Le widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> a été supprimé."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Développer la zone de déverrouillage"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Déverrouillage en faisant glisser votre doigt sur l\'écran"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Déverrouillage par schéma"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Déverrouillage par reconnaissance faciale"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Déverrouillage par code PIN"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Déverrouillage par mot de passe"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Zone du schéma"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Zone où faire glisser votre doigt sur l\'écran"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Bouton pour revenir au titre précédent"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Bouton pour atteindre le titre suivant"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Bouton de pause"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Bouton de lecture"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Bouton d\'arrêt"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuler"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Supprimer"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Terminé"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Changement de mode"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Maj"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Entrée"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Déverrouiller"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Appareil photo"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Mode silencieux"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Son activé"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Rechercher"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Faites glisser vers le haut pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Faites glisser vers le bas pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Faites glisser vers la gauche pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Faites glisser vers la droite pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Utilisateur actuel : <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Appel d\'urgence"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"J\'ai oublié le schéma"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Schéma incorrect."</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Mot de passe incorrect."</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Code PIN incorrect."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dessinez votre schéma."</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Saisissez le code PIN de la carte SIM."</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Saisissez le code PIN."</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Saisissez votre mot de passe."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"La carte SIM est maintenant désactivée. Saisissez le code PUK pour continuer. Contactez votre opérateur pour en savoir plus."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Saisir le code PIN souhaité"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirmer le code PIN souhaité"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Déblocage de la carte SIM en cours…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Le code PIN est erroné."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Veuillez saisir un code PIN comprenant entre quatre et huit chiffres."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Le code PUK doit contenir au moins 8 chiffres."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Veuillez saisir de nouveau le code PUK correct. Des tentatives répétées désactivent définitivement la carte SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Les codes PIN ne correspondent pas."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Trop de tentatives."</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Pour déverrouiller le téléphone, veuillez vous connecter avec votre compte Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nom d\'utilisateur (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Mot de passe"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Connexion"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nom d\'utilisateur ou mot de passe non valide."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Vous avez oublié votre nom d\'utilisateur ou votre mot de passe ?"\n"Rendez-vous sur la page "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Vérification du compte en cours…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Vous avez saisi un code PIN incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises."\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller votre tablette à l\'aide d\'un compte de messagerie électronique."\n\n" Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique."\n\n" Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Supprimer"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Bouton pour revenir au titre précédent"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Bouton pour atteindre le titre suivant"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Bouton de pause"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Bouton de lecture"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Bouton d\'arrêt"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Aucun service"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-hi/activitystrings.xml b/packages/Keyguard/res/values-hi/activitystrings.xml
new file mode 100644
index 0000000..4b0a082
--- /dev/null
+++ b/packages/Keyguard/res/values-hi/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"कोई सुरक्षा नहीं"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"पासवर्ड"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"प्रतिमान"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM पिन"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"विजेट चुनें..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-hi/strings.xml b/packages/Keyguard/res/values-hi/strings.xml
new file mode 100644
index 0000000..09092f4
--- /dev/null
+++ b/packages/Keyguard/res/values-hi/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"पिन कोड लिखें"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"PUK और नया पिन कोड लिखें"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK कोड"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"नया पिन कोड"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"पासवर्ड लिखने के लिए स्पर्श करें"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"अनलॉक करने के लिए पासवर्ड लिखें"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"अनलॉक करने के लिए पिन लिखें"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"गलत पिन कोड."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"अनलॉक करने के लिए, मेनू दबाएं और फिर 0 दबाएं."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"फेस अनलॉक के अधिकतम प्रयासों की सीमा पार हो गई"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"चार्ज हो गई"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"चार्ज हो रही है, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"अपना चार्जर कनेक्ट करें."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"अनलॉक करने के लिए मेनू दबाएं."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"नेटवर्क लॉक किया गया"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"कोई SIM कार्ड नहीं है"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"टेबलेट में कोई SIM कार्ड नहीं है."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"फ़ोन में कोई SIM कार्ड नहीं है."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"SIM कार्ड डालें."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM कार्ड गुम है या पढ़ने योग्य नहीं है. SIM कार्ड डालें."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"अनुपयोगी SIM कार्ड."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"आपका SIM कार्ड स्थायी रूप से अक्षम कर दिया गया है."\n" दूसरे SIM कार्ड के लिए अपने वायरलेस सेवा प्रदाता से संपर्क करें."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"सिम कार्ड लॉक है."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM कार्ड PUK द्वारा लॉक किया हुआ है."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM कार्ड अनलॉक हो रहा है…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. %3$d विजेट में से %2$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"विजेट जोड़ें"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"रिक्त"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"अनलॉक क्षेत्र को विस्तृत कर दिया गया."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"अनलॉक क्षेत्र को संक्षिप्त कर दिया गया."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> विजेट."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"उपयोगकर्ता चयनकर्ता"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"स्थिति"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"कैमरा"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"मीडिया नियंत्रण"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"विजेट पुनः क्रमित करना प्रारंभ."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"विजेट पुनः क्रमित करना समाप्त."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"विजेट <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> को हटा दिया गया."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"अनलॉक क्षेत्र विस्तृत करें."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"स्लाइड अनलॉक."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"प्रतिमान अनलॉक."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"फेस अनलॉक."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"पिन अनलॉक."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"पासवर्ड अनलॉक."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"प्रतिमान क्षेत्र."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"स्लाइड क्षेत्र."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"पिछला ट्रैक बटन"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"अगला ट्रैक बटन"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"पॉज़ करें बटन"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"चलाएं बटन"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"रोकें बटन"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"रद्द करें"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"हटाएं"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"पूर्ण"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Mode change"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"अनलॉक करें"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"कैमरा"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"मौन"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"ध्वनि चालू करें"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"खोजें"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> के लिए ऊपर स्लाइड करें."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> के लिए नीचे स्लाइड करें."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> के लिए बाएं स्लाइड करें."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> के लिए दाएं स्लाइड करें."</string>
+ <string name="user_switched" msgid="3768006783166984410">"वर्तमान उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"आपातकालीन कॉल"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"प्रतिमान भूल गए"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"गलत प्रतिमान"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"गलत पासवर्ड"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"गलत PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"अपना प्रतिमान आरेखित करें"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"सिम PIN डालें"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"PIN डालें"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"पासवर्ड डालें"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"सिम अब अक्षम हो गई है. जारी रखने के लिए PUK कोड डालें. विवरण के लिए कैरियर से संपर्क करें."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"इच्छित पिन कोड डालें"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"इच्छित पिन कोड की पुष्टि करें"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM कार्ड अनलॉक कर रहा है…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"गलत PIN कोड."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"ऐसा PIN लिखें, जो 4 से 8 अंकों का हो."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK कोड 8 या अधिक संख्या वाला होना चाहिए."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"सही PUK कोड पुन: डालें. बार-बार प्रयास करने से सिम स्थायी रूप से अक्षम हो जाएगी."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"पिन कोड का मिलान नहीं होता"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"बहुत अधिक प्रतिमान प्रयास"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"अनलॉक करने के लिए, अपने Google खाते से साइन इन करें."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"उपयोगकर्ता नाम (ईमेल)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"पासवर्ड"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"साइन इन करें"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"अमान्य उपयोगकर्ता नाम या पासवर्ड."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"अपना उपयोगकर्ता नाम या पासवर्ड भूल गए?"\n" "<b>"google.com/accounts/recovery"</b>" पर जाएं."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"खाते की जांच की जा रही है…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आप अपना PIN <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिख चुके हैं. "\n\n" <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिख चुके हैं. "\n\n" <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपने अपना अनलॉक प्रतिमान <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से आरेखित किया है. "\n\n" <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"आप टेबलेट को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, टेबलेट फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"आप फ़ोन को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, फ़ोन फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"आप टेबलेट को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. टेबलेट अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"आप फ़ोन को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. फ़ोन अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"आपने अपने अनलॉक प्रतिमान को <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से आरेखित किया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने टेबलेट को किसी ईमेल खाते के उपयोग से अनलॉक करने के लिए कहा जाएगा."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"आपने अपने अनलॉक प्रतिमान को <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से आरेखित किया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने फ़ोन को किसी ईमेल खाते का उपयोग करके अनलॉक करने के लिए कहा जाएगा."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"निकालें"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"पिछला ट्रैक बटन"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"अगला ट्रैक बटन"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"पॉज़ करें बटन"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"चलाएं बटन"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"रोकें बटन"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"कोई सेवा नहीं."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-hr/activitystrings.xml b/packages/Keyguard/res/values-hr/activitystrings.xml
new file mode 100644
index 0000000..d2b8e92
--- /dev/null
+++ b/packages/Keyguard/res/values-hr/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Nema zaštite"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Zaporka"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Uzorak"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN za SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK za SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Odaberite widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-hr/strings.xml b/packages/Keyguard/res/values-hr/strings.xml
new file mode 100644
index 0000000..232b2ca
--- /dev/null
+++ b/packages/Keyguard/res/values-hr/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Unesite PIN kôd"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Unesite PUK i novi PIN kôd"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK kôd"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Novi PIN kôd"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Dodirnite za tipkanje zaporke"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Unesite zaporku za otključavanje"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Unesite PIN za otključavanje"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Netočan PIN kôd."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Za otključavanje pritisnite Izbornik pa 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Premašen je maksimalni broj Otključavanja licem"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Napunjeno"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Puni se, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Priključite punjač."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Pritisnite Izbornik za otključavanje."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Mreža je zaključana"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Nema SIM kartice"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"U tabletnom uređaju nema SIM kartice."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"U telefonu nema SIM kartice."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Umetnite SIM karticu."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM kartica nedostaje ili nije čitljiva. Umetnite SIM karticu."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Neupotrebljiva SIM kartica."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Vaša SIM kartica trajno je onemogućena."\n" Obratite se svom pružatelju bežičnih usluga da biste dobili drugu SIM karticu."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kartica je zaključana."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kartica zaključana je PUK-om."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Otključavanje SIM kartice…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d od %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Dodavanje widgeta."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Prazno"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Područje za otključavanje prošireno je."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Područje za otključavanje sažeto je."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> widget."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Birač korisnika"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Fotoaparat"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Nadzor medija"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Pokrenuta je promjena redoslijeda widgeta."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Završena je promjena redoslijeda widgeta."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> izbrisan je."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Proširivanje područja za otključavanje."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Otključavanje klizanjem."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Uzorak za otključavanje."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Otključavanje licem."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Otključavanje PIN-om."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Otključavanje zaporkom."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Područje uzorka."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Područje klizanja."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Gumb Prethodni zapis"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Gumb Sljedeći zapis"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Gumb Pauza"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Gumb Reprodukcija"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Gumb Zaustavi"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Odustani"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Izbriši"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Gotovo"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Promjena načina"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Otključaj"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Fotoaparat"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Bešumno"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Zvuk je uključen"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Pretraživanje"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Kliznite prema gore za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Kliznite prema dolje za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Kliznite lijevo za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Kliznite desno za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Trenutačni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Hitan poziv"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Zaboravili ste obrazac"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Pogrešan obrazac"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Pogrešna zaporka"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Pogrešan PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Iscrtajte svoj obrazac"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Unesite PIN za SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Unesite PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Unesite zaporku"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM je sad onemogućen. Unesite PUK kôd da biste nastavili. Kontaktirajte operatera za pojedinosti."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Upišite željeni PIN kôd"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potvrdite željeni PIN kôd"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Otključavanje SIM kartice…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Netočan PIN kôd."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Unesite PIN koji ima od 4 do 8 brojeva."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK kôd treba imati 8 brojeva ili više."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Ponovo unesite ispravan PUK kôd. Ponovljeni pokušaji trajno će onemogućiti SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN kodovi nisu jednaki"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Previše pokušaja iscrtavanja obrasca"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Za otključavanje prijavite se Google računom."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Korisničko ime (e-pošta)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Zaporka"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prijava"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nevažeće korisničko ime ili zaporka."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zaboravili ste korisničko ime ili zaporku?"\n"Posjetite "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Provjera računa..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Netočno ste napisali PIN <xliff:g id="NUMBER_0">%d</xliff:g> puta. "\n\n"Pokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Netočno ste napisali zaporku <xliff:g id="NUMBER_0">%d</xliff:g> puta. "\n\n"Pokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. "\n\n"Pokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Netočno ste pokušali otključati tabletno računalo <xliff:g id="NUMBER_0">%d</xliff:g> puta. Ono će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Netočno ste pokušali otključati telefon <xliff:g id="NUMBER_0">%d</xliff:g> puta. On će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Netočno ste pokušali otključati tabletno računalo <xliff:g id="NUMBER">%d</xliff:g> puta. Sada će se vratiti na tvorničke postavke."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Netočno ste pokušali otključati telefon <xliff:g id="NUMBER">%d</xliff:g> puta. Sada će se vratiti na tvorničke postavke."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> morat ćete otključati tabletno računalo pomoću računa e-pošte."\n\n" Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> morat ćete otključati telefon pomoću računa e-pošte."\n\n" Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ukloni"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Gumb Prethodni zapis"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Gumb Sljedeći zapis"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Gumb Pauza"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Gumb Reprodukcija"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Gumb Zaustavi"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Nema usluge."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-hu/activitystrings.xml b/packages/Keyguard/res/values-hu/activitystrings.xml
new file mode 100644
index 0000000..30d2951
--- /dev/null
+++ b/packages/Keyguard/res/values-hu/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Nincs védelem"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN kód"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Jelszó"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Minta"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM kártya PIN kódja"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM kártya PUK kódja"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Modul kiválasztása..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-hu/strings.xml b/packages/Keyguard/res/values-hu/strings.xml
new file mode 100644
index 0000000..df9a745
--- /dev/null
+++ b/packages/Keyguard/res/values-hu/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Írja be a PIN kódot"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Írja be a PUK kódot, majd az új PIN kódot"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK kód"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Új PIN kód"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Érintsen jelszó megadásához"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"A feloldáshoz írja be a jelszót"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Feloldáshoz írja be a PIN kódot"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Helytelen PIN kód."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"A feloldáshoz nyomja meg a Menü, majd a 0 gombot."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Elérte az arcalapú feloldási kísérletek maximális számát"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Feltöltve"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Töltés (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Csatlakoztassa a töltőt."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"A feloldáshoz nyomja meg a Menü gombot."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"A hálózat lezárva"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Nincs SIM kártya."</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Nincs SIM kártya a táblagépben."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Nincs SIM kártya a telefonban."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Helyezzen be egy SIM kártyát."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"A SIM kártya hiányzik vagy nem olvasható. Helyezzen be egy SIM kártyát."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"A SIM kártya nem használható."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM kártyája véglegesen le van tiltva."\n" Forduljon a vezeték nélküli szolgáltatójához másik SIM kártya beszerzése érdekében."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"A SIM kártya le van zárva."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"A SIM kártya le van zárva a PUK kóddal."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM kártya feloldása..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Modul %3$d/%2$d"</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Modul hozzáadása."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Üres"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Feloldási terület kiterjesztve."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Feloldási terület összecsukva."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> modul."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Felhasználóválasztó"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Állapot"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Médiaelemek vezérlője"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"A modulátrendezés elkezdődött."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"A modulátrendezés véget ért."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> modul törölve."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"A feloldási terület kiterjesztése."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Feloldás csúsztatással"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Feloldás mintával"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Arcalapú feloldás"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Feloldás PIN kóddal"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Feloldás jelszóval"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Mintaterület"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Csúsztatási terület"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Előző szám gomb"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Következő szám gomb"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Szünet gomb"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Lejátszás gomb"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Leállítás gomb"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Mégse"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Kész"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Mód váltása"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Feloldás"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Némítás"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Hang bekapcsolása"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Keresés"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"A(z) <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> művelethez csúsztassa felfelé."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"A(z) <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> művelethez csúsztassa lefelé."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"A(z) <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> művelethez csúsztassa balra."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"A(z) <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> művelethez csúsztassa jobbra."</string>
+ <string name="user_switched" msgid="3768006783166984410">"<xliff:g id="NAME">%1$s</xliff:g> az aktuális felhasználó."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Segélyhívás"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Elfelejtett minta"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Helytelen minta"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Helytelen jelszó"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Helytelen PIN kód"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Próbálkozzon újra <xliff:g id="NUMBER">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Rajzolja le a mintát"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Adja meg a SIM kártya PIN kódját"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Adja meg a PIN kódot"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Írja be a jelszót"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"A SIM kártya le van tiltva. A folytatáshoz adja meg a PUK kódot. A részletekért vegye fel a kapcsolatot szolgáltatójával."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Kívánt PIN kód megadása"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Kívánt PIN kód megerősítése"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM kártya feloldása..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Helytelen PIN kód."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4–8 számjegyű PIN kódot írjon be."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"A PUK kód legalább 8 számjegyből kell, hogy álljon."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Adja meg újra a helyes PUK kódot. Az ismételt próbálkozással véglegesen letiltja a SIM kártyát."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"A PIN kódok nem egyeznek."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Túl sok mintarajzolási próbálkozás"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"A feloldáshoz jelentkezzen be Google Fiókjával."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Felhasználónév (e-mail cím)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Jelszó"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Bejelentkezés"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Érvénytelen felhasználónév vagy jelszó."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Elfelejtette a felhasználónevét vagy jelszavát?"\n"Keresse fel a "<b>"google.com/accounts/recovery"</b>" webhelyet."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Fiók ellenőrzése..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg PIN kódját. "\n\n"Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg a jelszót. "\n\n" Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal rosszul rajzolta le feloldási mintát. "\n\n"Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"A táblagépet <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a táblagép gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"A telefont <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a telefon gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"A táblagépet <xliff:g id="NUMBER">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. A rendszer visszaállítja a táblagép gyári alapértelmezett beállításait."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"A telefont <xliff:g id="NUMBER">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. A rendszer visszaállítja a telefon gyári alapértelmezett beállításait."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a táblagépét."\n\n" Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a telefonját."\n\n" Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eltávolítás"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Előző szám gomb"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Következő szám gomb"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Szünet gomb"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Lejátszás gomb"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Leállítás gomb"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Nincs szolgáltatás."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-in/activitystrings.xml b/packages/Keyguard/res/values-in/activitystrings.xml
new file mode 100644
index 0000000..ec9774d
--- /dev/null
+++ b/packages/Keyguard/res/values-in/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Tanpa pengamanan"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Sandi"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Pola"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Pilih widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-in/strings.xml b/packages/Keyguard/res/values-in/strings.xml
new file mode 100644
index 0000000..95ca2f7
--- /dev/null
+++ b/packages/Keyguard/res/values-in/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Ketik kode PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Ketik kode PUK dan PIN baru"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Kode PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Kode Pin baru"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Sentuh untuk mengetikkan sandi"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Ketik sandi untuk membuka kunci"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Ketik PIN untuk membuka kunci"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kode PIN salah."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka, tekan Menu lalu 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Percobaan Face Unlock melebihi batas maksimum"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Terisi"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Mengisi baterai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Hubungkan pengisi daya."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Tekan Menu untuk membuka."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Jaringan terkunci"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Tidak ada kartu SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Tidak ada kartu SIM dalam tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Tidak ada Kartu SIM di dalam ponsel."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Masukkan kartu SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Kartu SIM tidak ada atau tidak dapat dibaca. Masukkan kartu SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Kartu SIM tidak dapat digunakan."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Kartu SIM Anda telah dinonaktifkan secara permanen."\n" Hubungi penyedia layanan nirkabel Anda untuk kartu SIM lain."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Kartu SIM terkunci."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Kartu SIM terkunci PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Membuka kartu SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d dari %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Tambahkan widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Kosong"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Area buka kunci diluaskan."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Area buka kunci diciutkan."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Pemilih pengguna"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Kontrol media"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Pengurutan ulang widget dimulai."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Pengurutan ulang widget berakhir."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> dihapus."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Luaskan area buka kunci."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Buka kunci dengan menggeser."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Buka kunci dengan pola."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Buka kunci dengan face unlock."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Buka kunci dengan PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Buka kunci dengan sandi."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Area pola."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Area geser."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Tombol lagu sebelumnya"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Tombol lagu berikutnya"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Tombol jeda"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Tombol putar"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Tombol hentikan"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Batal"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Hapus"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Selesai"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Pengubahan mode"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Membuka gembok"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Senyap"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Suara hidup"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Telusuri"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Geser ke atas untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Geser ke bawah untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Geser ke kiri untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Geser ke kanan untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Pengguna saat ini <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Panggilan darurat"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Lupa Pola?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Pola Salah"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Sandi Salah"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN Salah"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Coba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> detik."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Gambar pola Anda"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Masukkan PIN SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Masukkan PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Masukkan Sandi"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM telah dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Hubungi operator untuk keterangan selengkapnya."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Masukkan kode PIN yang diinginkan"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Konfirmasi kode PIN yang diinginkan"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Membuka kunci kartu SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Kode PIN salah."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Ketik PIN yang terdiri dari 4 sampai 8 angka."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Kode PUK harus terdiri dari 8 angka atau lebih."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Masukkan kembali kode PUK yang benar. Jika berulang kali gagal, SIM akan dinonaktifkan secara permanen."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Kode PIN tidak cocok"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Terlalu banyak upaya pola"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Untuk membuka kunci, masuk dengan akun Google Anda."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nama pengguna (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Sandi"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Masuk"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nama pengguna atau sandi tidak valid."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Lupa nama pengguna atau sandi Anda?"\n"Kunjungi "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Memeriksa akun…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik PIN. "\n\n"Coba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik sandi. "\n\n"Coba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. "\n\n"Coba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali gagal saat berusaha membuka kunci ponsel. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Kini tablet akan disetel ulang ke setelan default pabrik."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha untuk membuka kunci ponsel. Kini ponsel akan disetel ulang ke setelan default pabrik."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan akun email."\n\n"Coba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email."\n\n"Coba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Hapus"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Tombol lagu sebelumnya"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Tombol lagu berikutnya"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Tombol jeda"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Tombol putar"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Tombol hentikan"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Tidak ada layanan."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-it/activitystrings.xml b/packages/Keyguard/res/values-it/activitystrings.xml
new file mode 100644
index 0000000..34ad96497
--- /dev/null
+++ b/packages/Keyguard/res/values-it/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Nessuna sicurezza"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Password"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Sequenza"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN della SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK della SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Scegli widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-it/strings.xml b/packages/Keyguard/res/values-it/strings.xml
new file mode 100644
index 0000000..c47b120
--- /dev/null
+++ b/packages/Keyguard/res/values-it/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Inserisci il codice PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Inserisci il PUK e il nuovo codice PIN"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Codice PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nuovo codice PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Tocca per inserire la password"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Inserisci password per sbloccare"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Inserisci PIN per sbloccare"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Codice PIN errato."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Per sbloccare, premi Menu, poi 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Numero massimo di tentativi di Sblocco col sorriso superato"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Carico"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"In carica (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Collega il caricabatterie."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Premi Menu per sbloccare."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rete bloccata"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Nessuna scheda SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Nessuna scheda SIM presente nel tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Nessuna scheda SIM presente nel telefono."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Inserisci una scheda SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Scheda SIM mancante o non leggibile. Inserisci una scheda SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Scheda SIM inutilizzabile."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"La scheda SIM è stata disattivata definitivamente."\n" Contatta il fornitore del tuo servizio wireless per ricevere un\'altra scheda SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La SIM è bloccata."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La SIM è bloccata tramite PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Sblocco scheda SIM..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d di %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Aggiungi widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vuoto"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Area di sblocco estesa."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Area di sblocco compressa."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> widget."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Selettore utente"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Stato"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Fotocamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Controlli media"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Riordino dei widget iniziato."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Riordino dei widget terminato."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> eliminato."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Espandi area di sblocco."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Sblocco con scorrimento."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Sblocco con sequenza."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Sblocco col sorriso."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Sblocco con PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Sblocco con password."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Area sequenza."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Area di scorrimento."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Pulsante traccia precedente"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Pulsante traccia successiva"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Pulsante Pausa"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Pulsante Riproduci"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Pulsante di arresto"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annulla"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Canc"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Fine"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Cambio modalità"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Maiuscolo"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Invio"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Sblocca"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Fotocamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Silenzioso"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Audio attivato"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Ricerca"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Su per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Giù per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"A sinistra per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"A destra per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Utente corrente <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Chiamata di emergenza"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Sequenza dimenticata"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Sequenza sbagliata"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Password sbagliata"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN errato"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Riprova fra <xliff:g id="NUMBER">%d</xliff:g> secondi."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Inserisci la sequenza"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Inserisci il PIN della SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Inserisci PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Inserisci la password"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"La scheda SIM è disattivata. Inserisci il codice PUK per continuare. Contatta l\'operatore per avere informazioni dettagliate."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Inserisci il codice PIN desiderato"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Conferma il codice PIN desiderato"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Sblocco scheda SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Codice PIN errato."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Il PIN deve essere di 4-8 numeri."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Il codice PUK dovrebbe avere almeno otto numeri."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Inserisci di nuovo il codice PUK corretto. Ripetuti tentativi comportano la disattivazione definitiva della scheda SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"I codici PIN non corrispondono"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Troppi tentativi di inserimento della sequenza"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Per sbloccare, accedi con il tuo account Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nome utente (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Password"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Accedi"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome utente o password non validi."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Hai dimenticato il nome utente o la password?"\n"Visita "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Controllo account…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Hai digitato il tuo PIN <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Hai digitato la tua password <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di sblocco del tablet. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, il tablet verrà sottoposto a un ripristino dei dati di fabbrica e tutti i dati utente andranno persi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di sblocco del telefono. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, il telefono verrà sottoposto a un ripristino dei dati di fabbrica e tutti i dati utente andranno persi."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"<xliff:g id="NUMBER">%d</xliff:g> tentativi errati di sblocco del tablet. Il tablet verrà sottoposto a un ripristino dei dati di fabbrica."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"<xliff:g id="NUMBER">%d</xliff:g> tentativi errati di sblocco del telefono. Il telefono verrà sottoposto a un ripristino dei dati di fabbrica."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il tablet con un account email."\n\n" Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono con un account email."\n\n" Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Rimuovi"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Pulsante traccia precedente"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Pulsante traccia successiva"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Pulsante Pausa"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Pulsante Riproduci"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Pulsante di arresto"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Nessun servizio."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-iw/activitystrings.xml b/packages/Keyguard/res/values-iw/activitystrings.xml
new file mode 100644
index 0000000..84e351a2
--- /dev/null
+++ b/packages/Keyguard/res/values-iw/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"ללא אבטחה"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"סיסמה"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"קו ביטול נעילה"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN של SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK של SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"בחר Widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-iw/strings.xml b/packages/Keyguard/res/values-iw/strings.xml
new file mode 100644
index 0000000..70f73db
--- /dev/null
+++ b/packages/Keyguard/res/values-iw/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"הקלד קוד PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"הקלד את קוד ה-PUK וקוד ה-PIN החדש"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"קוד PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"קוד PIN חדש"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"גע כדי להקליד את הסיסמה"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"הקלד סיסמה לביטול הנעילה"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"הקלד קוד PIN לביטול הנעילה"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"קוד PIN שגוי"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"כדי לבטל את הנעילה, לחץ על \'תפריט\' ולאחר מכן על 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"חרגת ממספר הניסיונות המרבי של זיהוי פרצוף"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"טעון"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"טוען, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"חבר את המטען."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"לחץ על \'תפריט\' כדי לבטל את הנעילה."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"רשת נעולה"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"אין כרטיס SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"אין כרטיס SIM בטאבלט."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"אין כרטיס SIM בטלפון."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"הכנס כרטיס SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"כרטיס ה-SIM חסר או שלא ניתן לקרוא אותו. הכנס כרטיס SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"לא ניתן להשתמש בכרטיס SIM זה."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"כרטיס ה-SIM שלך הושבת לצמיתות."\n"פנה לספק השירות האלחוטי שלך לקבלת כרטיס SIM אחר."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"כרטיס ה-SIM נעול."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"כרטיס SIM נעול באמצעות PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"מבטל נעילה של כרטיס SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d מתוך %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"הוסף Widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"ריק"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"אזור ביטול הנעילה הורחב."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"אזור ביטול הנעילה כווץ."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"בוחר משתמשים"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"סטטוס"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"מצלמה"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"פקדי מדיה"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"סידור מחדש של Widgets התחיל."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"סידור מחדש של Widgets הסתיים."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> נמחק."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"הרחב את אזור ביטול הנעילה."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"ביטול נעילה באמצעות הסטה."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"ביטול נעילה באמצעות ציור קו."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"ביטול נעילה באמצעות זיהוי פרצוף."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"ביטול נעילה באמצעות מספר PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"ביטול נעילה באמצעות סיסמה."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"אזור ציור קו."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"אזור הסטה."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"לחצן \'הרצועה הקודמת\'"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"לחצן \'הרצועה הבאה\'"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"לחצן \'השהה\'"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"לחצן \'הפעל\'"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"לחצן \'הפסק\'"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"אבג"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ביטול"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"מחק"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"סיום"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"שינוי מצב"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"בטל נעילה"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"מצלמה"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"שקט"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"הקול פועל"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"חיפוש"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"הסט למעלה כדי להציג <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"הסט למטה כדי להציג <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"הסט שמאלה כדי להציג <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"הסט ימינה כדי להציג <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"המשתמש הנוכחי <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"שיחת חירום"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"שכחת את הקו"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"קו ביטול נעילה שגוי"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"סיסמה שגויה"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"מספר PIN שגוי"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"נסה שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"שרטט את קו ביטול הנעילה"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"הזן מספר PIN ל-SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"הזן מספר PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"הזן את הסיסמה"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"כרטיס ה-SIM מושבת כעת. הזן קוד PUK כדי להמשיך. פנה אל הספק לפרטים."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"הזן את קוד ה-PIN הרצוי"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"אשר את קוד ה-PIN הרצוי"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"מבטל נעילה של כרטיס SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"קוד PIN שגוי."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"הקלד מספר PIN שאורכו 4 עד 8 ספרות."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"קוד PUK צריך להיות בן 8 ספרות או יותר."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"הזן מחדש את קוד PUK הנכון. ניסיונות חוזרים ישביתו לצמיתות את כרטיס ה-SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"קודי ה-PIN אינם תואמים"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ניסיונות רבים מדי לשרטוט קו ביטול נעילה."</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"כדי לבטל את הנעילה, היכנס באמצעות חשבון Google שלך."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"שם משתמש (דוא\"ל)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"סיסמה"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"היכנס"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"שם משתמש או סיסמה לא חוקיים."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"שכחת את שם המשתמש או הסיסמה?"\n"בקר בכתובת "<b>"google.com/accounts/recovery"</b></string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"בודק חשבון…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"הקלדת מספר PIN שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. "\n\n"נסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"הקלדת סיסמה שגויה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים."\n\n"נסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. "\n\n"נסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ביצעת <xliff:g id="NUMBER_0">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטאבלט יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ביצעת <xliff:g id="NUMBER_0">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטלפון יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"ביצעת <xliff:g id="NUMBER">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטאבלט. הטאבלט יעבור כעת איפוס לברירת המחדל של היצרן."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"ביצעת <xliff:g id="NUMBER">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. הטלפון יעבור כעת איפוס לברירת המחדל של היצרן."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטאבלט באמצעות חשבון דוא\"ל."\n\n"נסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות חשבון דוא\"ל."\n\n"נסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"הסר"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"לחצן \'הרצועה הקודמת\'"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"לחצן \'הרצועה הבאה\'"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"לחצן \'השהה\'"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"לחצן \'הפעל\'"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"לחצן \'הפסק\'"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"אין קליטה."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ja/activitystrings.xml b/packages/Keyguard/res/values-ja/activitystrings.xml
new file mode 100644
index 0000000..b0e77f1
--- /dev/null
+++ b/packages/Keyguard/res/values-ja/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"セキュリティなし"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"パスワード"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"パターン"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM PIN"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"ウィジェットを選択..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ja/strings.xml b/packages/Keyguard/res/values-ja/strings.xml
new file mode 100644
index 0000000..f93d3ad
--- /dev/null
+++ b/packages/Keyguard/res/values-ja/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"PINコードを入力"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"PUKと新しいPINコードを入力"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUKコード"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"新しいPINコード"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"タップしてパスワードを入力"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"ロックを解除するにはパスワードを入力"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"ロックを解除するにはPINを入力"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PINコードが正しくありません。"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"MENU、0キーでロック解除"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"フェイスアンロックの最大試行回数を超えました"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"充電完了"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"充電中: <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"充電してください。"</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"メニューからロックを解除できます。"</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"ネットワークがロックされました"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"SIMカードが挿入されていません"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"タブレット内にSIMカードがありません。"</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"SIMカードが端末に挿入されていません。"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"SIMカードを挿入してください。"</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIMカードが見つからないか読み取れません。SIMカードを挿入してください。"</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIMカードは使用できません。"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"お使いのSIMカードは永久に無効となっています。"\n"ワイヤレスサービスプロバイダに問い合わせて新しいSIMカードを入手してください。"</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIMカードはロックされています。"</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIMカードはPUKでロックされています。"</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIMカードをロック解除しています…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s。ウィジェット%2$d/%3$d。"</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"ウィジェットを追加します。"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"なし"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"ロック解除エリアを拡大しました。"</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"ロック解除エリアを縮小しました。"</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>ウィジェットです。"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"ユーザー切り替え"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"ステータス"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"カメラ"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"メディアコントロール"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"ウィジェットの並べ替えを開始しました。"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"ウィジェットの並べ替えを終了しました。"</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>ウィジェットを削除しました。"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"ロック解除エリアを拡大します。"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"スライドロックを解除します。"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"パターンロックを解除します。"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"フェイスアンロックを行います。"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PINロックを解除します。"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"パスワードロックを解除します。"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"パターンエリアです。"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"スライドエリアです。"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"前のトラックボタン"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"次のトラックボタン"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"一時停止ボタン"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"再生ボタン"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"停止ボタン"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"キャンセル"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"削除"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"完了"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"モードを変更"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"ロックを解除"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"カメラ"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"マナーモード"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"サウンドON"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"検索します"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"上にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"下にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"左にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"右にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
+ <string name="user_switched" msgid="3768006783166984410">"現在のユーザーは<xliff:g id="NAME">%1$s</xliff:g>です。"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"緊急通報"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"パターンを忘れた場合"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"パターンが正しくありません"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"パスワードが正しくありません"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PINが正しくありません"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"パターンを入力"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM PINを入力"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"PINを入力"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"パスワードを入力"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIMが無効になりました。続行するにはPUKコードを入力してください。詳しくは携帯通信会社にお問い合わせください。"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"希望のPINコードを入力してください"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"希望のPINコードを確認してください"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIMカードのロック解除中…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PINコードが正しくありません。"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"PINは4~8桁の数字で入力してください。"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUKコードは8桁以上の番号です。"</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"正しいPUKコードを再入力してください。誤入力を繰り返すと、SIMが永久に無効になるおそれがあります。"</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PINコードが一致しません"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"パターンの入力を所定の回数以上間違えました。"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"ロックを解除するにはGoogleアカウントでログインしてください。"</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"ユーザー名(メール)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"パスワード"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"ログイン"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"ユーザー名またはパスワードが無効です。"</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ユーザー名またはパスワードを忘れた場合は"\n" "<b>"google.com/accounts/recovery"</b>" にアクセスしてください。"</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"アカウントをチェックしています…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PINの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"パスワードの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"タブレットのロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、タブレットは出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"携帯端末のロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、端末は出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"タブレットのロック解除を<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。タブレットは出荷時設定にリセットされます。"</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"携帯端末のロック解除を<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。端末は出荷時設定にリセットされます。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、タブレットのロック解除にメールアカウントが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、携帯端末のロック解除にメールアカウントが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"削除"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"前のトラックボタン"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"次のトラックボタン"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"一時停止ボタン"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"再生ボタン"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"停止ボタン"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"通信サービスはありません。"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ko/activitystrings.xml b/packages/Keyguard/res/values-ko/activitystrings.xml
new file mode 100644
index 0000000..3aab225
--- /dev/null
+++ b/packages/Keyguard/res/values-ko/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"보안 사용 안함"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"비밀번호"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"패턴"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM PIN"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"위젯 선택..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ko/strings.xml b/packages/Keyguard/res/values-ko/strings.xml
new file mode 100644
index 0000000..28291c8
--- /dev/null
+++ b/packages/Keyguard/res/values-ko/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"PIN 코드 입력"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"PUK 및 새 PIN 코드 입력"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK 코드"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"새 PIN 코드"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"비밀번호를 입력하려면 터치"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"잠금 해제하려면 비밀번호 입력"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"잠금을 해제하려면 PIN 입력"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 코드가 잘못되었습니다."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"잠금해제하려면 메뉴를 누른 다음 0을 누릅니다."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"얼굴 인식 잠금해제 최대 시도 횟수를 초과했습니다."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"충전됨"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"충전 중(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"충전기를 연결하세요."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"잠금해제하려면 메뉴를 누르세요."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"네트워크 잠김"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"SIM 카드가 없습니다."</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"태블릿에 SIM 카드가 없습니다."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"휴대전화에 SIM 카드가 없습니다."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"SIM 카드를 삽입하세요."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM 카드가 없거나 읽을 수 없습니다. SIM 카드를 삽입하세요."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"사용할 수 없는 SIM 카드입니다."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM 카드를 완전히 사용할 수 없게 되었습니다."\n"다른 SIM 카드를 사용하려면 무선 서비스 제공업체에 문의하시기 바랍니다."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM 카드가 잠겨 있습니다."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM 카드가 PUK 잠김 상태입니다."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM 카드 잠금해제 중..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. %3$d의 위젯 %2$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"위젯 추가"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"비어 있음"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"잠금 해제 영역 확장됨"</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"잠금 해제 영역 축소됨"</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> 위젯"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"사용자 선택기"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"상태"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"카메라"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"미디어 조정"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"위젯 재정렬 시작됨"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"위젯 재정렬 완료됨"</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> 위젯이 삭제됨"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"잠금 해제 영역 확장"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"슬라이드하여 잠금해제합니다."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"패턴을 사용하여 잠금해제합니다."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"얼굴 인식을 사용하여 잠금해제합니다."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"핀을 사용하여 잠금해제합니다."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"비밀번호를 사용하여 잠금해제합니다."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"패턴을 그리는 부분입니다."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"슬라이드하는 부분입니다."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"이전 트랙 버튼"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"다음 트랙 버튼"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"일시중지 버튼"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"재생 버튼"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"중지 버튼"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt 키"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"취소"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete 키"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"완료"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"모드 변경"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift 키"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter 키"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"잠금 해제"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"카메라"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"무음"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"사운드 켜기"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"검색"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 위로 슬라이드"</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 아래로 슬라이드"</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 왼쪽으로 슬라이드"</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 오른쪽으로 슬라이드"</string>
+ <string name="user_switched" msgid="3768006783166984410">"현재 사용자는 <xliff:g id="NAME">%1$s</xliff:g>님입니다."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"긴급 통화"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"패턴을 잊음"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"잘못된 패턴"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"잘못된 비밀번호"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"잘못된 PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"패턴 그리기"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM PIN 입력"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"PIN 입력"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"비밀번호 입력"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"이제 SIM을 사용할 수 없습니다. 계속하려면 PUK 코드를 입력합니다. 자세한 내용은 이동통신사에 문의하세요."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"원하는 PIN 코드 입력"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"원하는 PIN 코드 확인"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM 카드 잠금해제 중..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN 코드가 잘못되었습니다."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4~8자리 숫자로 된 PIN을 입력하세요."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK 코드는 8자리 이상의 숫자여야 합니다."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"올바른 PUK 코드를 다시 입력하세요. 입력을 반복해서 시도하면 SIM을 영구적으로 사용할 수 없게 됩니다."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN 코드가 일치하지 않음"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"패턴 시도 횟수가 너무 많음"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"잠금해제하려면 Google 계정으로 로그인하세요."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"사용자 이름(이메일)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"비밀번호"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"로그인"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"사용자 이름 또는 비밀번호가 잘못되었습니다."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"사용자 이름이나 비밀번호를 잊어버렸습니까?"\n<b>"google.com/accounts/recovery"</b>" 페이지를 방문하세요."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"계정 확인 중…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"비밀번호를 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"태블릿을 잠금해제하려는 시도가 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못되었습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 태블릿이 초기화되고 사용자 데이터가 모두 사라집니다."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"휴대전화를 잠금해제하려는 시도가 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못되었습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 휴대전화가 초기화되고 사용자 데이터가 모두 사라집니다."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"태블릿을 잠금해제하려는 시도가 <xliff:g id="NUMBER">%d</xliff:g>회 잘못되었습니다. 태블릿이 초기화됩니다."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"휴대전화를 잠금해제하려는 시도가 <xliff:g id="NUMBER">%d</xliff:g>회 잘못되었습니다. 휴대전화가 초기화됩니다."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 이메일 계정을 사용하여 태블릿을 잠금해제해야 합니다."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 이메일 계정을 사용하여 휴대전화를 잠금해제해야 합니다."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"삭제"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"이전 트랙 버튼"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"다음 트랙 버튼"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"일시중지 버튼"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"재생 버튼"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"중지 버튼"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"서비스 불가"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-land/alias.xml b/packages/Keyguard/res/values-land/alias.xml
new file mode 100644
index 0000000..7aac5b4
--- /dev/null
+++ b/packages/Keyguard/res/values-land/alias.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Alias used to reference one of two possible layouts in keyguard. -->
+ <item type="layout" name="keyguard_eca">@layout/keyguard_emergency_carrier_area_empty</item>
+</resources>
diff --git a/packages/Keyguard/res/values-land/arrays.xml b/packages/Keyguard/res/values-land/arrays.xml
new file mode 100644
index 0000000..240b9e4
--- /dev/null
+++ b/packages/Keyguard/res/values-land/arrays.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Resources for GlowPadView in LockScreen -->
+ <array name="lockscreen_targets_when_silent">
+ <item>@null</item>"
+ <item>@drawable/ic_action_assist_generic</item>
+ <item>@drawable/ic_lockscreen_soundon</item>
+ <item>@drawable/ic_lockscreen_unlock</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_when_silent">
+ <item>@null</item>
+ <item>@string/description_target_search</item>
+ <item>@string/description_target_soundon</item>
+ <item>@string/description_target_unlock</item>
+ </array>
+
+ <array name="lockscreen_direction_descriptions">
+ <item>@null</item>
+ <item>@string/description_direction_up</item>
+ <item>@string/description_direction_left</item>
+ <item>@string/description_direction_down</item>
+ </array>
+
+ <array name="lockscreen_targets_when_soundon">
+ <item>@null</item>
+ <item>@drawable/ic_action_assist_generic</item>
+ <item>@drawable/ic_lockscreen_silent</item>
+ <item>@drawable/ic_lockscreen_unlock</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_when_soundon">
+ <item>@null</item>
+ <item>@string/description_target_search</item>
+ <item>@string/description_target_silent</item>
+ <item>@string/description_target_unlock</item>
+ </array>
+
+ <array name="lockscreen_targets_with_camera">
+ <item>@null</item>
+ <item>@drawable/ic_action_assist_generic</item>
+ <item>@drawable/ic_lockscreen_camera</item>
+ <item>@drawable/ic_lockscreen_unlock</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_with_camera">
+ <item>@null</item>
+ <item>@string/description_target_search</item>
+ <item>@string/description_target_camera</item>
+ <item>@string/description_target_unlock</item>
+ </array>
+
+</resources>
diff --git a/packages/Keyguard/res/values-land/bools.xml b/packages/Keyguard/res/values-land/bools.xml
new file mode 100644
index 0000000..a1dd2e4
--- /dev/null
+++ b/packages/Keyguard/res/values-land/bools.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <bool name="kg_enable_camera_default_widget">false</bool>
+ <bool name="kg_top_align_page_shrink_on_bouncer_visible">true</bool>
+ <bool name="kg_share_status_area">false</bool>
+ <bool name="kg_sim_puk_account_full_screen">false</bool>
+</resources>
diff --git a/packages/Keyguard/res/values-land/dimens.xml b/packages/Keyguard/res/values-land/dimens.xml
new file mode 100644
index 0000000..64e043c
--- /dev/null
+++ b/packages/Keyguard/res/values-land/dimens.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+ <!-- Default height of a key in the password keyboard for alpha -->
+ <dimen name="password_keyboard_key_height_alpha">47dip</dimen>
+ <!-- Default height of a key in the password keyboard for numeric -->
+ <dimen name="password_keyboard_key_height_numeric">50dip</dimen>
+ <!-- Default correction for the space key in the password keyboard -->
+ <dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen>
+ <dimen name="preference_widget_width">72dp</dimen>
+
+ <!-- Size of clock font in LockScreen on Unsecure unlock screen. -->
+ <dimen name="keyguard_lockscreen_clock_font_size">70sp</dimen>
+
+ <!-- Shift emergency button from the left edge by this amount. Used by landscape layout on
+ phones -->
+ <dimen name="kg_emergency_button_shift">30dp</dimen>
+
+ <!-- Space reserved at the bottom of secure views (pin/pattern/password/SIM pin/SIM puk) -->
+ <dimen name="kg_secure_padding_height">0dp</dimen>
+
+ <!-- Top padding for the widget pager -->
+ <dimen name="kg_widget_pager_top_padding">0dp</dimen>
+
+ <!-- Bottom padding for the widget pager -->
+ <dimen name="kg_widget_pager_bottom_padding">0dp</dimen>
+
+ <!-- If the height if keyguard drops below this threshold (most likely
+ due to the appearance of the IME), then drop the multiuser selector.
+ Landscape's layout allows this to be smaller than for portrait. -->
+ <dimen name="kg_squashed_layout_threshold">400dp</dimen>
+
+</resources>
diff --git a/packages/Keyguard/res/values-land/integers.xml b/packages/Keyguard/res/values-land/integers.xml
new file mode 100644
index 0000000..020fd23
--- /dev/null
+++ b/packages/Keyguard/res/values-land/integers.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Gravity to make KeyguardSelectorView work in multiple orientations
+ 0x13 == "left|center_vertical" -->
+ <integer name="kg_selector_gravity">0x13</integer>
+ <integer name="kg_widget_region_weight">45</integer>
+ <integer name="kg_security_flipper_weight">55</integer>
+ <integer name="kg_glowpad_rotation_offset">-90</integer>
+</resources>
diff --git a/packages/Keyguard/res/values-large/dimens.xml b/packages/Keyguard/res/values-large/dimens.xml
new file mode 100644
index 0000000..8cd614d
--- /dev/null
+++ b/packages/Keyguard/res/values-large/dimens.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Default height of a key in the password keyboard for alpha -->
+ <dimen name="password_keyboard_key_height_alpha">75dip</dimen>
+ <!-- Default height of a key in the password keyboard for numeric -->
+ <dimen name="password_keyboard_key_height_numeric">75dip</dimen>
+ <!-- keyboardHeight = key_height*4 + key_bottom_gap*3 -->
+ <dimen name="password_keyboard_height">48.0mm</dimen>
+
+ <!-- Minimum width of the search view text entry area. -->
+ <dimen name="search_view_text_min_width">192dip</dimen>
+
+ <item type="dimen" name="dialog_min_width_major">55%</item>
+ <item type="dimen" name="dialog_min_width_minor">80%</item>
+
+ <!-- Preference UI dimensions for larger screens. -->
+ <dimen name="preference_widget_width">56dp</dimen>
+</resources>
diff --git a/packages/Keyguard/res/values-lt/activitystrings.xml b/packages/Keyguard/res/values-lt/activitystrings.xml
new file mode 100644
index 0000000..9ec21e4
--- /dev/null
+++ b/packages/Keyguard/res/values-lt/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Neapsaugota"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN kodas"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Slaptažodis"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Šablonas"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM kortelės PIN kodas"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM kortelės PUK kodas"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Pasirinkite valdiklį..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-lt/strings.xml b/packages/Keyguard/res/values-lt/strings.xml
new file mode 100644
index 0000000..4d6519c
--- /dev/null
+++ b/packages/Keyguard/res/values-lt/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Įveskite PIN kodą"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Įveskite PUK ir naują PIN kodus"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK kodas"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Naujas PIN kodas"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Palieskite, kad įves. slaptaž."</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Jei norite atrakinti, įveskite slaptažodį"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Jei norite atrakinti, įveskite PIN kodą"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Neteisingas PIN kodas."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Jei norite atrakinti, paspauskite „Meniu“ ir 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Viršijote maksimalų atrakinimo pagal veidą bandymų skaičių"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Įkrauta"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Įkraunama, <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Prijunkite įkroviklį."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Jei norite atrakinti, paspauskite „Meniu“."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Tinklas užrakintas"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Nėra SIM kortelės"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Planšetiniame kompiuteryje nėra SIM kortelės."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Telefone nėra SIM kortelės."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Įdėkite SIM kortelę."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Trūksta SIM kortelės arba ji neskaitoma. Įdėkite SIM kortelę."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Negalima naudoti SIM kortelės."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM kortelė visam laikui neleidžiama."\n" Jei norite gauti kitą SIM kortelę, susisiekite su belaidžio ryšio paslaugos teikėju."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kortelė užrakinta."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kortelė užrakinta PUK kodu."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Atrakinama SIM kortelė…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. %2$d valdiklis iš %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Pridėti valdiklį."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tuščia"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Atrakinimo sritis išplėsta."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Atrakinimo sritis sutraukta."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Valdiklis <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Naudotojo pasirinkimo valdiklis"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Būsena"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Fotoaparatas"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Medijos valdikliai"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Valdiklių pertvarkymas pradėtas."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Valdiklių pertvarkymas baigtas."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Valdiklis <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ištrintas."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Išplėsti atrakinimo sritį."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Atrakinimas slystant."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Atrakinimas pagal piešinį."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Atrakinimas pagal veidą."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Atrakinimas įvedus PIN kodą."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Atrakinimas įvedus slaptažodį."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Atrakinimo pagal piešinį sritis."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Slydimo sritis."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Ankstesnio takelio mygtukas"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Kito takelio mygtukas"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Pristabdymo mygtukas"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Paleidimo mygtukas"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Sustabdymo mygtukas"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Atšaukti"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ištrinti"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Atlikta"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Režimo keitimas"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Įvesti"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Atrakinti"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Vaizdo kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Begarsis"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Garsas įjungtas"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Paieška"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Slyskite aukštyn link <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Slyskite žemyn link <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Slyskite į kairę link <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Slyskite į dešinę link <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Dabartinis naudotojas: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Skambutis pagalbos numeriu"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Pamiršau atrakinimo piešinį"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Netinkamas atrakinimo piešinys"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Netinkamas slaptažodis"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Netinkamas PIN kodas"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Bandyti dar kartą po <xliff:g id="NUMBER">%d</xliff:g> sek."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Nupieškite atrakinimo piešinį"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Įveskite SIM PIN kodą"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Įveskite PIN kodą"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Įveskite slaptažodį"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Dabar SIM neleidžiama. Jei norite tęsti, įveskite PUK kodą. Jei reikia išsamios informacijos, susisiekite su operatoriumi."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Įveskite pageidaujamą PIN kodą"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Patvirtinkite pageidaujamą PIN kodą"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Atrakinama SIM kortelė…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Netinkamas PIN kodas."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Įveskite PIN kodą, sudarytą iš 4–8 skaičių."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK kodas turėtų būti mažiausiai 8 skaitmenų."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Pakartotinai įveskite tinkamą PUK kodą. Pakartotinai bandant SIM bus neleidžiama visam laikui."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN kodai neatitinka"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Per daug atrakinimo piešinių bandymų"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Jei norite atrakinti, prisijunkite naudodami „Google“ paskyrą."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Naudotojo vardas (el. paštas)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Slaptažodis"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prisijungti"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Netinkamas naudotojo vardas ar slaptažodis."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Pamiršote naudotojo vardą ar slaptažodį?"\n"Apsilankykite šiuo adresu: "<b>"google.com/accounts/recovery"</b></string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Tikrinama paskyra…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodą netinkamai įvedėte <xliff:g id="NUMBER_0">%d</xliff:g> k. "\n\n"Bandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Neteisingai įvedėte slaptažodį <xliff:g id="NUMBER_0">%d</xliff:g> k. "\n\n"Bandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. "\n\n"Bandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"<xliff:g id="NUMBER_0">%d</xliff:g> k. bandėte netinkamai atrakinti planšetinį kompiuterį. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. planšetiniame kompiuteryje bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"<xliff:g id="NUMBER_0">%d</xliff:g> k. bandėte netinkamai atrakinti telefoną. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. telefone bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"<xliff:g id="NUMBER">%d</xliff:g> k. bandėte netinkamai atrakinti planšetinį kompiuterį. Planšetiniame kompiuteryje bus iš naujo nustatyti numatytieji gamyklos nustatymai."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"<xliff:g id="NUMBER">%d</xliff:g> k. bandėte netinkamai atrakinti telefoną. Telefone bus iš naujo nustatyti numatytieji gamyklos nustatymai."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. būsite paprašyti atrakinti planšetinį kompiuterį naudodami „Google“ prisijungimo duomenis."\n\n" Bandykite dar kartą po <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. būsite paprašyti atrakinti telefoną naudodami „Google“ prisijungimo duomenis."\n\n" Bandykite dar kartą po <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Pašalinti"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Ankstesnio takelio mygtukas"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Kito takelio mygtukas"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Pristabdymo mygtukas"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Paleidimo mygtukas"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Sustabdymo mygtukas"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Nėra paslaugos."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-lv/activitystrings.xml b/packages/Keyguard/res/values-lv/activitystrings.xml
new file mode 100644
index 0000000..96807de
--- /dev/null
+++ b/packages/Keyguard/res/values-lv/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Drošība nav iespējota"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Parole"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Kombinācija"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM PIN"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Izvēlēties logrīku..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-lv/strings.xml b/packages/Keyguard/res/values-lv/strings.xml
new file mode 100644
index 0000000..d8c64b5
--- /dev/null
+++ b/packages/Keyguard/res/values-lv/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Ievadiet PIN kodu."</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Ievadiet PUK kodu un jaunu PIN kodu."</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK kods"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Jauns PIN kods"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Pieskarieties, lai ievadītu paroli"</font>"."</string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Ievadiet paroli, lai atbloķētu."</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Lai atbloķētu, ievadiet PIN."</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN kods nav pareizs."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Lai atbloķētu, nospiediet Izvēlne, pēc tam 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Ir pārsniegts maksimālais Autorizācijas pēc sejas mēģinājumu skaits."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Uzlādēts"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Notiek uzlāde (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Pievienojiet uzlādes ierīci."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Tīkls ir bloķēts."</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Nav SIM kartes."</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Planšetdatorā nav SIM kartes."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Tālrunī nav SIM kartes."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Ievietojiet SIM karti."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Nav SIM kartes, vai arī to nevar nolasīt. Ievietojiet SIM karti."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM karte nav lietojama."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Jūsu SIM karte ir neatgriezeniski atspējota."\n"Sazinieties ar savu bezvadu pakalpojumu sniedzēju, lai iegūtu citu SIM karti."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM karte ir bloķēta."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM karte ir bloķēta ar PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Notiek SIM kartes atbloķēšana..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. %2$d. logrīks no %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Pievienot logrīku."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tukšs"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Atbloķēšanas apgabals ir izvērsts."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Atbloķēšanas apgabals ir sakļauts."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Logrīks <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Lietotāju atlasītājs"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Statuss"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Multivides vadīklas"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Logrīku pārkārtošana ir sākta."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Logrīku pārkārtošana ir pabeigta."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Logrīks <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ir izdzēsts."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Izvērst atbloķēšanas apgabalu."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Autorizācija, velkot ar pirkstu."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Autorizācija ar kombināciju."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Autorizācija pēc sejas."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Autorizācija ar PIN kodu."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Autorizācija ar paroli."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Kombinācijas ievades apgabals."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Apgabals, kur vilkt ar pirkstu."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Iepriekšējā ieraksta poga"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Nākamā ieraksta poga"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Pārtraukšanas poga"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Atskaņošanas poga"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Apturēšanas poga"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alternēšanas taustiņš"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Atcelt"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Dzēšanas taustiņš"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Gatavs"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Režīma maiņa"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Pārslēgšanas taustiņš"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Ievadīšanas taustiņš"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Atbloķēt"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Klusums"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Skaņa ieslēgta"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Meklēt"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Velciet uz augšu, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Velciet uz leju, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Velciet pa kreisi, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Velciet pa labi, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Pašreizējais lietotājs: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Ārkārtas izsaukums"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Aizmirsu kombināciju"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Nepareiza kombinācija"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nepareiza parole"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Nepareizs PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER">%d</xliff:g> sekundēm."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Norādiet savu kombināciju"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ievadiet SIM kartes PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ievadiet PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ievadiet paroli"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM karte ir atspējota. Lai turpinātu, ievadiet PUK kodu. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Ievadiet vēlamo PIN kodu."</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Apstipriniet vēlamo PIN."</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Notiek SIM kartes atbloķēšana..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN kods nav pareizs."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Ievadiet PIN, kas sastāv no 4 līdz 8 cipariem."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK kodam ir jābūt vismaz 8 ciparus garam."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Atkārtoti ievadiet pareizo PUK kodu. Ja vairākas reizes ievadīsiet to nepareizi, SIM karte tiks neatgriezeniski atspējota."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN kodi neatbilst."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Pārāk daudz kombinācijas mēģinājumu"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Lai atbloķētu, pierakstieties, izmantojot savu Google kontu."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Lietotājvārds (e-pasts)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Parole"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Pierakstīties"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nederīgs lietotājvārds vai parole."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Vai aizmirsāt lietotājvārdu vai paroli?"\n"Apmeklējiet vietni "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Notiek konta pārbaude…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jūs nepareizi ievadījāt PIN <xliff:g id="NUMBER_0">%d</xliff:g> reizes."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jūs nepareizi ievadījāt paroli <xliff:g id="NUMBER_0">%d</xliff:g> reizes."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi un lietotāja dati tiks zaudēti."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi un lietotāja dati tiks zaudēti."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER">%d</xliff:g> reizes. Planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER">%d</xliff:g> reizes. Tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem planšetdators būs jāatbloķē, izmantojot e-pasta kontu."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot e-pasta kontu."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Noņemt"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Iepriekšējā ieraksta poga"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Nākamā ieraksta poga"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Pārtraukšanas poga"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Atskaņošanas poga"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Apturēšanas poga"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Nav pakalpojuma."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ms/activitystrings.xml b/packages/Keyguard/res/values-ms/activitystrings.xml
new file mode 100644
index 0000000..04e2184
--- /dev/null
+++ b/packages/Keyguard/res/values-ms/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Tiada keselamatan"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Kata laluan"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Corak"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Pilih widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ms/strings.xml b/packages/Keyguard/res/values-ms/strings.xml
new file mode 100644
index 0000000..2379755
--- /dev/null
+++ b/packages/Keyguard/res/values-ms/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Taip kod PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Taip PUK dan kod PIN baharu"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Kod PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Kod PIN Baharu"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Sentuh untuk menaip kata laluan"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Taip kata laluan untuk membuka kunci"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Taip PIN untuk membuka kunci"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kod PIN salah."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka kunci, tekan Menu, kemudian 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Telah melepasi had cubaan Buka Kunci Wajah"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Sudah dicas"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Mengecas, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Sambungkan pengecas anda."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Tekan Menu untuk membuka kunci."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rangkaian dikunci"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Tiada kad SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Tiada kad SIM dalam tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Tiada kad SIM dalam telefon."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Masukkan kad SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Kad SIM tiada atau tidak boleh dibaca. Sila masukkan kad SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Kad SIM tidak boleh digunakan."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Kad SIM anda telah dilumpuhkan secara kekal."\n" Hubungi pembekal perkhidmatan wayarles anda untuk mendapatkan kad SIM lain."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Kad SIM dikunci."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Kad SIM dikunci dengan PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Membuka kunci kad SIM..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d dari %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Tambah widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Kosong"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Bahagian buka kunci dikembangkan."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Bahagian buka kunci diruntuhkan."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Pemilih pengguna"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Kawalan media"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Penyusunan semula widget dimulakan."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Penyusunan semula widget tamat."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> dipadamkan."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Kembangkan bahagian buka kunci."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Buka kunci luncur."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Buka kunci corak."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Wajah Buka Kunci"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Buka kunci pin."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Buka kunci kata laluan."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Kawasan corak."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Kawasan luncur."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Butang lagu sebelumnya"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Butang lagu seterusnya"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Butang jeda"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Butang main"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Butang berhenti"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Batal"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Padam"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Selesai"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Perubahan mod"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Masuk"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Buka kunci"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Senyap"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Bunyi dihidupkan"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Carian"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Luncurkan ke atas untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Luncurkan ke bawah untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Luncurkan ke kiri untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Luncurkan ke kanan untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Pengguna semasa <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Panggilan kecemasan"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Lupa Corak"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Corak Salah"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Kata Laluan Salah"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN salah"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Cuba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> saat."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Lukiskan corak anda"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Masukkan PIN SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Masukkan PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Masukkan Kata Laluan"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Kini SIM dilumpuhkan. Masukkan kod PUK untuk meneruskan. Hubungi pembawa untuk butiran."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Masukkan kod PIN yang diingini"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Sahkan kod PIN yang diingini"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Membuka kunci kad SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Kod PIN salah."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Taipkan PIN yang mengandungi 4 hingga 8 nombor."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Kod PUK mestilah 8 nombor atau lebih."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Masukkan semula kod PIN yang betul. Percubaan berulang akan melumpuhkan SIM secara kekal."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Kod PIN tidak sepadan"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Terlalu banyak percubaan melukis corak"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Untuk membuka kunci, log masuk dengan akaun Google anda."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nama Pengguna (E-mel)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Kata laluan"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Log masuk"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nama pengguna atau kata laluan tidak sah."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Lupa nama pengguna atau kata laluan anda?"\n"Lawati"<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Menyemak akaun…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah menaip PIN anda secara salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. "\n\n"Cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah menaip kata laluan anda secara salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. "\n\n"Cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah tersilap melukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. "\n\n"Cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Anda telah mencuba untuk membuka kunci tablet dengan salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, tablet akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Anda telah mencuba untuk membuka kunci telefon dengan salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, telefon akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Anda telah mencuba untuk membuka kunci tablet secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Tablet kini akan ditetapkan semula ke tetapan lalai kilang."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Anda telah mencuba untuk membuka kunci telefon secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Telefon kini akan ditetapkan semula ke tetapan lalai kilang."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Anda telah tersilap melukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci tablet anda menggunakan log masuk Google anda."\n\n" Cuba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah tersilap lukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci telefon anda menggunakan log masuk Google anda."\n\n" Cuba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Alih keluar"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Butang lagu sebelumnya"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Butang lagu seterusnya"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Butang jeda"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Butang main"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Butang berhenti"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Tiada perkhidmatan."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-nb/activitystrings.xml b/packages/Keyguard/res/values-nb/activitystrings.xml
new file mode 100644
index 0000000..015df15
--- /dev/null
+++ b/packages/Keyguard/res/values-nb/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Ingen sikkerhet"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Passord"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Mønster"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"Personlig kode for SIM-kort"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK-kode for SIM-kort"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Velg modul"</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-nb/strings.xml b/packages/Keyguard/res/values-nb/strings.xml
new file mode 100644
index 0000000..623d640
--- /dev/null
+++ b/packages/Keyguard/res/values-nb/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Skriv inn PIN-kode"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Skriv inn PUK-kode og ny personlig kode"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-kode"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Ny PIN-kode"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Trykk for å skrive inn passord"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Skriv inn passord for å låse opp"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Skriv inn PIN-kode for å låse opp"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Feil personlig kode."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"For å låse opp, trykk på menyknappen og deretter 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har overskredet grensen for opplåsingsforsøk med Ansiktslås"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Oppladet"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Lader: <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Koble til laderen."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Trykk på Meny for å låse opp."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Nettverk låst"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"SIM-kortet mangler"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Nettbrettet mangler SIM-kort."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Telefonen mangler SIM-kort."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Sett inn et SIM-kort."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-kort mangler eller er uleselig. Sett inn et SIM-kort."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Ubrukelig SIM-kort."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM-kortet er deaktivert permanent."\n"Ta kontakt med leverandøren av trådløstjenesten for å få et nytt SIM-kort."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kortet er låst."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kortet er PUK-låst."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Låser opp SIM-kortet ..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Modul %2$d av %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Legg til modul."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tom"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Opplåsingsfeltet vises."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Opplåsingsfeltet skjules."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>-modul."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Brukervelgeren"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Mediekontroll"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Endring av modulplasseringen har startet."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Endringen av modulplasseringen er ferdig."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Modulen <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ble slettet."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Vis opplåsingsfeltet."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Opplåsning ved å dra med fingeren."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Mønsteropplåsning."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Ansiktsopplåsning."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN-opplåsning."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Passordopplåsning."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Mønsterområde."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Dra-felt."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Forrige spor-knapp"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Neste spor-knapp"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Pause-knapp"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Avspillingsknapp"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Stopp-knapp"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Avbryt"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Slett"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Ferdig"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Modusendring"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Lås opp"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Stille"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Lyd på"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Søk"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Dra opp for å <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Dra ned for å <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Dra til venstre for å <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Dra til høyre for å <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Gjeldende bruker: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Nødnummer"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Har du glemt mønsteret?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Feil mønster"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Feil passord"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Feil PIN-kode"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Prøv på nytt om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Tegn mønsteret ditt"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Skriv inn PIN-koden for SIM-kortet"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Skriv inn PIN-koden"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Skriv inn passordet"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM-kortet er nå deaktivert. Skriv inn PUK-koden for å fortsette. Ta kontakt med operatøren for mer informasjon."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Tast inn ønsket PIN-kode"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Bekreft ønsket PIN-kode"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Låser opp SIM-kortet ..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Feil PIN-kode."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Skriv inn en PIN-kode på fire til åtte sifre."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK-koden skal være på åtte eller flere siffer."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Skriv inn den korrekte PUK-koden på nytt. Gjentatte forsøk kommer til å deaktivere SIM-kortet."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-kodene stemmer ikke overens"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"For mange forsøk på tegning av mønster"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Logg deg på med Google-kontoen din for å låse opp."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Brukernavn (e-postadresse)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Passord"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Logg på"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ugyldig brukernavn eller passord."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glemt brukernavnet eller passordet?"\n"Gå til "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Sjekker kontoen ..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har oppgitt feil PIN-kode <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har tastet inn passordet ditt feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet opplåsningsmønsteret ditt feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har oppgitt feil opplåsningspassord for nettbrettet <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, tilbakestilles nettbrettet til fabrikkstandard og all data går tapt."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har oppgitt feil opplåsningspassord for telefonen <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, tilbakestilles telefonen til fabrikkstandard og all data går tapt."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har oppgitt feil opplåsningspassord for nettbrettet <xliff:g id="NUMBER">%d</xliff:g> ganger. Telefonen tilbakestilles nå til fabrikkstandard."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har oppgitt feil opplåsningspassord for telefonen <xliff:g id="NUMBER">%d</xliff:g> ganger. Telefonen tilbakestilles nå til fabrikkstandard."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, blir du bedt om å låse opp nettbrettet via en e-postkonto."\n\n" Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, blir du bedt om å låse opp telefonen via en e-postkonto."\n\n" Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Fjern"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Forrige spor-knapp"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Neste spor-knapp"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Pause-knapp"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Avspillingsknapp"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Stopp-knappen"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Ingen tjeneste."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-nl/activitystrings.xml b/packages/Keyguard/res/values-nl/activitystrings.xml
new file mode 100644
index 0000000..fcb0be9
--- /dev/null
+++ b/packages/Keyguard/res/values-nl/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Geen beveiliging"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"Pincode"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Wachtwoord"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Patroon"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"Pincode van simkaart"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK-code van simkaart"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Widget kiezen…"</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-nl/strings.xml b/packages/Keyguard/res/values-nl/strings.xml
new file mode 100644
index 0000000..33a99c8
--- /dev/null
+++ b/packages/Keyguard/res/values-nl/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Pincode typen"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Geef de PUK-code en de nieuwe pincode op"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-code"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nieuwe pincode"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Raak aan om wachtwoord in te voeren"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Typ het wachtwoord om te ontgrendelen"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Typ pincode om te ontgrendelen"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Onjuiste pincode."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Druk op \'Menu\' en vervolgens op 0 om te ontgrendelen."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximaal aantal pogingen voor Ontgrendelen via gezichtsherkenning overschreden"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Opgeladen"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Opladen, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Sluit de oplader aan."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Druk op \'Menu\' om te ontgrendelen."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Netwerk vergrendeld"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Geen simkaart"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Geen simkaart in tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Geen simkaart in telefoon."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Plaats een simkaart."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"De simkaart ontbreekt of kan niet worden gelezen. Plaats een simkaart."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Onbruikbare simkaart."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Uw simkaart is permanent uitgeschakeld."\n" Neem contact op met uw mobiele serviceprovider voor een nieuwe simkaart."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Simkaart is vergrendeld."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Simkaart is vergrendeld met PUK-code."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Simkaart ontgrendelen…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d van %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Widget toevoegen."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Leeg"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Ontgrendelingsgebied uitgevouwen."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Ontgrendelingsgebied samengevouwen."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Gebruikersselectie"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Camera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Mediabediening"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Opnieuw indelen van widget gestart."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Opnieuw indelen van widget beëindigd."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> verwijderd."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Ontgrendelingsgebied uitvouwen."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Ontgrendeling via schuiven."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Ontgrendeling via patroon."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Ontgrendelen via gezichtsherkenning"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Ontgrendeling via pincode."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Ontgrendeling via wachtwoord."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Tekengebied voor patroon."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Schuifgebied."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Knop voor vorig nummer"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Knop voor volgend nummer"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Knop voor onderbreken"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Knop voor afspelen"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Knop voor stoppen"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"Alt"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuleren"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Gereed"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Modus wijzigen"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Ontgrendelen"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Camera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Stil"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Geluid aan"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Zoeken"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Veeg omhoog voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Veeg omlaag voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Veeg naar links voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Veeg naar rechts voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Noodoproep"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Patroon vergeten"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Onjuist patroon"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Onjuist wachtwoord"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Onjuiste pincode"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Probeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Teken uw patroon"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Geef de pincode van de simkaart op"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Pincode opgeven"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Wachtwoord invoeren"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"De simkaart is nu uitgeschakeld. Geef de PUK-code op om door te gaan. Neem contact op met de provider voor informatie."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Gewenste pincode opgeven"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Gewenste pincode bevestigen"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Simkaart ontgrendelen..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Onjuiste pincode."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Voer een pincode van 4 tot 8 cijfers in."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"De PUK-code is minimaal acht nummers lang."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Geef de juiste PUK-code opnieuw op. Bij herhaalde pogingen wordt de simkaart permanent uitgeschakeld."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Pincodes komen niet overeen"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Te veel patroonpogingen"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Als u wilt ontgrendelen, moet u inloggen op uw Google-account."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Gebruikersnaam (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Wachtwoord"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Inloggen"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ongeldige gebruikersnaam of wachtwoord."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bent u uw gebruikersnaam of wachtwoord vergeten?"\n"Ga naar "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Account controleren…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"U heeft uw pincode <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"U heeft uw wachtwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"U heeft nu <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"U heeft <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de tablet."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"U heeft <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de telefoon."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd uw tablet te ontgrendelen via een e-mailaccount."\n\n" Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd uw telefoon te ontgrendelen via een e-mailaccount."\n\n" Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwijderen"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Knop voor vorig nummer"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Knop voor volgend nummer"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Knop voor onderbreken"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Knop voor afspelen"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Knop voor stoppen"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Geen service"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-pl/activitystrings.xml b/packages/Keyguard/res/values-pl/activitystrings.xml
new file mode 100644
index 0000000..f04170e
--- /dev/null
+++ b/packages/Keyguard/res/values-pl/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Bez zabezpieczeń"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Hasło"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Wzór"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN do karty SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK do karty SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Wybierz widżet..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-pl/strings.xml b/packages/Keyguard/res/values-pl/strings.xml
new file mode 100644
index 0000000..501a084
--- /dev/null
+++ b/packages/Keyguard/res/values-pl/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Wpisz kod PIN."</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Wpisz kod PUK i nowy kod PIN."</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Kod PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nowy PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Dotknij, aby wpisać hasło."</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Wpisz hasło, aby odblokować."</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Wpisz kod PIN, aby odblokować."</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Błędny kod PIN"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Aby odblokować, naciśnij Menu, a następnie 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Przekroczono maksymalną liczbę prób rozpoznania twarzy."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Naładowana"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Ładowanie (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Podłącz ładowarkę."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Naciśnij Menu, by odblokować."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Zablokowana sieć"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Brak karty SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Brak karty SIM w tablecie."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Brak karty SIM w telefonie."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Włóż kartę SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Brak karty SIM lub nie można jej odczytać. Włóż kartę SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Karta SIM bezużyteczna."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Karta SIM jest trwale wyłączona."\n" Skontaktuj się z dostawcą usług bezprzewodowych, by otrzymać inną kartę SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Karta SIM jest zablokowana."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Karta SIM jest zablokowana za pomocą kodu PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Odblokowuję kartę SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widżet %2$d z %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Dodaj widżet."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Puste"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Rozwinięto obszar odblokowania."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Zwinięto obszar odblokowania."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widżet <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Wybór użytkownika"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Stan"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Aparat"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Elementy sterujące multimediów"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Rozpoczęto zmienianie kolejności widżetów."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Zakończono zmienianie kolejności widżetów."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Usunięto widżet <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Rozwiń obszar odblokowania."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Odblokowanie przesunięciem."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Odblokowanie wzorem."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Rozpoznanie twarzy"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Odblokowanie kodem PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Odblokowanie hasłem."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Obszar wzoru."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Obszar przesuwania."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Przycisk poprzedniego utworu"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Przycisk następnego utworu"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Przycisk wstrzymania"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Przycisk odtwarzania"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Przycisk zatrzymania"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anuluj"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Gotowe"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Zmiana trybu"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Odblokuj"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Aparat"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Wyciszenie"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Włącz dźwięk"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Szukaj"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Przesuń w górę: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Przesuń w dół: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Przesuń w lewo: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Przesuń w prawo: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Bieżący użytkownik: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Połączenie alarmowe"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Nie pamiętam wzoru"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Nieprawidłowy wzór"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nieprawidłowe hasło"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Nieprawidłowy PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Spróbuj ponownie za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Narysuj wzór"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Podaj PIN karty SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Podaj PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Wpisz hasło"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Karta SIM została wyłączona. Podaj kod PUK, by przejść dalej. Szczegóły uzyskasz od operatora."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Podaj wybrany kod PIN"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potwierdź wybrany kod PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Odblokowuję kartę SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Nieprawidłowy PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Wpisz PIN o długości od 4 do 8 cyfr."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Kod PUK musi mieć co najmniej 8 cyfr."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Ponownie podaj poprawny kod PUK. Nieudane próby spowodują trwałe wyłączenie karty SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Kody PIN nie pasują"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Zbyt wiele prób narysowania wzoru"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Aby odblokować, zaloguj się na konto Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nazwa użytkownika (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Hasło"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Zaloguj się"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nieprawidłowa nazwa użytkownika lub hasło."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nie pamiętasz nazwy użytkownika lub hasła?"\n"Wejdź na "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Sprawdzam konto"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> wpisałeś nieprawidłowy PIN. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> wpisałeś nieprawidłowe hasło. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> narysowałeś nieprawidłowy wzór odblokowania. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> próbowałeś nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach tablet zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> próbowałeś nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach telefon zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowałeś nieprawidłowo odblokować tablet. Tablet zostanie teraz zresetowany do ustawień fabrycznych."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowałeś nieprawidłowo odblokować telefon. Telefon zostanie teraz zresetowany do ustawień fabrycznych."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach konieczne będzie odblokowanie tabletu przy użyciu danych logowania na konto Google."\n\n" Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google."\n\n" Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Usuń"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Przycisk poprzedniego utworu"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Przycisk następnego utworu"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Przycisk wstrzymania"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Przycisk odtwarzania"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Przycisk zatrzymania"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Brak usługi."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-port/alias.xml b/packages/Keyguard/res/values-port/alias.xml
new file mode 100644
index 0000000..c3ecbb9
--- /dev/null
+++ b/packages/Keyguard/res/values-port/alias.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Alias used to reference one of two possible layouts in keyguard. -->
+ <item type="layout" name="keyguard_eca">@layout/keyguard_emergency_carrier_area</item>
+</resources>
diff --git a/packages/Keyguard/res/values-port/bools.xml b/packages/Keyguard/res/values-port/bools.xml
new file mode 100644
index 0000000..1e2a4f2
--- /dev/null
+++ b/packages/Keyguard/res/values-port/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <bool name="action_bar_embed_tabs">false</bool>
+ <bool name="kg_share_status_area">true</bool>
+ <bool name="kg_sim_puk_account_full_screen">true</bool>
+</resources>
diff --git a/packages/Keyguard/res/values-port/integers.xml b/packages/Keyguard/res/values-port/integers.xml
new file mode 100644
index 0000000..ef7e4da
--- /dev/null
+++ b/packages/Keyguard/res/values-port/integers.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Gravity to make KeyguardSelectorView work in multiple orientations
+ 0x31 == "top|center_horizontal" -->
+ <integer name="kg_selector_gravity">0x31</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values-pt-rPT/activitystrings.xml b/packages/Keyguard/res/values-pt-rPT/activitystrings.xml
new file mode 100644
index 0000000..470865d
--- /dev/null
+++ b/packages/Keyguard/res/values-pt-rPT/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Sem segurança"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Palavra-passe"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Sequência"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN do SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK do SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Escolher widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-pt-rPT/strings.xml b/packages/Keyguard/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..19fef32
--- /dev/null
+++ b/packages/Keyguard/res/values-pt-rPT/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Escreva o código PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Escreva o PUK e o novo código PIN"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Código PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Novo código PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Toque para escrever a palavra-passe"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Escreva a palavra-passe para desbloquear"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Escreva o PIN para desbloquear"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, prima Menu e, em seguida, 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Excedido o n.º máximo de tentativas de Desbloqueio Através do Rosto"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Carregado"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"A carregar, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Ligue o carregador."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Prima Menu para desbloquear."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rede bloqueada"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Nenhum cartão SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Nenhum cartão SIM no tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Nenhum cartão SIM no telemóvel."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Insira um cartão SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"O cartão SIM está em falta ou não é legível. Introduza um cartão SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Cartão SIM inutilizável."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"O cartão SIM foi desativado definitivamente."\n" Contacte o seu fornecedor de serviços de rede sem fios para obter outro cartão SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"O cartão SIM está bloqueado."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"O cartão SIM está bloqueado por PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"A desbloquear o cartão SIM..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Adicionar widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vazio"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Área de desbloqueio expandida."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Área de desbloqueio minimizada."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> widget."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Seletor de utilizadores"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Estado"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Câmara"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Controlos de multimédia"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Reordenação de widgets iniciada."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Reordenação de widgets concluída."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> eliminado."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Expandir área de desbloqueio."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Desbloqueio através de deslize."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueio através de sequência."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueio através do rosto."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueio através de PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueio através de palavra-passe."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área da sequência."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área de deslize."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Botão Faixa anterior"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Botão Faixa seguinte"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Botão Pausa"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Botão Reproduzir"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Botão Parar"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Concluído"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Alteração do modo"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Desbloquear"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Câmara"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Silencioso"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Som ativado"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Pesquisar"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Deslize para cima para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Deslize para baixo para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Deslize para a esquerda para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Deslize para a direita para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="user_switched" msgid="3768006783166984410">"<xliff:g id="NAME">%1$s</xliff:g> do utilizador atual."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Chamada de emergência"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Esqueceu-se da Sequência"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Sequência Incorreta"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Palavra-passe Incorreta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN Incorreto"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe a sua sequência"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduzir PIN do cartão SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Introduzir PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Introduzir Palavra-passe"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"O SIM está agora desativado. Introduza o código PUK para continuar. Contacte o operador para obter detalhes."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Introduza o código PIN pretendido"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirme o código PIN pretendido"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"A desbloquear cartão SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorreto."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Introduza um PIN entre 4 e 8 números."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"O código PUK deve ter 8 ou mais números."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Volte a introduzir o código PUK correto. Demasiadas tentativas consecutivas irão desativar permanentemente o SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Os códigos PIN não correspondem"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Demasiadas tentativas para desenhar sequência"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear, inicie sessão com a sua Conta do Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nome de utilizador (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Palavra-passe"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Iniciar sessão"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome de utilizador ou palavra-passe inválidos."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueceu-se do nome de utilizador ou da palavra-passe?"\n"Aceda a "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"A verificar a conta…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escreveu o PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escreveu a palavra-passe incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Desenhou a sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tentou desbloquear o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes de forma incorreta. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, as definições de origem do telemóvel serão repostas e todos os dados do utilizador serão perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER_0">%d</xliff:g> vezes de forma incorreta. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, as definições de origem do telemóvel serão repostas e todos os dados do utilizador serão perdidos."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tentou desbloquear o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes de forma incorreta, pelo que será reposta a predefinição de fábrica."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER">%d</xliff:g> vezes de forma incorreta, pelo que será reposta a predefinição de fábrica."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o tablet através de uma conta de email."\n\n" Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email."\n\n" Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remover"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Botão Faixa anterior"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Botão Faixa seguinte"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Botão Pausa"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Botão Reproduzir"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Botão Parar"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Sem serviço."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-pt/activitystrings.xml b/packages/Keyguard/res/values-pt/activitystrings.xml
new file mode 100644
index 0000000..7a63708
--- /dev/null
+++ b/packages/Keyguard/res/values-pt/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Sem segurança"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Senha"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Padrão"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN do SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK do SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Escolher widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-pt/strings.xml b/packages/Keyguard/res/values-pt/strings.xml
new file mode 100644
index 0000000..08e2a60
--- /dev/null
+++ b/packages/Keyguard/res/values-pt/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Insira o código PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Insira o PUK e o novo código PIN"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Código PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Novo código PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Toque para inserir a senha"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Digite a senha para desbloquear"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Insira o PIN para desbloquear"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"O número máximo de tentativas de Desbloqueio por reconhecimento facial foi excedido"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Carregado"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Carregando, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Conecte seu carregador."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Pressione \"Menu\" para desbloquear."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rede bloqueada"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Sem cartão SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Não há um cartão SIM no tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Não há um cartão SIM no telefone."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Insira um cartão SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"O cartão SIM não foi inserido ou não é possível lê-lo. Insira um cartão SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Cartão SIM inutilizável."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"O cartão SIM foi desativado permanentemente."\n"Entre em contato com seu provedor de serviços sem fio para receber outro cartão SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"O cartão SIM está bloqueado."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"O cartão SIM está bloqueado pelo PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Desbloqueando o cartão SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Adicionar widget"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vazio"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Área de desbloqueio expandida."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Área de desbloqueio recolhida."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget de <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Seletor de usuários"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Câmera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Controles de mídia"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Reordenação de widgets iniciada."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Reordenação de widgets concluída."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> excluído."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Expandir a área de desbloqueio."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Desbloqueio com deslize."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueio com padrão."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueio facial."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueio com PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueio com senha."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área do padrão."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área de deslize."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Botão \"Faixa anterior\""</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Botão \"Próxima faixa\""</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Botão \"Pausar\""</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Botão \"Reproduzir\""</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Botão \"Parar\""</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Excluir"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Concluído"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Alteração do modo"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Desbloquear"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Câmera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Silencioso"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Som ativado"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Pesquisar"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para cima."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para baixo."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para a esquerda."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para a direita."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Usuário atual <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Chamada de emergência"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Esqueci o padrão"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Padrão incorreto"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Senha incorreta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorreto"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe seu padrão"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Digite o PIN do cartão SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Digite o PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Digite a senha"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"O SIM foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para obter mais detalhes."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Digite o código PIN desejado"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirme o código PIN desejado"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando o cartão SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorreto."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Digite um PIN com quatro a oito números."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"O código PUK deve ter 8 números ou mais."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Introduza novamente o código PUK correto. Muitas tentativas malsucedidas desativarão permanentemente o SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Os códigos PIN não coincidem"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Muitas tentativas de padrão"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear, faça login usando sua Conta do Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nome de usuário (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Senha"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Fazer login"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome de usuário ou senha inválidos."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueceu seu nome de usuário ou senha?"\n"Acesse "<b>"google.com.br/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Verificando a conta..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes. O tablet será redefinido para o padrão de fábrica."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER">%d</xliff:g> vezes. O telefone será redefinido para o padrão de fábrica."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet."\n\n" Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear."\n\n" Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remover"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Botão \"Faixa anterior\""</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Botão \"Próxima faixa\""</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Botão \"Pausar\""</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Botão \"Reproduzir\""</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Botão \"Parar\""</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Sem serviço."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-rm/strings.xml b/packages/Keyguard/res/values-rm/strings.xml
new file mode 100644
index 0000000..8dda055
--- /dev/null
+++ b/packages/Keyguard/res/values-rm/strings.xml
@@ -0,0 +1,249 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for keyguard_password_enter_pin_code (3037685796058495017) -->
+ <skip />
+ <!-- no translation found for keyguard_password_enter_puk_code (4800725266925845333) -->
+ <skip />
+ <!-- no translation found for keyguard_password_enter_puk_prompt (1341112146710087048) -->
+ <skip />
+ <!-- no translation found for keyguard_password_enter_pin_prompt (8027680321614196258) -->
+ <skip />
+ <!-- no translation found for keyguard_password_entry_touch_hint (7858547464982981384) -->
+ <skip />
+ <!-- no translation found for keyguard_password_enter_password_code (1054721668279049780) -->
+ <skip />
+ <!-- no translation found for keyguard_password_enter_pin_password_code (6391755146112503443) -->
+ <skip />
+ <!-- no translation found for keyguard_password_wrong_pin_code (2422225591006134936) -->
+ <skip />
+ <string name="keyguard_label_text" msgid="861796461028298424">"Smatgai per debloccar sin la tasta Menu e lura sin 0."</string>
+ <!-- no translation found for faceunlock_multiple_failures (754137583022792429) -->
+ <skip />
+ <!-- no translation found for keyguard_charged (3272223906073492454) -->
+ <skip />
+ <!-- no translation found for keyguard_plugged_in (8117572000639998388) -->
+ <skip />
+ <!-- no translation found for keyguard_low_battery (8143808018719173859) -->
+ <skip />
+ <!-- no translation found for keyguard_instructions_when_pattern_disabled (1332288268600329841) -->
+ <skip />
+ <!-- no translation found for keyguard_network_locked_message (9169717779058037168) -->
+ <skip />
+ <!-- no translation found for keyguard_missing_sim_message_short (494980561304211931) -->
+ <skip />
+ <!-- no translation found for keyguard_missing_sim_message (1445849005909260039) -->
+ <skip />
+ <!-- no translation found for keyguard_missing_sim_message (3481110395508637643) -->
+ <skip />
+ <!-- no translation found for keyguard_missing_sim_instructions (5210891509995942250) -->
+ <skip />
+ <!-- no translation found for keyguard_missing_sim_instructions_long (5968985489463870358) -->
+ <skip />
+ <!-- no translation found for keyguard_permanent_disabled_sim_message_short (8340813989586622356) -->
+ <skip />
+ <!-- no translation found for keyguard_permanent_disabled_sim_instructions (5892940909699723544) -->
+ <skip />
+ <!-- no translation found for keyguard_sim_locked_message (6875773413306380902) -->
+ <skip />
+ <!-- no translation found for keyguard_sim_puk_locked_message (3747232467471801633) -->
+ <skip />
+ <!-- no translation found for keyguard_sim_unlock_progress_dialog_message (7975221805033614426) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_widget_changed (5678624624681400191) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_add_widget (8273277058724924654) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_widget_empty_slot (1281505703307930757) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_unlock_area_expanded (2278106022311170299) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_unlock_area_collapsed (6366992066936076396) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_widget (6527131039741808240) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_user_selector (1226798370913698896) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_status (8008264603935930611) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_camera (8904231194181114603) -->
+ <skip />
+ <!-- no translation found for keygaurd_accessibility_media_controls (262209654292161806) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_widget_reorder_start (8736853615588828197) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_widget_reorder_end (7170190950870468320) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_widget_deleted (4426204263929224434) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_expand_lock_area (519859720934178024) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_slide_unlock (2959928478764697254) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_pattern_unlock (1490840706075246612) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_face_unlock (4817282543351718535) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_pin_unlock (2469687111784035046) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_password_unlock (7675777623912155089) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_pattern_area (7679891324509597904) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_slide_area (6736064494019979544) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_transport_prev_description (1337286538318543555) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_transport_next_description (7073928300444909320) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_transport_pause_description (8455979545295224302) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_transport_play_description (8146417789511154044) -->
+ <skip />
+ <!-- no translation found for keyguard_accessibility_transport_stop_description (7656358482980912216) -->
+ <skip />
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <!-- no translation found for keyboardview_keycode_alt (4856868820040051939) -->
+ <skip />
+ <!-- no translation found for keyboardview_keycode_cancel (1203984017245783244) -->
+ <skip />
+ <!-- no translation found for keyboardview_keycode_delete (3337914833206635744) -->
+ <skip />
+ <!-- no translation found for keyboardview_keycode_done (1992571118466679775) -->
+ <skip />
+ <!-- no translation found for keyboardview_keycode_mode_change (4547387741906537519) -->
+ <skip />
+ <!-- no translation found for keyboardview_keycode_shift (2270748814315147690) -->
+ <skip />
+ <!-- no translation found for keyboardview_keycode_enter (2985864015076059467) -->
+ <skip />
+ <!-- no translation found for description_target_unlock (2228524900439801453) -->
+ <skip />
+ <!-- no translation found for description_target_camera (969071997552486814) -->
+ <skip />
+ <!-- no translation found for description_target_silent (893551287746522182) -->
+ <skip />
+ <!-- no translation found for description_target_soundon (30052466675500172) -->
+ <skip />
+ <!-- no translation found for description_target_search (3091587249776033139) -->
+ <skip />
+ <!-- no translation found for description_direction_up (7169032478259485180) -->
+ <skip />
+ <!-- no translation found for description_direction_down (5087739728639014595) -->
+ <skip />
+ <!-- no translation found for description_direction_left (7207478719805562165) -->
+ <skip />
+ <!-- no translation found for description_direction_right (8034433242579600980) -->
+ <skip />
+ <!-- no translation found for user_switched (3768006783166984410) -->
+ <skip />
+ <!-- no translation found for kg_emergency_call_label (684946192523830531) -->
+ <skip />
+ <!-- no translation found for kg_forgot_pattern_button_text (8852021467868220608) -->
+ <skip />
+ <!-- no translation found for kg_wrong_pattern (1850806070801358830) -->
+ <skip />
+ <!-- no translation found for kg_wrong_password (2333281762128113157) -->
+ <skip />
+ <!-- no translation found for kg_wrong_pin (1131306510833563801) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_attempts_countdown (6358110221603297548) -->
+ <skip />
+ <!-- no translation found for kg_pattern_instructions (398978611683075868) -->
+ <skip />
+ <!-- no translation found for kg_sim_pin_instructions (2319508550934557331) -->
+ <skip />
+ <!-- no translation found for kg_pin_instructions (2377242233495111557) -->
+ <skip />
+ <!-- no translation found for kg_password_instructions (5753646556186936819) -->
+ <skip />
+ <!-- no translation found for kg_puk_enter_puk_hint (453227143861735537) -->
+ <skip />
+ <!-- no translation found for kg_puk_enter_pin_hint (7871604527429602024) -->
+ <skip />
+ <!-- no translation found for kg_enter_confirm_pin_hint (325676184762529976) -->
+ <skip />
+ <!-- no translation found for kg_sim_unlock_progress_dialog_message (8950398016976865762) -->
+ <skip />
+ <!-- no translation found for kg_password_wrong_pin_code (1139324887413846912) -->
+ <skip />
+ <!-- no translation found for kg_invalid_sim_pin_hint (8795159358110620001) -->
+ <skip />
+ <!-- no translation found for kg_invalid_sim_puk_hint (7553388325654369575) -->
+ <skip />
+ <!-- no translation found for kg_invalid_puk (3638289409676051243) -->
+ <skip />
+ <!-- no translation found for kg_invalid_confirm_pin_hint (7003469261464593516) -->
+ <skip />
+ <!-- no translation found for kg_login_too_many_attempts (6486842094005698475) -->
+ <skip />
+ <!-- no translation found for kg_login_instructions (1100551261265506448) -->
+ <skip />
+ <!-- no translation found for kg_login_username_hint (5718534272070920364) -->
+ <skip />
+ <!-- no translation found for kg_login_password_hint (9057289103827298549) -->
+ <skip />
+ <!-- no translation found for kg_login_submit_button (5355904582674054702) -->
+ <skip />
+ <!-- no translation found for kg_login_invalid_input (5754664119319872197) -->
+ <skip />
+ <!-- no translation found for kg_login_account_recovery_hint (5690709132841752974) -->
+ <skip />
+ <!-- no translation found for kg_login_checking_password (1052685197710252395) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_pin_attempts_dialog_message (8276745642049502550) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_password_attempts_dialog_message (7813713389422226531) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_pattern_attempts_dialog_message (74089475965050805) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_wipe (1575557200627128949) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_wipe (4051015943038199910) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_now_wiping (2072996269148483637) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_now_wiping (4817627474419471518) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_login (3253575572118914370) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_login (1437638152015574839) -->
+ <skip />
+ <!-- no translation found for kg_text_message_separator (4160700433287233771) -->
+ <skip />
+ <!-- no translation found for kg_reordering_delete_drop_target_text (7899202978204438708) -->
+ <skip />
+ <!-- no translation found for keyguard_transport_prev_description (8229108430245669854) -->
+ <skip />
+ <!-- no translation found for keyguard_transport_next_description (4299258300283778305) -->
+ <skip />
+ <!-- no translation found for keyguard_transport_pause_description (5093073338238310224) -->
+ <skip />
+ <!-- no translation found for keyguard_transport_play_description (2924628863741150956) -->
+ <skip />
+ <!-- no translation found for keyguard_transport_stop_description (3084179324810575787) -->
+ <skip />
+ <!-- no translation found for keyguard_carrier_default (8700650403054042153) -->
+ <skip />
+</resources>
diff --git a/packages/Keyguard/res/values-ro/activitystrings.xml b/packages/Keyguard/res/values-ro/activitystrings.xml
new file mode 100644
index 0000000..6d3447d
--- /dev/null
+++ b/packages/Keyguard/res/values-ro/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Fără securitate"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Parolă"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Model"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Alegeți un widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ro/strings.xml b/packages/Keyguard/res/values-ro/strings.xml
new file mode 100644
index 0000000..2430200
--- /dev/null
+++ b/packages/Keyguard/res/values-ro/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Introduceţi codul PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Introduceţi codul PUK şi noul cod PIN"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Codul PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Noul cod PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Atingeţi şi introduceţi parola"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Introduceţi parola pentru a debloca"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Introduceţi codul PIN pentru a debloca"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Cod PIN incorect."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Pentru a debloca, apăsaţi Meniu, apoi 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"S-a depăşit numărul maxim de încercări pentru Deblocare facială"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Încărcată"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Se încarcă, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Conectați încărcătorul."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Apăsați pe Meniu pentru a debloca."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rețea blocată"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Niciun card SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Tableta nu are card SIM."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Telefonul nu are card SIM."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Introduceți un card SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Cardul SIM lipsește sau nu poate fi citit. Introduceți un card SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Card SIM inutilizabil."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Cardul SIM este dezactivat definitiv."\n" Contactați furnizorul de servicii wireless pentru a obține alt card SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Cardul SIM este blocat."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Cardul SIM este blocat cu codul PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Se deblochează cardul SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d din %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Adăugaţi un widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Gol"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Zona de deblocare a fost extinsă."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Zona de deblocare a fost restrânsă."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Selector utilizator"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Stare"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Cameră foto"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Comenzi media"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"A început reordonarea widgeturilor."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Reordonarea widgeturilor s-a încheiat."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widgetul <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> a fost eliminat."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Extindeţi zona de deblocare."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Deblocare prin glisare."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Deblocare cu model."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Deblocare facială."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Deblocare cu PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Deblocare cu parolă."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Zonă model."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Zonă glisare."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Butonul Melodia anterioară"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Butonul Melodia următoare"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Butonul Întrerupeți"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Butonul Redați"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Butonul Opriți"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anulaţi"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ștergeţi"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Terminat"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Schimbarea modului"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Deblocaţi"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Cameră foto"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Silenţios"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Sunet activat"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Căutaţi"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Glisaţi în sus pentru <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Glisaţi în jos pentru <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Glisaţi spre stânga pentru <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Glisaţi spre dreapta pentru <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Utilizator curent: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Apel de urgenţă"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Model uitat"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Model greşit"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Parolă greşită"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Cod PIN greşit"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Încercaţi din nou peste <xliff:g id="NUMBER">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Desenaţi modelul"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduceţi codul PIN al cardului SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Introduceţi codul PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Introduceţi parola"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Cardul SIM este acum dezactivat. Introduceţi codul PUK pentru a continua. Contactaţi operatorul pentru mai multe detalii."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Introduceţi codul PIN dorit"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirmaţi codul PIN dorit"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Se deblochează cardul SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Cod PIN incorect."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Introduceţi un cod PIN format din 4 până la 8 cifre."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Codul PUK trebuie să aibă minimum 8 cifre."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Reintroduceţi codul PUK corect. Încercările repetate vor dezactiva definitiv cardul SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Codurile PIN nu coincid"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Prea multe încercări de desenare a modelului"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Pentru a debloca, conectaţi-vă cu Contul dvs. Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nume de utilizator (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Parolă"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Conectaţi-vă"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nume de utilizator sau parolă nevalide."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Aţi uitat numele de utilizator sau parola?"\n"Accesaţi "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Se verifică contul…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Aţi introdus incorect codul PIN de <xliff:g id="NUMBER_0">%d</xliff:g> ori."\n\n"Încercaţi din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Aţi introdus incorect parola de <xliff:g id="NUMBER_0">%d</xliff:g> ori. "\n\n"Încercaţi din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. "\n\n"Încercaţi din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Aţi efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Aţi efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Aţi efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Aţi efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Telefonul va fi acum resetat la setările prestabilite din fabrică."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi tableta cu ajutorul unui cont de e-mail."\n\n" Încercaţi din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi telefonul cu ajutorul unui cont de e-mail."\n\n" Încercaţi din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminaţi"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Butonul Melodia anterioară"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Butonul Melodia următoare"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Butonul Întrerupeți"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Butonul Redați"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Butonul Opriți"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Fără serviciu."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ru/activitystrings.xml b/packages/Keyguard/res/values-ru/activitystrings.xml
new file mode 100644
index 0000000..002cd56
--- /dev/null
+++ b/packages/Keyguard/res/values-ru/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Защита отключена"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN-код"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Пароль"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Графический ключ"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN-код SIM-карты"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK-код SIM-карты"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Выбор виджета..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-ru/strings.xml b/packages/Keyguard/res/values-ru/strings.xml
new file mode 100644
index 0000000..3d482d9
--- /dev/null
+++ b/packages/Keyguard/res/values-ru/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Введите PIN-код"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Введите PUK-код и новый PIN-код"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-код"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Новый PIN-код"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Нажмите для ввода пароля"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Введите пароль"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Введите PIN-код"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неверный PIN-код."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Для разблокировки нажмите \"Меню\", а затем 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Все попытки войти с помощью Фейсконтроля использованы"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Батарея заряжена"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Идет зарядка (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Подключите зарядное устройство."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Для разблокировки нажмите \"Меню\"."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Сеть заблокирована"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Нет SIM-карты"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Нет SIM-карты."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Нет SIM-карты."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Вставьте SIM-карту."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-карта отсутствует или недоступна. Вставьте SIM-карту."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM-карта непригодна."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM-карта окончательно заблокирована."\n"Чтобы получить новую, обратитесь к своему оператору."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-карта заблокирована."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Для разблокировки SIM-карты требуется PUK-код."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Разблокировка SIM-карты…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Виджет %2$d из %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Добавить виджет"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Пусто"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Область разблокировки развернута"</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Область разблокировки свернута"</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Виджет \"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>\""</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Выбор аккаунта"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Статус"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Камера"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Управление блокировкой"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Начато переопределение порядка виджетов"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Порядок виджетов определен"</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Виджет <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> удален"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Развернуть области разблокировки"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Прокрутка"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Графический ключ"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Фейсконтроль"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN-код"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Пароль"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Область ввода графического ключа"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Область слайдера"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Кнопка перехода к предыдущему треку"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Кнопка перехода к следующему треку"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Кнопка паузы"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Кнопка воспроизведения"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Кнопка выключения"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"АБВ"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Клавиша ALT"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Отмена"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Клавиша удаления"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Готово"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Клавиша смены режима"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Клавиша смены регистра"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Клавиша ввода"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Разблокировать"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Камера"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Без звука"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Включить звук"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Поиск"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Проведите вверх, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Проведите вниз, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Проведите влево, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Проведите вправо, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Выбран аккаунт пользователя <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Экстренный вызов"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Забыли графический ключ?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Неправильный графический ключ"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Неправильный пароль"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Неправильный PIN-код"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Повторите попытку через <xliff:g id="NUMBER">%d</xliff:g> сек."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Введите графический ключ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Введите PIN-код SIM-карты"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Введите PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Введите пароль"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM-карта заблокирована. Чтобы продолжить, введите PUK-код. За подробной информацией обратитесь к своему оператору связи."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Введите желаемый PIN-код"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Введите PIN-код ещё раз"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Разблокировка SIM-карты…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Неверный PIN-код."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Введите PIN-код (от 4 до 8 цифр)."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK-код должен содержать не менее 8 символов."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Введите правильный PUK-код. После нескольких неудачных попыток SIM-карта будет заблокирована."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-коды не совпадают"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Слишком много попыток ввода графического ключа"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Чтобы разблокировать устройство, войдите в свой аккаунт Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Имя пользователя (эл. почта)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Пароль"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Войти"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Неверное имя пользователя или пароль."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Забыли имя пользователя или пароль?"\n"Перейдите на страницу "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Проверка данных…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали PIN-код. "\n\n"Повтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали пароль."\n\n"Повтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ."\n\n"Повтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз не смогли разблокировать планшетный ПК. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз не смогли разблокировать телефон. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Вы <xliff:g id="NUMBER">%d</xliff:g> раз не смогли разблокировать планшетный ПК. Будут восстановлены заводские настройки."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Вы <xliff:g id="NUMBER">%d</xliff:g> раз не смогли разблокировать телефон. Будут восстановлены заводские настройки."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток для разблокировки планшетного ПК потребуется войти в аккаунт Google."\n\n"Повтор через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток для разблокировки телефона потребуется войти в аккаунт Google."\n\n"Повтор через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Удалить"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Кнопка перехода к предыдущему треку"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Кнопка перехода к следующему треку"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Кнопка паузы"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Кнопка воспроизведения"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Кнопка выключения"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Нет сигнала."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sk/activitystrings.xml b/packages/Keyguard/res/values-sk/activitystrings.xml
new file mode 100644
index 0000000..33f2228
--- /dev/null
+++ b/packages/Keyguard/res/values-sk/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Bez zabezpečenia"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"Kód PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Heslo"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Vzor"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN karty SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK karty SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Vyberte miniaplikáciu..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sk/strings.xml b/packages/Keyguard/res/values-sk/strings.xml
new file mode 100644
index 0000000..66887bc
--- /dev/null
+++ b/packages/Keyguard/res/values-sk/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Zadajte kód PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Zadajte kód PUK a nový kód PIN"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Kód PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nový kód PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Dotknutím zadajte heslo"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Zadajte heslo na odomknutie"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Zadajte kód PIN na odomknutie"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Nesprávny kód PIN."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Ak chcete telefón odomknúť, stlačte Menu a následne 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Prekročili ste maximálny povolený počet pokusov o odomknutie tvárou"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Batéria je nabitá"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Nabíjanie, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Pripojte nabíjačku."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Telefón odomknete stlačením tlačidla Menu."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Sieť je zablokovaná"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Nie je vložená karta SIM."</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"V tablete nie je žiadna karta SIM."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"V telefóne nie je žiadna karta SIM."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Vložte kartu SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Karta SIM chýba alebo sa z nej nedá čítať. Vložte kartu SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Karta SIM je nepoužiteľná."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Vaša karta SIM bola natrvalo zakázaná."\n"Ak chcete získať inú kartu SIM, kontaktujte svojho poskytovateľa bezdrôtových služieb."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Karta SIM je uzamknutá."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Karta SIM je uzamknutá pomocou kódu PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Prebieha odomykanie karty SIM..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Miniaplikácia %2$d z %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Pridať miniaplikáciu."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Prázdne"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Oblasť na odomknutie bola rozšírená."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Oblasť na odomknutie bola zúžená."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Miniaplikácia <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Výber používateľa"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Stav"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Fotoaparát"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Ovládacie prvky médií"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Zmena usporiadania miniaplikácií sa začala."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Zmena usporiadania miniaplikácií sa skončila."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Miniaplikácia <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> bola odstránená."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Rozšíriť oblasť na odomknutie."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Odomknutie prejdením prstom."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Odomknutie vzorom."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Odomknutie tvárou."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Odomknutie kódom PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Odomknutie heslom."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Oblasť na zadanie bezpečnostného vzoru."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Oblasť na prejdenie prstom."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Tlačidlo Predchádzajúca stopa"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Tlačidlo Ďalšia stopa"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Tlačidlo Pozastaviť"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Tlačidlo Prehrať"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Tlačidlo Zastaviť"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Zrušiť"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Odstrániť"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Hotovo"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Zmena režimu"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Odomknúť"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Fotoaparát"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Tichý"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Zapnúť zvuk"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Vyhľadávanie"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Prejdite prstom nahor: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Prejdite prstom nadol: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Prejdite prstom doľava: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Prejdite prstom doprava: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Aktuálny používateľ je <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Tiesňové volanie"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Nepamätám si vzor"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Nesprávny vzor"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nesprávne heslo"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Nesprávny kód PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Skúste to znova o <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Nakreslite svoj vzor"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Zadajte kód PIN karty SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Zadajte kód PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Zadajte heslo"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Karta SIM je teraz zakázaná. Ak chcete pokračovať, zadajte kód PUK. Podrobné informácie získate od operátora."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Zadajte požadovaný kód PIN"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potvrďte požadovaný kód PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Prebieha odomykanie karty SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Nesprávny kód PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Zadajte kód PIN s dĺžkou 4 až 8 číslic."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Kód PUK musí obsahovať 8 alebo viac číslic."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Znova zadajte správny kód PUK. Opakované pokusy zakážu kartu SIM natrvalo."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Kódy PIN sa nezhodujú"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Príliš veľa pokusov o nakreslenie vzoru"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Ak chcete telefón odomknúť, prihláste sa pomocou svojho účtu Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Používateľské meno (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Heslo"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prihlásiť sa"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Neplatné používateľské meno alebo heslo."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zabudli ste svoje používateľské meno alebo heslo?"\n" Navštívte stránky "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Prebieha kontrola účtu..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávny kód PIN. "\n\n"Skúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávne heslo. "\n\n"Skúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste použili nesprávny bezpečnostný vzor. "\n\n"Skúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa v tablete obnovia predvolené továrenské nastavenia a všetky používateľské údaje budú stratené."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa v telefóne obnovia predvolené továrenské nastavenia a všetky používateľské údaje budú stratené."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER">%d</xliff:g>-krát. V tablete sa teraz obnovia predvolené továrenské nastavenia."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER">%d</xliff:g>-krát. V telefóne sa teraz obnovia predvolené továrenské nastavenia."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po ďalších <xliff:g id="NUMBER_1">%d</xliff:g> neúspešných pokusoch sa zobrazí výzva na odomknutie tabletu pomocou e-mailového účtu."\n\n" Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou e-mailového účtu."\n\n" Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odstrániť"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Tlačidlo Predchádzajúca stopa"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Tlačidlo Ďalšia stopa"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Tlačidlo Pozastaviť"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Tlačidlo Prehrať"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Tlačidlo Zastaviť"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Žiadny signál"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sl/activitystrings.xml b/packages/Keyguard/res/values-sl/activitystrings.xml
new file mode 100644
index 0000000..2c60219
--- /dev/null
+++ b/packages/Keyguard/res/values-sl/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Brez varnosti"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Geslo"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Vzorec"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN za kartico SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK za kartico SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Izberite pripomoček ..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sl/strings.xml b/packages/Keyguard/res/values-sl/strings.xml
new file mode 100644
index 0000000..cdd5ca7b
--- /dev/null
+++ b/packages/Keyguard/res/values-sl/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Vnesite kodo PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Vnesite kodo PUK in novo kodo PIN"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Koda PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Nova koda PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Dotaknite se za vnos gesla"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Vnesite geslo za odklepanje"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Vnesite PIN za odklepanje"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Napačna koda PIN."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Če želite telefon odkleniti, pritisnite meni in nato 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Presegli ste dovoljeno število poskusov odklepanja z obrazom"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Napolnjeno"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Polnjenje, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Priključite napajalnik."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Če želite odkleniti, pritisnite meni."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Omrežje je zaklenjeno"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Ni kartice SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"V tabličnem računalniku ni kartice SIM."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"V telefonu ni kartice SIM."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Vstavite kartico SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Kartice SIM ni ali je ni mogoče prebrati. Vstavite jo."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Neuporabna kartica SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Kartica SIM je trajno onemogočena."\n" Obrnite se na operaterja za drugo."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Kartica SIM je zaklenjena."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Kartica SIM je zaklenjena s kodo PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Odklepanje kartice SIM …"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Pripomoček %2$d za %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Dodajanje pripomočka."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Prazno"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Območje odklepanja razširjeno."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Območje odklepanja strnjeno."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Pripomoček <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Izbirnik uporabnika"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Stanje"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Fotoaparat"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Kontrolniki predstavnosti"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Prerazporejanje pripomočkov začeto."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Prerazporejanje pripomočkov končano."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Pripomoček <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> izbrisan."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Razširitev območja odklepanja."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Odklepanje s podrsanjem."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Odklepanje z vzorcem."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Odklepanje z obrazom."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Odklepanje s kodo PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Odklepanje z geslom."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Območje vzorca."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Območje podrsanja."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Gumb za prejšnjo skladbo"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Gumb za naslednjo skladbo"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Gumb za začasno ustavitev"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Gumb za predvajanje"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Gumb za ustavitev"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Tipka Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Prekliči"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Tipka Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Končano"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Sprememba načina"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Tipka Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Tipka Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Odkleni"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Fotoaparat"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Tiho"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Vklopljen zvok"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Iskanje"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Povlecite navzgor za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Povlecite navzdol za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Povlecite v levo za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Povlecite v desno za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Trenutni uporabnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Klic v sili"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Pozabljen vzorec"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Napačen vzorec"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Napačno geslo"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Napačen PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Čez <xliff:g id="NUMBER">%d</xliff:g> sekund poskusite znova."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Narišite vzorec"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Vnesite PIN za kartico SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Vnesite PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Vnesite geslo"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Kartica SIM je onemogočena. Če želite nadaljevati, vnesite kodo PUK. Za dodatne informacije se obrnite na operaterja."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Vnesite želeno kodo PIN"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potrdite želeno kodo PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Odklepanje kartice SIM ..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Napačna koda PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Vnesite PIN, ki vsebuje od štiri do osem številk."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Koda PUK mora vsebovati 8 ali več števk."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Vnovič vnesite pravilno kodo PUK. Večkratni poskusi bodo trajno onemogočili kartico SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Kodi PIN se ne ujemata"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Preveč poskusov vzorca"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Če želite odkleniti napravo, se prijavite z Google Računom."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Uporabniško ime (e-pošta)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Geslo"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prijava"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Neveljavno uporabniško ime ali geslo."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ali ste pozabili uporabniško ime ali geslo?"\n"Obiščite "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Preverjanje računa ..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. "\n\n"Znova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Geslo ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. "\n\n"Znova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vzorec za odklepanje ste nepravilno narisali <xliff:g id="NUMBER_0">%d</xliff:g>-krat. "\n\n"Poskusite znova čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablični računalnik ste poskusili <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefon ste poskusili <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablični računalnik ste poskusili <xliff:g id="NUMBER">%d</xliff:g>-krat napačno odkleniti, zato bo ponastavljen na privzete tovarniške nastavitve."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefon ste poskusili <xliff:g id="NUMBER">%d</xliff:g>-krat napačno odkleniti, zato bo ponastavljen na privzete tovarniške nastavitve."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%d</xliff:g> neuspešnih poskusih boste pozvani, da tablični računalnik odklenete z e-poštnim računom."\n\n"Poskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete telefon z Googlovimi podatki za prijavo."\n\n"Poskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odstrani"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Gumb za prejšnjo skladbo"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Gumb za naslednjo skladbo"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Gumb za začasno ustavitev"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Gumb za predvajanje"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Gumb za ustavitev"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Ni storitve."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sr/activitystrings.xml b/packages/Keyguard/res/values-sr/activitystrings.xml
new file mode 100644
index 0000000..34802df
--- /dev/null
+++ b/packages/Keyguard/res/values-sr/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Без заштите"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Лозинка"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Шаблон"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN SIM картице"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK SIM картице"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Изабери виџет..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sr/strings.xml b/packages/Keyguard/res/values-sr/strings.xml
new file mode 100644
index 0000000..0f08426
--- /dev/null
+++ b/packages/Keyguard/res/values-sr/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Унесите PIN кôд"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Унесите PUK и нови PIN кôд"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK кôд"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Нови PIN кôд"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Додирните да бисте унели лозинку"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Откуцајте лозинку да бисте откључали"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Унесите PIN за откључавање"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN кôд је нетачан."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Да бисте откључали, притисните „Мени“, а затим 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Премашен је највећи дозвољени број покушаја Откључавања лицем"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Напуњено"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Пуњење, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Повежите пуњач."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Притисните Мени да бисте откључали."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Мрежа је закључана"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Нема SIM картице"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"У таблету нема SIM картице."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"У телефону нема SIM картице."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Уметните SIM картицу."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM картица недостаје или не може да се прочита. Уметните SIM картицу."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM картица је неупотребљива."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM картица је трајно онемогућена."\n" Обратите се добављачу услуге бежичне мреже да бисте добили другу SIM картицу."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM картица је закључана."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM картица је закључана PUK кодом."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Откључавање SIM картице…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Виџет %2$d од %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Додај виџет."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Празно"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Област откључавања је проширена."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Област откључавања је скупљена."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Виџет <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Избор корисника"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Статус"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Камера"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Контроле за медије"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Започела је промена редоследа виџета."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Промена редоследа виџета је завршена."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Виџет <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> је избрисан."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Прошири област откључавања."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Откључавање превлачењем."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Откључавање шаблоном."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Откључавање лицем."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Откључавање PIN-ом."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Откључавање лозинком."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Област шаблона."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Област превлачења."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Дугме за претходну песму"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Дугме за следећу песму"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Дугме за паузу"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Дугме за репродукцију"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Дугме за заустављање"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Откажи"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Избриши"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Готово"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Промена режима"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Откључај"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Камера"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Нечујно"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Укључи звук"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Претрага"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Превуците нагоре за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Превуците надоле за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Превуците улево за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Превуците удесно за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Актуелни корисник <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Хитни позив"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Заборављени шаблон"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Погрешан шаблон"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Погрешна лозинка"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Погрешан PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Покушајте поново за <xliff:g id="NUMBER">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Нацртајте шаблон"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Унесите PIN SIM картице"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Унесите PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Унесите лозинку"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM картица је сада онемогућена. Унесите PUK кôд да бисте наставили. За детаље контактирајте оператера."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Унесите жељени PIN кôд"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Потврдите жељени PIN кôд"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Откључавање SIM картице…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN кôд је нетачан."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Унесите PIN који има од 4 до 8 бројева."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK кôд треба да има 8 или више бројева."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Поново унесите исправни PUK кôд. Поновљени покушаји ће трајно онемогућити SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN кодови се не подударају"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Превише покушаја уноса шаблона"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Да бисте откључали, пријавите се помоћу Google налога."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Корисничко име (адреса е-поште)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Лозинка"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Пријави ме"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Неважеће корисничко име или лозинка."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Заборавили сте корисничко име или лозинку?"\n"Посетите адресу "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Провера налога…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Унели сте PIN неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. "\n\n"Покушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Унели сте лозинку неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. "\n\n"Покушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. "\n\n"Покушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Покушали сте да откључате таблет неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја таблет ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Покушали сте да откључате телефон неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја телефон ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Покушали сте да откључате таблет неисправно <xliff:g id="NUMBER">%d</xliff:g> пута. Таблет ће сада бити враћен на подразумевана фабричка подешавања."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Покушали сте да откључате телефон неисправно <xliff:g id="NUMBER">%d</xliff:g> пута. Телефон ће сада бити враћен на подразумевана фабричка подешавања."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу налога е-поште."\n\n"Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу налога е-поште."\n\n"Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Уклони"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Дугме за претходну песму"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Дугме за следећу песму"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Дугме за паузу"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Дугме за репродукцију"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Дугме за заустављање"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Ван мреже сте."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sv/activitystrings.xml b/packages/Keyguard/res/values-sv/activitystrings.xml
new file mode 100644
index 0000000..e664383
--- /dev/null
+++ b/packages/Keyguard/res/values-sv/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Ingen säkerhet"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN-kod"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Lösenord"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Grafiskt lösenord"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN-kod för SIM-kort"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK-kod för SIM-kort"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Välj widget ..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sv/strings.xml b/packages/Keyguard/res/values-sv/strings.xml
new file mode 100644
index 0000000..a08cdfb
--- /dev/null
+++ b/packages/Keyguard/res/values-sv/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Ange PIN-kod"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Ange PUK-koden och en ny PIN-kod"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-kod"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Ny PIN-kod"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Tryck om du vill ange lösenord"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Ange lösenord för att låsa upp"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Ange PIN-kod för att låsa upp"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Fel PIN-kod."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Tryck på Menu och sedan på 0 om du vill låsa upp."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har försökt låsa upp med Ansiktslås för många gånger"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Batteriet har laddats"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Laddar, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Anslut din laddare."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Tryck på Meny om du vill låsa upp."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Nätverk låst"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Inget SIM-kort"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Inget SIM-kort i surfplattan."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Inget SIM-kort i mobilen."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Sätt i ett SIM-kort."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-kort saknas eller kan inte läsas. Sätt i ett SIM-kort."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Oanvändbart SIM-kort."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM-kortet har inaktiverats permanent."\n" Beställ ett nytt SIM-kort från din operatör."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kortet är låst."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kortet är PUK-låst."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Låser upp SIM-kort …"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d av %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Lägg till en widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tom"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Expanderad upplåsningsyta."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Komprimerad upplåsningsyta."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Widget för <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Användarväljare"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Status"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Mediereglage"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Ändring av widgetarnas ordning har påbörjats."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Ändring av widgetarnas ordning har avslutats."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Widgeten <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> har tagits bort."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Expandera upplåsningsytan."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Lås upp genom att dra."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Lås upp med grafiskt lösenord."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Lås upp med Ansiktslås."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Lås upp med PIN-kod."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Lås upp med lösenord."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Fält för grafiskt lösenord."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Fält med dragreglage."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Knapp för föregående spår"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Knapp för nästa spår"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Pausknappen"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Uppspelningsknappen"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Stoppknappen"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Avbryt"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Klar"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Funktionsändring"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Skift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Retur"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Lås upp"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Tyst"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Ljud på"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Sök"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Dra uppåt för <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Dra nedåt för <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Dra åt vänster för <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Dra åt höger för <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Nuvarande användare: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Nödsamtal"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Har du glömt ditt grafiska lösenord?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Fel grafiskt lösenord"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Fel lösenord"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Fel PIN-kod"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Rita ditt grafiska lösenord"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ange PIN-kod för SIM-kortet"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ange PIN-kod"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ange lösenord"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM-kortet är nu inaktiverat. Ange PUK-koden om du vill fortsätta. Kontakta operatören om du vill få mer information."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Ange önskad PIN-kod"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Bekräfta önskad PIN-kod"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Låser upp SIM-kort …"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Fel PIN-kod."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Ange en PIN-kod med 4 till 8 siffror."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK-koden ska vara minst åtta siffror."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Ange rätt PUK-kod igen. Om försöken upprepas inaktiveras SIM-kortet permanent."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-koderna stämmer inte överens"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"För många försök med grafiskt lösenord"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Logga in med ditt Google-konto om du vill låsa upp."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Användarnamn (e-post)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Lösenord"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Logga in"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ogiltigt användarnamn eller lösenord."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glömt ditt användarnamn eller lösenord?"\n"Besök "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Kontot kontrolleras …"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs surfplattan till fabriksinställningarna. Du förlorar då alla användardata."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs mobilen till fabriksinställningarna. Du förlorar då alla användardata."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp surfplattan med ett e-postkonto."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ta bort"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Knapp för föregående spår"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Knapp för nästa spår"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Pausknappen"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Uppspelningsknappen"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Stoppknappen"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Ingen tjänst."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sw/activitystrings.xml b/packages/Keyguard/res/values-sw/activitystrings.xml
new file mode 100644
index 0000000..357b911
--- /dev/null
+++ b/packages/Keyguard/res/values-sw/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Hakuna usalama"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Nenosiri"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Ruwaza"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN ya SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK ya SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Chagua wijeti..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sw/strings.xml b/packages/Keyguard/res/values-sw/strings.xml
new file mode 100644
index 0000000..014c732
--- /dev/null
+++ b/packages/Keyguard/res/values-sw/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Ingiza msimbo wa PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Ingiza PUK na msimbo mpya wa PIN"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Msimbo wa PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Msimbo mpya wa PIN"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Gusa kuingiza nenosiri "</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Charaza nenosiri ili kufungua"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Ingiza PIN ili kufungua"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Msimbo wa PIN usio sahihi."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Ili kufungua, bofya Menyu kisha 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Majaribio ya Juu ya Kufungua Uso yamezidishwa"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Imechajiwa"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Inachaji, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Unganisha chaja yako."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Bonyeza Menyu ili kufungua."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Mtandao umefungwa"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Hakuna SIM kadi"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Hakuna SIM kadi katika kompyuta ndogo."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Hakuna SIM kadi kwenye simu."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Ingiza SIM kadi."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM kadi haiko au haisomeki. Ingiza SIM kadi."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM kadi isiyotumika."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM kadi yako imefungwa kabisa."\n" Wasiliana na mtoa huduma wako wa pasi waya ili upate SIM kadi nyingine."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kadi imefungwa."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kadi imefungwa na PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Inafungua SIM kadi..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Wiji %2$d ya %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Ongeza wiji"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tupu"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Eneo la kufungua limepanuliwa."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Eneo la kufungua limekunjwa."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ya wiji."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Kiteuzi cha mtumiaji"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Hali"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Vidhibiti vya media"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Upangaji upya wa wiji umeanza."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Upangaji upya wa wiji umekamilika."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Wiji <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> imefutwa."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Panua eneo la kufungua."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Kufungua slaidi."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Kufungua kwa ruwaza."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Kufungua kwa uso."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Kufungua kwa PIN."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Kufungua kwa nenosiri."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Eneo la ruwaza."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Eneo la slaidi."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Kitufe cha wimbo uliotangulia"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Kitufe cha wimbo unaofuata"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Kitufe cha kusitisha"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Kitufe cha kucheza"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Kitufe cha kusitisha"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Ghairi"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Futa"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Imefanyika"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Modi ya mabadiliko"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Songa"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Fungua"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Kimya"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Sauti imewashwa"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Tafuta"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Sogeza juu kwa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Sogeza chini kwa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Sogeza kushoto kwa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Sogeza kulika kwa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Mtumiaji wa sasa <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Simu ya dharura"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Umesahau Ruwaza"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Mchoro Usio sahihi"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nenosiri Lisilo sahihi"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN isiyo sahihi"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Jaribu tena baada ya sekunde <xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Chora ruwaza yako"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ingiza PIN ya SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ingiza PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ingiza Nenosiri"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM sasa imelemazwa. Ingiza msimbo wa PUK ili kuendelea. Wasiliana na mtoa huduma kwa maelezo."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Ingiza msimbo wa PIN unaopendelewa"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Thibitisha msimbo wa PIN unaopendelewa"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Inafungua SIM kadi..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Msimbo wa PIN usio sahihi."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Charaza PIN iliyo na tarakimu kati ya 4 na 8."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Msimbo wa PUK unafaa kuwa na nambari 8 au zaidi."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Ingiza upya msimbo sahihi wa PUK. Majaribio yanayorudiwa yatalemaza SIM kabisa."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Misimbo ya PIN haifanani"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Majaribio mengi mno ya mchoro"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Ili kufungua, ingia kwa Akaunti yako ya Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Jina la mtumiaji (barua pepe)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Nenosiri"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Ingia"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Jina la mtumiaji au nenosiri batili."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Je, umesahau jina lako la mtumiaji au nenosiri?"\n"Tembela "<b>"Bgoogle.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Inakagua akaunti…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Umechora ruwaza yako ya kufunga kwa makosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> zaidi yasiyofaulu, kompyuta ndogo itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani data yote ya mtumiaji itapotea."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> zaidi yasiyofaulu, simu itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani na data yote ya mtumiaji itapotea."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa kompyuta ndogo itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa simu itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Umekosea katika kuweka mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> bila kufaulu, utaombwa kufungua kompyuta yako ndogo kwa kutumia akaunti yako ya barua pepe."\n\n" Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> yasiyofaulu, utaombwa kufungua simu yako kwa kutumia akaunti ya barua pepe."\n\n" Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ondoa"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Kitufe cha wimbo wa awali"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Kitufe cha wimbo unaofuata"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Kitufe cha kusitisha"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Kitufe cha kucheza"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Kitufe cha kusimamisha"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Hakuna huduma."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-sw380dp-land/dimens.xml b/packages/Keyguard/res/values-sw380dp-land/dimens.xml
new file mode 100644
index 0000000..20eb1be
--- /dev/null
+++ b/packages/Keyguard/res/values-sw380dp-land/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Top margin for the clock view -->
+ <dimen name="kg_clock_top_margin">48dp</dimen>
+</resources>
diff --git a/packages/Keyguard/res/values-sw380dp/dimens.xml b/packages/Keyguard/res/values-sw380dp/dimens.xml
new file mode 100644
index 0000000..fc0e85d
--- /dev/null
+++ b/packages/Keyguard/res/values-sw380dp/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+ <!-- Width of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) -->
+ <dimen name="keyguard_security_width">340dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values-sw600dp-land/arrays.xml b/packages/Keyguard/res/values-sw600dp-land/arrays.xml
new file mode 100644
index 0000000..5550216
--- /dev/null
+++ b/packages/Keyguard/res/values-sw600dp-land/arrays.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Resources for GlowPadView in LockScreen -->
+ <array name="lockscreen_targets_when_silent">
+ <item>@drawable/ic_lockscreen_unlock</item>
+ <item>@null</item>
+ <item>@drawable/ic_lockscreen_soundon</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_when_silent">
+ <item>@string/description_target_unlock</item>
+ <item>@null</item>
+ <item>@string/description_target_soundon</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_direction_descriptions">
+ <item>@string/description_direction_right</item>
+ <item>@null</item>
+ <item>@string/description_direction_left</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_targets_when_soundon">
+ <item>@drawable/ic_lockscreen_unlock</item>
+ <item>@null</item>
+ <item>@drawable/ic_lockscreen_silent</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_when_soundon">
+ <item>@string/description_target_unlock</item>
+ <item>@null</item>
+ <item>@string/description_target_silent</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_targets_with_camera">
+ <item>@drawable/ic_lockscreen_unlock</item>
+ <item>@drawable/ic_action_assist_generic</item>
+ <item>@drawable/ic_lockscreen_camera</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_with_camera">
+ <item>@string/description_target_unlock</item>
+ <item>@string/description_target_search</item>
+ <item>@string/description_target_camera</item>
+ <item>@null</item>
+ </array>
+
+</resources>
diff --git a/packages/Keyguard/res/values-sw600dp-land/dimens.xml b/packages/Keyguard/res/values-sw600dp-land/dimens.xml
new file mode 100644
index 0000000..5507e5f
--- /dev/null
+++ b/packages/Keyguard/res/values-sw600dp-land/dimens.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.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.
+*/
+-->
+<resources>
+ <!-- Top margin for the clock view -->
+ <dimen name="kg_clock_top_margin">85dp</dimen>
+
+ <!-- Size of margin on the right of keyguard's status view -->
+ <dimen name="kg_status_line_font_right_margin">16dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values-sw600dp-land/integers.xml b/packages/Keyguard/res/values-sw600dp-land/integers.xml
new file mode 100644
index 0000000..b724c90
--- /dev/null
+++ b/packages/Keyguard/res/values-sw600dp-land/integers.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <integer name="kg_widget_region_weight">50</integer>
+ <integer name="kg_security_flipper_weight">50</integer>
+ <integer name="kg_glowpad_rotation_offset">0</integer>
+</resources>
diff --git a/packages/Keyguard/res/values-sw600dp-port/integers.xml b/packages/Keyguard/res/values-sw600dp-port/integers.xml
new file mode 100644
index 0000000..65b854a
--- /dev/null
+++ b/packages/Keyguard/res/values-sw600dp-port/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <integer name="kg_widget_region_weight">46</integer>
+ <integer name="kg_security_flipper_weight">54</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values-sw600dp/alias.xml b/packages/Keyguard/res/values-sw600dp/alias.xml
new file mode 100644
index 0000000..c3ecbb9
--- /dev/null
+++ b/packages/Keyguard/res/values-sw600dp/alias.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Alias used to reference one of two possible layouts in keyguard. -->
+ <item type="layout" name="keyguard_eca">@layout/keyguard_emergency_carrier_area</item>
+</resources>
diff --git a/packages/Keyguard/res/values-sw600dp/bools.xml b/packages/Keyguard/res/values-sw600dp/bools.xml
new file mode 100644
index 0000000..ddc48c5
--- /dev/null
+++ b/packages/Keyguard/res/values-sw600dp/bools.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<resources>
+ <bool name="target_honeycomb_needs_options_menu">false</bool>
+ <bool name="show_ongoing_ime_switcher">true</bool>
+ <bool name="kg_share_status_area">false</bool>
+ <bool name="kg_sim_puk_account_full_screen">false</bool>
+ <bool name="kg_show_ime_at_screen_on">false</bool>
+ <!-- No camera for you, tablet user -->
+ <bool name="kg_enable_camera_default_widget">false</bool>
+ <bool name="kg_center_small_widgets_vertically">true</bool>
+ <bool name="kg_top_align_page_shrink_on_bouncer_visible">false</bool>
+</resources>
diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..c0e3937
--- /dev/null
+++ b/packages/Keyguard/res/values-sw600dp/dimens.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.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.
+*/
+-->
+<resources>
+ <!-- Size of clock font in LockScreen. -->
+ <dimen name="keyguard_pattern_unlock_clock_font_size">112sp</dimen>
+
+ <!-- Size of lockscreen outerring on unsecure unlock LockScreen -->
+ <dimen name="keyguard_lockscreen_outerring_diameter">364dp</dimen>
+
+ <!-- Height of FaceUnlock view in keyguard -->
+ <dimen name="face_unlock_height">430dip</dimen>
+
+ <!-- target placement radius for GlowPadView. Should be 1/2 of outerring diameter. -->
+ <dimen name="glowpadview_target_placement_radius">182dip</dimen>
+
+ <!-- Size of status line font in LockScreen. -->
+ <dimen name="keyguard_pattern_unlock_status_line_font_size">14sp</dimen>
+
+ <!-- Keyguard dimensions -->
+ <!-- Size of the clock font in keyguard's status view -->
+ <dimen name="kg_status_clock_font_size">141dp</dimen>
+
+ <!-- Size of the date font in keyguard's status view -->
+ <dimen name="kg_status_date_font_size">25.5dp</dimen>
+
+ <!-- Size of the generic status lines keyguard's status view -->
+ <dimen name="kg_status_line_font_size">16sp</dimen>
+
+ <!-- Top margin for the clock view -->
+ <dimen name="kg_clock_top_margin">0dp</dimen>
+
+ <!-- Size of margin on the right of keyguard's status view -->
+ <dimen name="kg_status_line_font_right_margin">50dp</dimen>
+
+ <!-- Horizontal padding for the widget pager -->
+ <dimen name="kg_widget_pager_horizontal_padding">24dp</dimen>
+
+ <!-- Top padding for the widget pager -->
+ <dimen name="kg_widget_pager_top_padding">0dp</dimen>
+
+ <!-- Bottom padding for the widget pager -->
+ <dimen name="kg_widget_pager_bottom_padding">0dp</dimen>
+
+ <!-- Top margin for the runway lights. We add a negative margin in large
+ devices to account for the widget pager padding -->
+ <dimen name="kg_runway_lights_top_margin">-10dp</dimen>
+
+ <!-- Margin around the various security views -->
+ <dimen name="keyguard_security_view_margin">12dp</dimen>
+
+ <!-- Margin around the various security views -->
+ <dimen name="keyguard_muliuser_selector_margin">12dp</dimen>
+
+</resources>
diff --git a/packages/Keyguard/res/values-sw600dp/integers.xml b/packages/Keyguard/res/values-sw600dp/integers.xml
new file mode 100644
index 0000000..de9829c
--- /dev/null
+++ b/packages/Keyguard/res/values-sw600dp/integers.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <integer name="kg_carousel_angle">60</integer>
+</resources>
diff --git a/packages/Keyguard/res/values-sw720dp-land/dimens.xml b/packages/Keyguard/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 0000000..14726ab
--- /dev/null
+++ b/packages/Keyguard/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Top margin for the clock view -->
+ <dimen name="kg_clock_top_margin">174dp</dimen>
+
+ <!-- Size of margin on the right of keyguard's status view -->
+ <dimen name="kg_status_line_font_right_margin">16dp</dimen>
+
+ <!-- Horizontal padding for the widget pager -->
+ <dimen name="kg_widget_pager_horizontal_padding">32dp</dimen>
+</resources>
diff --git a/packages/Keyguard/res/values-sw720dp-port/integers.xml b/packages/Keyguard/res/values-sw720dp-port/integers.xml
new file mode 100644
index 0000000..5f85f71
--- /dev/null
+++ b/packages/Keyguard/res/values-sw720dp-port/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <integer name="kg_widget_region_weight">48</integer>
+ <integer name="kg_security_flipper_weight">52</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values-sw720dp/dimens.xml b/packages/Keyguard/res/values-sw720dp/dimens.xml
new file mode 100644
index 0000000..b29ac22
--- /dev/null
+++ b/packages/Keyguard/res/values-sw720dp/dimens.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Keyguard dimensions -->
+ <!-- Size of the clock font in keyguard's status view -->
+ <dimen name="kg_status_clock_font_size">188dp</dimen>
+
+ <!-- Size of the date font in keyguard's status view -->
+ <dimen name="kg_status_date_font_size">34dp</dimen>
+
+ <!-- Size of the generic status lines keyguard's status view -->
+ <dimen name="kg_status_line_font_size">19sp</dimen>
+
+ <!-- Top margin for the clock view -->
+ <dimen name="kg_clock_top_margin">0dp</dimen>
+
+ <!-- Size of margin on the right of keyguard's status view -->
+ <dimen name="kg_status_line_font_right_margin">32dp</dimen>
+
+ <!-- Horizontal padding for the widget pager -->
+ <dimen name="kg_widget_pager_horizontal_padding">80dp</dimen>
+
+ <!-- Top padding for the widget pager -->
+ <dimen name="kg_widget_pager_top_padding">0dp</dimen>
+
+ <!-- Bottom padding for the widget pager -->
+ <dimen name="kg_widget_pager_bottom_padding">0dp</dimen>
+
+ <!-- Top margin for the runway lights. We add a negative margin in large
+ devices to account for the widget pager padding -->
+ <dimen name="kg_runway_lights_top_margin">-30dp</dimen>
+
+ <!-- Margin around the various security views -->
+ <dimen name="keyguard_muliuser_selector_margin">24dp</dimen>
+
+ <!-- Stroke width of the frame for the circular avatars. -->
+ <dimen name="keyguard_avatar_frame_stroke_width">3dp</dimen>
+
+ <!-- Size of the avator on the multiuser lockscreen. -->
+ <dimen name="keyguard_avatar_size">88dp</dimen>
+
+ <!-- Size of the text under the avator on the multiuser lockscreen. -->
+ <dimen name="keyguard_avatar_name_size">12sp</dimen>
+
+ <!-- Width of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) -->
+ <dimen name="keyguard_security_width">420dp</dimen>
+
+ <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) -->
+ <dimen name="keyguard_security_height">420dp</dimen>
+</resources>
diff --git a/packages/Keyguard/res/values-th/activitystrings.xml b/packages/Keyguard/res/values-th/activitystrings.xml
new file mode 100644
index 0000000..64f50cc
--- /dev/null
+++ b/packages/Keyguard/res/values-th/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"ไม่มีการรักษาความปลอดภัย"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"รหัสผ่าน"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"รูปแบบ"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN ของซิม"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK ของซิม"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"เลือกวิดเจ็ต..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-th/strings.xml b/packages/Keyguard/res/values-th/strings.xml
new file mode 100644
index 0000000..4df8cdd
--- /dev/null
+++ b/packages/Keyguard/res/values-th/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"พิมพ์รหัส PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"พิมพ์ PUK และรหัส PIN ใหม่"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"รหัส PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"รหัส PIN ใหม่"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"แตะเพื่อพิมพ์รหัสผ่าน"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"พิมพ์รหัสผ่านเพื่อปลดล็อก"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"พิมพ์ PIN เพื่อปลดล็อก"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"รหัส PIN ไม่ถูกต้อง"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"หากต้องการปลดล็อก กด เมนู ตามด้วย 0"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"มีความพยายามที่จะใช้ Face Unlock เกินขีดจำกัด"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"ชาร์จแล้ว"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"กำลังชาร์จ, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"เสียบที่ชาร์จของคุณ"</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"กด \"เมนู\" เพื่อปลดล็อก"</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"เครือข่ายล็อก"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"ไม่มีซิมการ์ด"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"ไม่มีซิมการ์ดในแท็บเล็ต"</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"ไม่มีซิมการ์ดในโทรศัพท์"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"ใส่ซิมการ์ด"</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"ไม่มีหรือไม่สามารถอ่านซิมการ์ดได้ โปรดใส่ซิมการ์ด"</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"ซิมการ์ดใช้ไม่ได้"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"ซิมการ์ดของคุณถูกปิดใช้งานอย่างถาวร"\n"ติดต่อผู้ให้บริการระบบไร้สายของคุณเพื่อรับซิมการ์ดใหม่"</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"ซิมการ์ดถูกล็อก"</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"ซิมการ์ดถูกล็อกด้วย PUK"</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"กำลังปลดล็อกซิมการ์ด…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s วิดเจ็ต %2$d ของ %3$d"</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"เพิ่มวิดเจ็ต"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"ว่าง"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"ขยายพื้นที่ปลดล็อกแล้ว"</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"ยุบพื้นที่ปลดล็อกแล้ว"</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"วิดเจ็ต <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"ตัวเลือกผู้ใช้"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"สถานะ"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"กล้องถ่ายรูป"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"การควบคุมสื่อ"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"เริ่มเรียงลำดับวิดเจ็ตใหม่"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"เรียงลำดับวิดเจ็ตใหม่เสร็จแล้ว"</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"ลบวิดเจ็ต <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> แล้ว"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"ขยายพื้นที่ปลดล็อก"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"การปลดล็อกด้วยการเลื่อน"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"การปลดล็อกด้วยรูปแบบ"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"การปลดล็อกด้วยใบหน้า"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"การปลดล็อกด้วย PIN"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"การปลดล็อกด้วยรหัสผ่าน"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"พื้นที่สำหรับรูปแบบ"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"พื้นที่สำหรับการเลื่อน"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"ปุ่มแทร็กก่อนหน้า"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"ปุ่มแทร็กถัดไป"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"ปุ่มหยุดชั่วคราว"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"ปุ่มเล่น"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"ปุ่มหยุด"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ยกเลิก"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ลบ"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"เสร็จสิ้น"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"เปลี่ยนโหมด"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"ป้อน"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"ปลดล็อก"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"กล้อง"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"ปิดเสียง"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"เปิดเสียง"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"ค้นหา"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"เลื่อนขึ้นเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"เลื่อนลงเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"เลื่อนไปทางซ้ายเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"เลื่อนไปทางขวาเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
+ <string name="user_switched" msgid="3768006783166984410">"ผู้ใช้ปัจจุบัน <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"หมายเลขฉุกเฉิน"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"ลืมรูปแบบใช่หรือไม่"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"รูปแบบไม่ถูกต้อง"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"รหัสผ่านไม่ถูกต้อง"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN ไม่ถูกต้อง"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"ลองอีกครั้งในอีก <xliff:g id="NUMBER">%d</xliff:g> วินาที"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"วาดรูปแบบของคุณ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"ป้อน PIN ของซิม"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"ป้อน PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"ป้อนรหัสผ่าน"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"ซิมการ์ดถูกปิดใช้งานแล้วในตอนนี้ ป้อนรหัส PUK เพื่อดำเนินการต่อ โปรดติดต่อผู้ให้บริการสำหรับรายละเอียด"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"ป้อนรหัส PIN ที่ต้องการ"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"ยืนยันรหัส PIN ที่ต้องการ"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"กำลังปลดล็อกซิมการ์ด…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"รหัส PIN ไม่ถูกต้อง"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"พิมพ์ PIN ซึ่งเป็นเลข 4 ถึง 8 หลัก"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"รหัส PUK ต้องเป็นตัวเลขอย่างน้อย 8 หลัก"</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"ใส่รหัส PUK ที่ถูกต้องอีกครั้ง การพยายามซ้ำหลายครั้งจะทำให้ซิมการ์ดถูกปิดใช้งานอย่างถาวร"</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"รหัส PIN ไม่ตรง"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ลองหลายรูปแบบมากเกินไป"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"หากต้องการปลดล็อก ให้ลงชื่อเข้าใช้ด้วยบัญชี Google ของคุณ"</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"ชื่อผู้ใช้ (อีเมล)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"รหัสผ่าน"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"ลงชื่อเข้าใช้"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง"</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"หากลืมชื่อผู้ใช้หรือรหัสผ่าน"\n"โปรดไปที่ "<b>"google.com/accounts/recovery"</b></string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"กำลังตรวจสอบบัญชี…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"คุณพิมพ์ PIN ไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว "\n\n"โปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"คุณพิมพ์รหัสผ่านไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว "\n\n"โปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว "\n\n"โปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"คุณพยายามปลดล็อกแท็บเล็ตอย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากพยายามแล้วไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง แท็บเล็ตจะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"คุณพยายามปลดล็อกโทรศัพท์อย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากพยายามแล้วไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง โทรศัพท์จะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"คุณพยายามปลดล็อกแท็บเล็ตอย่างไม่ถูกต้อง <xliff:g id="NUMBER">%d</xliff:g> ครั้งแล้ว ขณะนี้แท็บเล็ตจะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน"</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"คุณพยายามปลดล็อกโทรศัพท์อย่างไม่ถูกต้อง <xliff:g id="NUMBER">%d</xliff:g> ครั้งแล้ว ขณะนี้โทรศัพท์จะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกแท็บเล็ตโดยใช้บัญชีอีเมล"\n\n" โปรดลองอีกครั้งใน <xliff:g id="NUMBER_2">%d</xliff:g> วินาที"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกโทรศัพท์โดยใช้ับัญชีอีเมล"\n\n" โปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_2">%d</xliff:g> วินาที"</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"นำออก"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"ปุ่มแทร็กก่อนหน้า"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"ปุ่มแทร็กถัดไป"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"ปุ่มหยุดชั่วคราว"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"ปุ่มเล่น"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"ปุ่มหยุด"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"ไม่มีบริการ"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-tl/activitystrings.xml b/packages/Keyguard/res/values-tl/activitystrings.xml
new file mode 100644
index 0000000..71f3564
--- /dev/null
+++ b/packages/Keyguard/res/values-tl/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Walang seguridad"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Password"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Pattern"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM PIN"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Pumili ng widget..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenNaka-off"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenNaka-on"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-tl/strings.xml b/packages/Keyguard/res/values-tl/strings.xml
new file mode 100644
index 0000000..4e6350b
--- /dev/null
+++ b/packages/Keyguard/res/values-tl/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"I-type ang PIN code"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"I-type ang PUK at bagong PIN code"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK code"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Bagong PIN code"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Pindutin upang i-type password"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"I-type ang password upang i-unlock"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"I-type ang PIN upang i-unlock"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Maling PIN code."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Upang i-unlock, pindutin ang Menu pagkatapos ay 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Nalagpasan na ang maximum na mga pagtatangka sa Face Unlock"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Na-charge"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Nagcha-charge, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Ikonekta ang iyong charger."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Pindutin ang Menu upang i-unlock."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Naka-lock ang network"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Walang SIM card"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Walang SIM card sa tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Walang SIM card sa telepono."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Maglagay ng SIM card."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Wala o hindi nababasa ang SIM card. Maglagay ng SIM card."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Hindi nagagamit na SIM card."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Ang iyong SIM card ay permanenteng hindi pinagana."\n" Makipag-ugnay sa iyong wireless service provider para sa isa pang SIM card."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Naka-lock ang SIM card."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Naka-lock ang SIM card gamit ang PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Ina-unlock ang SIM card…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d ng %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Magdagdag ng widget."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Walang laman"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Pinalaki ang bahagi ng pag-unlock."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Pinaliit ang bahagi ng pag-unlock."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> widget."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Tagapili ng user"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Katayuan"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Camera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Mga kontrol ng media"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Nagsimula na ang pagbabago ng ayos ng widget."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Natapos na ang pagbabago ng ayos ng widget."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Tinanggal ang widget na <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Palakihin ang bahagi ng pag-unlock."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Pag-unlock ng slide."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Pag-unlock ng pattern."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Face unlock."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pag-unlock ng pin."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Pag-unlock ng password."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Bahagi ng pattern."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Bahagi ng slide."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Button na Nakaraang track"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Button na Susunod na track"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Button na I-pause"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Button na I-play"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Button na Ihinto"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Kanselahin"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Tanggalin"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Tapos na"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Pagbabago ng Mode"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"I-unlock"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Camera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Tahimik"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"I-on ang tunog"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Maghanap"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Mag-slide pataas para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Mag-slide pababa para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Mag-slide pakaliwa para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Mag-slide pakanan para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Kasalukuyang user <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Emergency na tawag"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Nakalimutan ang Pattern"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Maling Pattern"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Maling Password"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Maling PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Subukang muli sa loob ng <xliff:g id="NUMBER">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Iguhit ang iyong pattern"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ilagay ang SIM PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ilagay ang PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ilagay ang Password"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Hindi na pinagana ang SIM ngayon. Maglagay ng PUK code upang magpatuloy. Makipag-ugnay sa carrier para sa mga detalye."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Ilagay ang ninanais na PIN code"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Kumpirmahin ang ninanais na PIN code"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Ina-unlock ang SIM card…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Hindi tamang PIN code."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Mag-type ng PIN na 4 hanggang 8 numero."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Dapat ay 8 numero o higit pa ang PUK code."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Muling ilagay ang tamang PUK code. Permanenteng hindi pagaganahin ang SIM ng mga paulit-ulit na pagtatangka."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Hindi tumutugma ang mga PIN code"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Masyadong maraming pagtatangka sa pattern"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Upang i-unlock, mag-sign in gamit ang iyong Google account."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Username (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Password"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Mag-sign in"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Di-wastong username o password."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nakalimutan ang iyong username o password?"\n"Bisitahin ang "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Tinitingnan ang account…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Na-type mo nang hindi tama ang iyong PIN nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. "\n\n"Subukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Na-type mo nang hindi tama ang iyong password nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. "\n\n"Subukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. "\n\n"Subukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tinangka mo sa hindi tamang paraan na i-unlock ang tabelt nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, ire-reset ang tablet sa factory default at mawawala ang lahat ng data ng user."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tinangka mo sa hindi tamang paraan na i-unlock ang telepono nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, ire-reset ang telepono sa factory default at mawawala ang lahat ng data ng user."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tinangka mo sa hindi tamang paraan na i-unlock ang tablet nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses. Ire-reset na ngayon ang tablet sa factory default."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Tinangka mo sa hindi tamang paraan na i-unlock ang telepono nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses. Ire-reset na ngayon ang telepono sa factory default."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang tablet mo gamit ang isang email account."\n\n" Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang telepono mo gamit ang isang email account."\n\n" Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Alisin"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Button na Nakaraang track"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Button na Susunod na track"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Button na I-pause"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Button na I-play"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Button na Ihinto"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Walang serbisyo."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-tr/activitystrings.xml b/packages/Keyguard/res/values-tr/activitystrings.xml
new file mode 100644
index 0000000..7f5a958
--- /dev/null
+++ b/packages/Keyguard/res/values-tr/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Güvenlik yok"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Şifre"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Desen"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM PIN\'i"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM PUK\'u"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Widget seç..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-tr/strings.xml b/packages/Keyguard/res/values-tr/strings.xml
new file mode 100644
index 0000000..d58f9a0
--- /dev/null
+++ b/packages/Keyguard/res/values-tr/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"PIN kodunu yazın"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"PUK ve yeni PIN kodunu yazın"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK kodu"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Yeni PIN kodu"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Şifre yazmak için dokunun"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Kilidi açmak için şifreyi yazın"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Kilidi açmak için PIN kodunu yazın"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Yanlış PIN kodu."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmak için önce Menü\'ye, sonra 0\'a basın."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Yüz Tanıma Kilidi için maksimum deneme sayısı aşıldı"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Şarj oldu"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Şarj oluyor, <xliff:g id="PERCENT">%%</xliff:g><xliff:g id="NUMBER">%d</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Şarj cihazınızı takın."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Kilidi açmak için Menü\'ye basın."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Ağ kilitli"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"SIM kart yok"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Tablette SIM kart yok."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Telefonda SIM kart yok."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"SIM kart takın."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM kart yok veya okunamıyor. Bir SIM kart takın."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Kullanılamayan SIM kart"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM kartınız kalıcı olarak devre dışı bırakıldı."\n" Başka bir SIM kart için kablosuz servis sağlayıcınıza başvurun."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kart kilitli."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kart PUK kilidi devrede."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM kart kilidi açılıyor…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d / %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Widget ekleyin."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Boş"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Kilit açma alanı genişletildi."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Kilit açma alanı daraltıldı."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> widget\'ı."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Kullanıcı seçici"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Durum"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Kamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Medya denetimleri"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Widget\'ları yeniden sıralama işlemi başladı."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Widget\'ları yeniden sıralama işlemi bitti."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> widget\'ı silindi."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Kilit açma alanını genişletin."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Kaydırarak kilit açma."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desenle kilit açma."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Yüzle kilit açma."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin koduyla kilit açma."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Şifreyle kilit açma."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Desen alanı."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Kaydırma alanı."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Önceki parça düğmesi"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Sonraki parça düğmesi"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Duraklat düğmesi"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Oynat düğmesi"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Durdur düğmesi"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"İptal"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Sil"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Bitti"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Mod değiştirme"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"ÜstKrkt"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Giriş"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Kilidi aç"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Kamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Sessiz"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Ses açık"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Ara"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için yukarı kaydırın."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için aşağı kaydırın."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için sola kaydırın."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için sağa kaydırın."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Geçerli kullanıcı: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Acil durum çağrısı"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Deseni Unuttunuz mu?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Yanlış Desen"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Yanlış Şifre"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Yanlış PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g> saniye içinde yeniden deneyin."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Deseninizi çizin"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM PIN kodunu girin"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"PIN\'i girin"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Şifreyi Girin"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM kart artık devre dışı bırakıldı. Devam etmek için PUK kodunu girin. Ayrıntılı bilgi için operatörle bağlantı kurun."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"İstenen PIN kodunu girin"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"İstenen PIN kodunu onaylayın"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM kart kilidi açılıyor…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Yanlış PIN kodu."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4-8 rakamdan oluşan bir PIN girin."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK kodu 8 veya daha çok basamaklı bir sayı olmalıdır."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Doğru PUK kodunu tekrar girin. Çok sayıda deneme yapılırsa SIM kart kalıcı olarak devre dışı bırakılır."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN kodları eşleşmiyor"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Çok fazla sayıda desen denemesi yapıldı"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Kilidi açmak için Google hesabınızla oturum açın."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Kullanıcı adı (e-posta)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Şifre"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Oturum aç"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Geçersiz kullanıcı adı veya şifre."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Kullanıcı adınızı veya şifrenizi mi unuttunuz?"\n<b>"google.com/accounts/recovery"</b>" adresini ziyaret edin."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Hesap denetleniyor…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodunuzu <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış girdiniz. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Şifrenizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış yazdınız. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet kilidini <xliff:g id="NUMBER_0">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> defa daha başarısız deneme yapılırsa, tablet fabrika varsayılan değerine sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefonun kilidini <xliff:g id="NUMBER_0">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> defa daha başarısız deneme yapılırsa, telefon fabrika varsayılan değerine sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablet kilidini <xliff:g id="NUMBER">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. Tablet şimdi fabrika varsayılanına sıfırlanacak."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefon kilidini <xliff:g id="NUMBER">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. Telefon şimdi fabrika varsayılanına sıfırlanacak."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra, tabletinizi bir e-posta hesabı kullanarak açmanız istenir."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra telefonunuzu bir e-posta hesabı kullanarak açmanız istenir."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Kaldır"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Önceki parça düğmesi"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Sonraki parça düğmesi"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Duraklat düğmesi"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Oynat düğmesi"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Durdur düğmesi"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Hizmet yok."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-uk/activitystrings.xml b/packages/Keyguard/res/values-uk/activitystrings.xml
new file mode 100644
index 0000000..d4d0a4d
--- /dev/null
+++ b/packages/Keyguard/res/values-uk/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Без захисту"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN-код"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Пароль"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Ключ"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"PIN-код SIM-карти"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"PUK-код SIM-карти"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Вибрати віджет…"</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-uk/strings.xml b/packages/Keyguard/res/values-uk/strings.xml
new file mode 100644
index 0000000..273f0c9
--- /dev/null
+++ b/packages/Keyguard/res/values-uk/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Введіть PIN-код"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Введіть PUK-код і новий PIN-код"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-код"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Новий PIN-код"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Торкніться, щоб ввести пароль"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Введіть пароль, щоб розблокувати"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Введіть PIN-код, щоб розблокувати"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неправильний PIN-код."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Щоб розбл., натисн. меню та 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Перевищено максимальну кількість спроб розблокування за допомогою функції \"Фейсконтроль\""</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Заряджено"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Заряджається, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Підключіть зарядний пристрій."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Натисніть Меню, щоб розблокувати."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Мережу заблоковано"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Відсутня SIM-карта"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"У планшетному ПК немає SIM-карти."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"У телефоні немає SIM-карти."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Вставте SIM-карту."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-карта відсутня або не доступна для читання. Вставте SIM-карту."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Непридатна SIM-карта."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Вашу SIM-карту вимкнено назавжди."\n" Зверніться до свого постачальника послуг бездротового зв’язку, щоб отримати іншу SIM-карту."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-карту заблоковано."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-карту заблоковано PUK-кодом."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Розблокування SIM-карти…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Віджет %2$d з %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Додати віджет."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Порожня область"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Область розблокування розгорнуто."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Область розблокування згорнуто."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Віджет <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Вибір користувача"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Статус"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Камера"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Елементи керування носієм"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Змінення порядку віджетів розпочато."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Змінення порядку віджетів закінчено."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Віджет <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> видалено."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Розгорнути область розблокування."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Розблокування повзунком."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Розблокування ключем."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Фейсконтроль"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Розблокування PIN-кодом."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Розблокування паролем."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Область ключа."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Область повзунка."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Кнопка \"Попередня композиція\""</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Кнопка \"Наступна композиція\""</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Кнопка \"Призупинити\""</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Кнопка \"Відтворити\""</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Кнопка \"Зупинити\""</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Скасувати"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Готово"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Зміна режиму"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Розблокувати"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Камера"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Без звуку"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Увімкнути звук"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Пошук"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Проведіть пальцем угору, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Проведіть пальцем униз, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Проведіть пальцем ліворуч, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Проведіть пальцем праворуч, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Поточний користувач: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Екстрений виклик"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Не пам’ятаю ключ"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Неправильний ключ"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Неправильний пароль"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Неправильний PIN-код"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Повторіть спробу через <xliff:g id="NUMBER">%d</xliff:g> с."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Намалюйте ключ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Введіть PIN-код SIM-карти"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Введіть PIN-код"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Введіть пароль"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"Зараз SIM-карту вимкнено. Введіть PUK-код, щоб продовжити. Зв’яжіться з оператором, щоб дізнатися більше."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Введіть потрібний PIN-код"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Підтвердьте потрібний PIN-код"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Розблокування SIM-карти…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Неправильний PIN-код."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Введіть PIN-код із 4–8 цифр."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK-код має складатися зі щонайменше 8 цифр."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Повторно введіть правильний PUK-код. Численні спроби назавжди вимкнуть SIM-карту."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-коди не збігаються"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Забагато спроб намалювати ключ"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Щоб розблокувати, увійдіть, використовуючи дані облікового запису Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Ім’я користувача (електронна адреса)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Пароль"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Увійти"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Недійсне ім’я користувача чи пароль."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Не пам’ятаєте ім’я користувача чи пароль?"\n"Відвідайте сторінку "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Перевірка облікового запису…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN-код неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Пароль неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Кількість невдалих спроб розблокувати планшетний ПК: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі налаштування планшетного ПК буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Кількість невдалих спроб розблокувати телефон: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі налаштування телефону буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Кількість невдалих спроб розблокувати планшетний ПК: <xliff:g id="NUMBER">%d</xliff:g>. Налаштування планшетного ПК буде змінено на заводські за умовчанням."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Кількість невдалих спроб розблокувати телефон: <xliff:g id="NUMBER">%d</xliff:g>. Налаштування телефону буде змінено на заводські за умовчанням."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі з’явиться запит розблокувати планшетний ПК за допомогою облікового запису електронної пошти."\n\n" Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі з’явиться запит розблокувати телефон за допомогою облікового запису електронної пошти."\n\n" Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Вилучити"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Кнопка \"Попередня композиція\""</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Кнопка \"Наступна композиція\""</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Кнопка \"Призупинити\""</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Кнопка \"Відтворити\""</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Кнопка \"Зупинити\""</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Зв’язку немає."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-vi/activitystrings.xml b/packages/Keyguard/res/values-vi/activitystrings.xml
new file mode 100644
index 0000000..009c3bd
--- /dev/null
+++ b/packages/Keyguard/res/values-vi/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Không có bảo mật"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"Mã PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Mật khẩu"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Hình"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"Mã PIN của SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"Mã PUK của SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Chọn tiện ích..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-vi/strings.xml b/packages/Keyguard/res/values-vi/strings.xml
new file mode 100644
index 0000000..7b57fc7
--- /dev/null
+++ b/packages/Keyguard/res/values-vi/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Nhập mã PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Nhập PUK và mã PIN mới"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Mã PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Mã PIN mới"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Chạm để nhập mật khẩu"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Nhập mật khẩu để mở khóa"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Nhập mã PIN để mở khóa"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Mã PIN không chính xác."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Để mở khóa, hãy nhấn vào Trình đơn sau đó nhấn 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Đã vượt quá số lần Mở khóa bằng khuôn mặt tối đa"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Pin đầy"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Đang sạc, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Kết nối bộ sạc của bạn."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Nhấn vào Trình đơn để mở khóa."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Mạng đã bị khóa"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Không có thẻ SIM nào"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Không có thẻ SIM nào trong máy tính bảng."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Không có thẻ SIM nào trong điện thoại."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Vui lòng lắp thẻ SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Thẻ SIM bị thiếu hoặc không thể đọc được. Vui lòng lắp thẻ SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Thẻ SIM không sử dụng được."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Thẻ SIM của bạn đã bị vô hiệu hóa vĩnh viễn."\n" Hãy liên hệ với nhà cung cấp dịch vụ không dây của bạn để lấy thẻ SIM khác."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Thẻ SIM đã bị khóa."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Thẻ SIM đã bị khóa PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Đang mở khóa thẻ SIM…"</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Tiện ích %2$d trong số %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Thêm tiện ích."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Trống"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Đã mở rộng vùng khóa."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Đã thu gọn vùng khóa."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> tiện ích."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Bộ chọn người dùng"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Trạng thái"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Máy ảnh"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Điều khiển phương tiện"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Đã bắt đầu xắp xếp lại tiện ích."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Đã kết thúc sắp xếp lại tiện ích."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Đã xóa tiện ích <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Mở rộng vùng khóa."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Mở khóa bằng cách trượt."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Mở khóa bằng hình."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Mở khóa bằng khuôn mặt."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Mở khóa bằng mã pin."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Mở khóa bằng mật khẩu."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Khu vực hình."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Khu vực trượt."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Nút bản nhạc trước"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Nút bản nhạc tiếp theo"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Nút tạm dừng"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Nút phát"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Nút dừng"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Hủy"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Xóa"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Xong"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Thay đổi chế độ"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Mở khóa"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Máy ảnh"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Im lặng"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Bật âm thanh"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Tìm kiếm"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Trượt lên để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Trượt xuống để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Trượt sang trái để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Trượt sang phải để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Người dùng hiện tại <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Cuộc gọi khẩn cấp"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Đã quên hình"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Hình sai"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Mật khẩu sai"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN sai"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Hãy thử lại sau <xliff:g id="NUMBER">%d</xliff:g> giây."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Vẽ hình của bạn"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Nhập PIN của SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Nhập PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Nhập mật khẩu"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM hiện bị vô hiệu hóa. Nhập mã PUK để tiếp tục. Liên hệ với nhà cung cấp dịch vụ để biết chi tiết."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Nhập mã PIN mong muốn"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Xác nhận mã PIN mong muốn"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Đang mở khóa thẻ SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Mã PIN không chính xác."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Nhập mã PIN có từ 4 đến 8 số."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Mã PUK phải có từ 8 số trở lên."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Hãy nhập lại mã PUK chính xác. Nhiều lần lặp lại sẽ vô hiệu hóa vĩnh viễn thẻ SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Mã PIN không khớp"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Quá nhiều lần nhập hình"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Để mở khóa, hãy đăng nhập bằng tài khoản Google của bạn."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Tên người dùng (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Mật khẩu"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Đăng nhập"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Tên người dùng hoặc mật khẩu không hợp lệ."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bạn quên tên người dùng hoặc mật khẩu?"\n"Hãy truy cập "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Đang kiểm tra tài khoản…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần nhập sai mã PIN của mình. Hãy "\n\n"thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần nhập sai mật khẩu của mình. Hãy "\n\n"thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Hãy "\n\n"thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần mở khóa máy tính bảng không đúng cách. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, máy tính bảng sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần mở khóa điện thoại không đúng cách. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, điện thoại sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Bạn đã <xliff:g id="NUMBER">%d</xliff:g> lần mở khóa máy tính bảng không đúng cách. Bây giờ, máy tính bảng sẽ được đặt lại về mặc định ban đầu."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Bạn đã <xliff:g id="NUMBER">%d</xliff:g> lần mở khóa điện thoại không đúng cách. Bây giờ, điện thoại sẽ được đặt lại về mặc định ban đầu."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa máy tính bảng bằng tài khoản email."\n\n" Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng tài khoản email."\n\n" Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Xóa"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Nút bản nhạc trước"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Nút bản nhạc tiếp theo"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Nút tạm dừng"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Nút phát"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Nút dừng"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Không có dịch vụ."</string>
+</resources>
diff --git a/packages/Keyguard/res/values-xlarge/dimens.xml b/packages/Keyguard/res/values-xlarge/dimens.xml
new file mode 100644
index 0000000..b8cf287
--- /dev/null
+++ b/packages/Keyguard/res/values-xlarge/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.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.
+*/
+-->
+<resources>
+ <!-- Default height of a key in the password keyboard for alpha -->
+ <dimen name="password_keyboard_key_height_alpha">75dip</dimen>
+ <!-- Default height of a key in the password keyboard for numeric -->
+ <dimen name="password_keyboard_key_height_numeric">75dip</dimen>
+ <!-- keyboardHeight = key_height*4 + key_bottom_gap*3 -->
+ <dimen name="password_keyboard_height">48.0mm</dimen>
+</resources>
diff --git a/packages/Keyguard/res/values-zh-rCN/activitystrings.xml b/packages/Keyguard/res/values-zh-rCN/activitystrings.xml
new file mode 100644
index 0000000..d9b99e0
--- /dev/null
+++ b/packages/Keyguard/res/values-zh-rCN/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"无安全措施"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"密码"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"图案"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM 卡 PIN"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM 卡 PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"选择小部件..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-zh-rCN/strings.xml b/packages/Keyguard/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..9e7c088
--- /dev/null
+++ b/packages/Keyguard/res/values-zh-rCN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"输入 PIN 码"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"请输入 PUK 码和新的 PIN 码"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK 码"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"新的 PIN 码"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"触摸可输入密码"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"输入密码以解锁"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"输入 PIN 进行解锁"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 码有误。"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"要解锁,请先按 MENU 再按 0。"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超过“人脸解锁”尝试次数上限"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"充电完成"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"正在充电 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"请连接充电器。"</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"按“菜单”键解锁。"</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"网络已锁定"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"无 SIM 卡"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"平板电脑中没有 SIM 卡。"</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"手机中没有 SIM 卡。"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"请插入 SIM 卡。"</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM 卡缺失或无法读取,请插入 SIM 卡。"</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM 卡无法使用。"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"您的 SIM 卡已永久停用。"\n"请与您的无线服务提供商联系,以便重新获取一张 SIM 卡。"</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM 卡已被锁定。"</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM 卡已被 PUK 锁定。"</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"正在解锁 SIM 卡..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s。%3$d的小部件%2$d。"</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"添加小部件。"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"空白"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"已展开解锁区域。"</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"已折叠解锁区域。"</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>小部件。"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"用户选择器"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"状态"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"相机"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"媒体控制"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"已开始将小部件重新排序。"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"已完成小部件重新排序。"</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"已删除小部件<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>。"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"展开解锁区域。"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"滑动解锁。"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"图案解锁。"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"人脸解锁。"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN 解锁。"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"密码解锁。"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"图案区域。"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"滑动区域。"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"上一曲按钮"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"下一曲按钮"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"暂停按钮"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"播放按钮"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"停止按钮"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"取消"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"完成"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"模式更改"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"解锁"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"相机"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"静音"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"打开声音"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"搜索"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"向上滑动以<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"向下滑动以<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"向左滑动以<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"向右滑动以<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
+ <string name="user_switched" msgid="3768006783166984410">"当前用户是<xliff:g id="NAME">%1$s</xliff:g>。"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"紧急呼救"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"忘记了图案"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"图案错误"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"密码错误"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN 有误"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"请在 <xliff:g id="NUMBER">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"绘制您的图案"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"输入 SIM PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"输入 PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"输入密码"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM 卡已被停用,需要输入 PUK 码才能继续使用。有关详情,请联系您的运营商。"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"请输入所需 PIN 码"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"请确认所需 PIN 码"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"正在解锁 SIM 卡..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN 码有误。"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"请输入 4 至 8 位数的 PIN。"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK 码应至少包含 8 位数字。"</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"请重新输入正确的 PUK 码。如果尝试错误次数过多,SIM 卡将永久停用。"</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN 码不匹配"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"图案尝试次数过多"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"要解锁,请登录您的 Google 帐户。"</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"用户名(电子邮件地址)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"密码"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"登录"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"用户名或密码无效。"</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"忘记了用户名或密码?"\n"请访问 "<b>"google.com/accounts/recovery"</b>"。"</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"正在检查帐户…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了 PIN。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了密码。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁平板电脑。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,平板电脑就会重置为出厂默认设置,而且所有用户数据都会丢失。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁手机。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,手机就会重置为出厂默认设置,而且所有用户数据都会丢失。"</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁平板电脑。平板电脑现在将重置为出厂默认设置。"</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁手机。手机现在将重置为出厂默认设置。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁平板电脑。"\n\n"请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁手机。"\n\n"请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"删除"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"“上一曲”按钮"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"“下一曲”按钮"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"“暂停”按钮"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"“播放”按钮"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"“停止”按钮"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"无服务。"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-zh-rTW/activitystrings.xml b/packages/Keyguard/res/values-zh-rTW/activitystrings.xml
new file mode 100644
index 0000000..42c2a51
--- /dev/null
+++ b/packages/Keyguard/res/values-zh-rTW/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"Keyguard 測試活動"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"整合式相機"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"無安全性設定"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"密碼"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"圖形"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"SIM PIN"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"SIM PUK"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"選擇小工具..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"在螢幕上關閉"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"在螢幕上開啟"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"執行 Keyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"驗證解鎖"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-zh-rTW/strings.xml b/packages/Keyguard/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..4c181d1
--- /dev/null
+++ b/packages/Keyguard/res/values-zh-rTW/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"輸入 PIN 碼"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"輸入 PUK 碼和新 PIN 碼"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK 碼"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"新 PIN 碼"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"輕觸即可輸入密碼"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"輸入密碼即可解鎖"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"輸入 PIN 即可解鎖"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 碼不正確。"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超過人臉解鎖嘗試次數上限"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"充電完成"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"充電中 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"連接充電器。"</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"按選單鍵解鎖。"</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"網路已鎖定"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"找不到 SIM 卡"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"平板電腦中沒有 SIM 卡。"</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"手機中沒有 SIM 卡。"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"插入 SIM 卡。"</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"找不到或無法讀取 SIM 卡。請插入 SIM 卡。"</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM 卡無法使用。"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"您的 SIM 卡已遭永久停用。"\n"請與您的無線網路服務供應商聯絡,以取得別張 SIM 卡。"</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM 卡處於鎖定狀態。"</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM 卡處於 PUK 鎖定狀態"</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"正在解除 SIM 卡鎖定..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s。第 %2$d 個小工具,共 %3$d 個。"</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"新增小工具。"</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"空白"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"解鎖區域已展開。"</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"解鎖區域已收合。"</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>小工具。"</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"使用者選取工具"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"狀態"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"相機"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"媒體控制項"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"已開始將小工具重新排序。"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"小工具重新排序已完成。"</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>小工具已刪除。"</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"展開解鎖區域。"</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"滑動解鎖。"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"圖形解鎖。"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"人臉解鎖。"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN 解鎖。"</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"密碼解鎖。"</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"圖形區域。"</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"滑動區域。"</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"[上一首曲目] 按鈕"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"[下一首曲目] 按鈕"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"[暫停] 按鈕"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"[播放] 按鈕"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"[停止] 按鈕"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt 鍵"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"取消"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete 鍵"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"完成"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"模式變更"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift 鍵"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter 鍵"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"解除鎖定"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"相機"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"靜音"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"開啟音效"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"搜尋"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"向上滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"向下滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"向左滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"向右滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
+ <string name="user_switched" msgid="3768006783166984410">"目前的使用者是 <xliff:g id="NAME">%1$s</xliff:g>。"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"緊急電話"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"忘記圖形"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"圖形錯誤"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"密碼錯誤"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN 錯誤"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"請在 <xliff:g id="NUMBER">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"畫出圖形"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"輸入 SIM PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"輸入 PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"輸入密碼"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM 卡已遭停用,必須輸入 PUK 碼才能繼續使用。詳情請洽您的行動通訊業者。"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"輸入所需的 PIN 碼"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"確認所需的 PIN 碼"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"正在解除 SIM 卡鎖定..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN 碼不正確。"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"請輸入 4 到 8 碼的 PIN。"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK 碼至少必須為 8 碼。"</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"重新輸入正確的 PUK 碼。如果錯誤次數過多,SIM 卡將會永久停用。"</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN 碼不符"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"圖形嘗試次數過多"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"如要解除鎖定,請使用 Google 帳戶登入。"</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"使用者名稱 (電子郵件)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"密碼"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"登入"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"使用者名稱或密碼無效。"</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"忘了使用者名稱或密碼?"\n"請前往 "<b>"google.com/accounts/recovery"</b>"。"</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"正在檢查帳戶…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您的 PIN 已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n"請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您的密碼已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n"請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n"請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您嘗試解除這個平板電腦的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,平板電腦將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您嘗試解除這支手機的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,手機將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"您嘗試解除這個平板電腦的鎖定已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次,平板電腦現在將恢復原廠設定。"</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"您嘗試解除這支手機的鎖定已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次,手機現在將恢復原廠設定。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除平板電腦的鎖定狀態。"\n\n"請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除手機的鎖定狀態。"\n\n"請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"移除"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"[上一首曲目] 按鈕"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"[下一首曲目] 按鈕"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"[暫停] 按鈕"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"[播放] 按鈕"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"[停止] 按鈕"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"沒有服務。"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-zu/activitystrings.xml b/packages/Keyguard/res/values-zu/activitystrings.xml
new file mode 100644
index 0000000..0031a8b
--- /dev/null
+++ b/packages/Keyguard/res/values-zu/activitystrings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="3352888186674981593">"I-KeyguardTestActivity"</string>
+ <string name="secure_app_name" msgid="7955498742816868049">"I-UnifiedCamera"</string>
+ <string name="none_menu_item" msgid="6156747285687551424">"Akukho ukuphepha"</string>
+ <string name="pin_menu_item" msgid="1179756433268962311">"I-PIN"</string>
+ <string name="password_menu_item" msgid="1959980499662153160">"Iphasiwedi"</string>
+ <string name="pattern_menu_item" msgid="2987798152175140249">"Iphethini"</string>
+ <string name="sim_pin_menu_item" msgid="3962286639645084880">"I-PIN ye-SIM"</string>
+ <string name="sim_puk_menu_item" msgid="6190044133008392974">"I-PUK YE-SIM"</string>
+ <string name="add_widget_item" msgid="279702152366857415">"Khetha iwijethi..."</string>
+ <string name="on_screen_turned_off" msgid="8761396329770508367">"I-onScreenTurnedOff"</string>
+ <string name="on_screen_turned_on" msgid="9222926818030728999">"I-onScreenTurnedOn"</string>
+ <string name="do_keyguard" msgid="9210936977823118796">"I-doKeyguard"</string>
+ <string name="verify_unlock" msgid="8508722273329306968">"I-verifyUnlock"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-zu/strings.xml b/packages/Keyguard/res/values-zu/strings.xml
new file mode 100644
index 0000000..350feb0
--- /dev/null
+++ b/packages/Keyguard/res/values-zu/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Faka ikhodi ye-PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Faka i-PUK nephinikhodi entsha"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"Ikhodi le-PUK"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Iphinikhodi entsha"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Thinta ukubhala iphasiwedi"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Bhala iphasiwedi ukuze kuvuleke"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Faka i-PIN ukuvula"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Ikhodi ye-PIN engalungile!"</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Ukuvula, chofoza Menyu bese 0."</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Ukuzama Kokuvula Ubuso Okuningi kudluliwe"</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Kushajiwe"</string>
+ <string name="keyguard_plugged_in" msgid="8117572000639998388">"Iyashaja, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Xhuma ishaja yakho."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Chofoza imenyu ukuze uvule."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Inethiwekhi ikhiyiwe"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Alikho ikhadi le-SIM"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Alikho ikhadi le-SIM kuthebulethi."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Alikho ikhadi le-SIM kufoni."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Faka ikhadi le-SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"Ikhadi le-SIM alitholakali noma alifundeki. Faka ikhadi le-SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Ikhadi le-SIM elingasebenziseki."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"I-SIM khadi yakho ikhutshazwe unomphela."\n" Xhumana nomhlinzeki wakho wokuxhumana okungenazintambo ukuze uthole enye i-SIM khadi."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Ikhadi le-SIM likhiyiwe."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Ikhadi le-SIM likhiywe nge-PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Ivula ikhadi le-SIM..."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. iwijethi %2$d ye-%3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Engeza iwijethi."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Akunalutho"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Indawo yokuvula inwetshisiwe."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Indawo yokuvula inciphisiwe."</string>
+ <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> iwijethi."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Isikhethi somsebenzisi"</string>
+ <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Isimo"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Ikhamera"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Izilawuli zemidiya"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Ukuhlelwa kabusha kwewijethi kuqalile"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Ukuhlelwa kabusha kwewijethi kuphelile."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"Iwijethi <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> isusiwe."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Nwebisa indawo yokuvula."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Ukuvula ngokuslayida."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Ukuvula ngephethini."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Vula ngobuso"</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Ukuvula ngephinikhodi."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Ukuvula ngephasiwedi."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Indawo yephethini."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Indawo yokushelelisa."</string>
+ <string name="keyguard_accessibility_transport_prev_description" msgid="1337286538318543555">"Inkinombo yethrekhi yangaphambilini"</string>
+ <string name="keyguard_accessibility_transport_next_description" msgid="7073928300444909320">"Inkinobho yethrekhi elandelayo"</string>
+ <string name="keyguard_accessibility_transport_pause_description" msgid="8455979545295224302">"Inkinobho yokumiswa isikhashana"</string>
+ <string name="keyguard_accessibility_transport_play_description" msgid="8146417789511154044">"Inkinobho yokudlala"</string>
+ <string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Inkinobho yokumisa"</string>
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
+ <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"i-ALT"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Khansela"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Susa"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Kwenziwe"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Ukushintsha kwendlela esetshenziswayo"</string>
+ <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Beka kwenye indawo"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Faka"</string>
+ <string name="description_target_unlock" msgid="2228524900439801453">"Vula"</string>
+ <string name="description_target_camera" msgid="969071997552486814">"Ikhamera"</string>
+ <string name="description_target_silent" msgid="893551287746522182">"Thulile"</string>
+ <string name="description_target_soundon" msgid="30052466675500172">"Umsindo uvuliwe"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Sesha"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Shelelisela ngenhla ku-<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_down" msgid="5087739728639014595">"Shelelisela ngezansi ku-<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Shelelisela ngakwesokunxele ku-<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_right" msgid="8034433242579600980">"Shelelisela ngakwesokudla ku-<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="user_switched" msgid="3768006783166984410">"Umsebenzisi wamanje <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Ucingo lwezimo eziphuthumayo"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Ukhohlwe iphethini?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Iphatheni engalungile"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Iphasiwedi engalungile"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Iphinikhodi engalungile"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Zama futhi emasekhondini angu-<xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dweba iphethini"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Faka iphinikhodi ye-SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Faka iphinikhodi"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Faka iphasiwedi"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Xhumana nenkampani yenethiwekhi ngemininingwane."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Faka iphinikhodi oyithandayo"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Qiniseka iphinikhodi oyithandayo"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Ivula ikhadi le-SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Iphinikhodi engalungile."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Thayipha iphinikhodi enezinombolo ezingu-4 kuya kwezingu-8."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"Ikhodi ye-PUK kufanele ibe yizinombolo ezingu-8 noma eziningi."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Faka kabusha ikhodi ye-PUK elungile. Imizamo ephindiwe izokhubaza unaphakade i-SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Iphinikhodi ayifani"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Kunemizamo eminingi kakhulu yephathini"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Ukuvula, ngena ngemvume kwi-akhawunti ye-Google"</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Igama lomsebenzisi (i-imeyli)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Iphasiwedi"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Ngena ngemvume"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Igama lomsebezisi elingalungile noma iphasiwedi."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ukhohlwe igama lomsebenzisi noma iphasiwedi?"\n"Vakashela"<b>"google.com/accounts/recovery"</b></string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"Ukuhlola i-akhawunti…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Ubhale iphinikhodi ykho ngendlela engafanele izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Ubhale iphasiwedi yakho ngendlela engafanele <xliff:g id="NUMBER_0">%d</xliff:g> izikhathi. "\n\n"Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Udwebe iphathini yakho yokuvula ngendlela engafanele-<xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Uzame ngokusebenzisa indlela engafanele ukuvula ithebhulethi izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kokuzama ngaphandle kwempumelelo okungu-<xliff:g id="NUMBER_1">%d</xliff:g>, ithebhulethi izobuyiselwa kwizimiso zasembonini futhi yonke imininingwane yomsebenzisi izolahleka."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Uzame ngokusebenzisa indlela engafanele ukuvula ifoni izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kokuzama ngaphandle kwempumelelo okungu-<xliff:g id="NUMBER_1">%d</xliff:g>, ifoni izobuyiselwa kwizimiso zasembonini futhi yonke imininingwane yomsebenzisi izolahleka."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Uzame ukuvula ngendlela engafanele ifoni izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. Ithebhulethi manje isizosethwa kabusha ibe yizimiso ezizenzakalelayo."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Uzame ukuvula ngendlela engafanele ifoni izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. Ifoni manje isizosethwa kabusha ibe yizimiso ezizenzakalelayo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Udwebe ngokungalungile iphathini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Emva <xliff:g id="NUMBER_1">%d</xliff:g> kweminye imizamo engaphumelelanga, uzocelwa ukuvula ithebhulethi yakho usebenzisa ukungena ngemvume kwi-Google."\n\n" Sicela uzame futhi emuva kwamasekhondi angu-<xliff:g id="NUMBER_2">%d</xliff:g>"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-<xliff:g id="NUMBER_0">%d</xliff:g> Emva kweminye imizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google"\n\n" Zame futhi emumva kwengu- <xliff:g id="NUMBER_2">%d</xliff:g> amasekhondi."</string>
+ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Susa"</string>
+ <string name="keyguard_transport_prev_description" msgid="8229108430245669854">"Inkinombo yethrekhi yangaphambilini"</string>
+ <string name="keyguard_transport_next_description" msgid="4299258300283778305">"Inkinobho yethrekhi elandelayo"</string>
+ <string name="keyguard_transport_pause_description" msgid="5093073338238310224">"Inkinobho yokumiswa isikhashana"</string>
+ <string name="keyguard_transport_play_description" msgid="2924628863741150956">"Inkinobho yokudlala"</string>
+ <string name="keyguard_transport_stop_description" msgid="3084179324810575787">"Inkinobho yokumisa"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Ayikho isevisi."</string>
+</resources>
diff --git a/packages/Keyguard/res/values/alias.xml b/packages/Keyguard/res/values/alias.xml
new file mode 100644
index 0000000..47291b2
--- /dev/null
+++ b/packages/Keyguard/res/values/alias.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** 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.
+*/
+-->
+<resources>
+
+ <!-- Alias used to reference framework color for transparency. -->
+ <item type="color" name="transparent">@android:color/transparent</item>
+
+ <!-- Alias used to reference framework drawable in keyguard. -->
+ <item type="drawable" name="stat_sys_warning">@android:drawable/stat_sys_warning</item>
+
+ <!-- Alias used to reference framework drawable in keyguard. -->
+ <item type="drawable" name="ic_media_pause">@android:drawable/ic_media_pause</item>
+
+ <!-- Alias used to reference framework drawable in keyguard. -->
+ <item type="drawable" name="ic_media_stop">@*android:drawable/ic_media_stop</item>
+
+ <!-- Alias used to reference framework drawable in keyguard. -->
+ <item type="drawable" name="ic_contact_picture">@*android:drawable/ic_contact_picture</item>
+
+ <!-- Alias used to reference framework drawable in keyguard. -->
+ <item type="drawable" name="ic_lock_idle_alarm">@*android:drawable/ic_lock_idle_alarm</item>
+
+ <!-- Alias used to reference framework "OK" string in keyguard. -->
+ <item type="string" name="ok">@*android:string/ok</item>
+
+ <!-- Alias used to reference framework "OK" string in keyguard. -->
+ <item type="string" name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</item>
+
+ <!-- Alias used to reference framework configuration for screen rotation. -->
+ <item type="bool" name="config_enableLockScreenRotation">@*android:bool/config_enableLockScreenRotation</item>
+
+ <!-- Alias used to reference framework activity duration. -->
+ <item type="integer" name="config_activityDefaultDur">@*android:integer/config_activityDefaultDur</item>
+
+</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values/arrays.xml b/packages/Keyguard/res/values/arrays.xml
new file mode 100644
index 0000000..550f80c
--- /dev/null
+++ b/packages/Keyguard/res/values/arrays.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Resources for GlowPadView in LockScreen -->
+ <array name="lockscreen_targets_when_silent">
+ <item>@drawable/ic_lockscreen_unlock</item>
+ <item>@drawable/ic_action_assist_generic</item>
+ <item>@drawable/ic_lockscreen_soundon</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_when_silent">
+ <item>@string/description_target_unlock</item>
+ <item>@string/description_target_search</item>
+ <item>@string/description_target_soundon</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_direction_descriptions">
+ <item>@string/description_direction_right</item>
+ <item>@string/description_direction_up</item>
+ <item>@string/description_direction_left</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_targets_when_soundon">
+ <item>@drawable/ic_lockscreen_unlock</item>
+ <item>@drawable/ic_action_assist_generic</item>
+ <item>@drawable/ic_lockscreen_silent</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_when_soundon">
+ <item>@string/description_target_unlock</item>
+ <item>@string/description_target_search</item>
+ <item>@string/description_target_silent</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_targets_with_camera">
+ <item>@drawable/ic_lockscreen_unlock</item>
+ <item>@drawable/ic_action_assist_generic</item>
+ <item>@drawable/ic_lockscreen_camera</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_with_camera">
+ <item>@string/description_target_unlock</item>
+ <item>@string/description_target_search</item>
+ <item>@string/description_target_camera</item>
+ <item>@null</item>
+ </array>
+
+ <array name="lockscreen_targets_unlock_only">
+ <item>@drawable/ic_lockscreen_unlock</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_unlock_only">
+ <item>@string/description_target_unlock</item>
+ </array>
+
+ <!-- list of 3- or 4-letter mnemonics for a 10-key numeric keypad -->
+ <string-array translatable="false" name="lockscreen_num_pad_klondike">
+ <item></item><!-- 0 -->
+ <item></item><!-- 1 -->
+ <item>ABC</item><!-- 2 -->
+ <item>DEF</item><!-- 3 -->
+ <item>GHI</item><!-- 4 -->
+ <item>JKL</item><!-- 5 -->
+ <item>MNO</item><!-- 6 -->
+ <item>PQRS</item><!-- 7 -->
+ <item>TUV</item><!-- 8 -->
+ <item>WXYZ</item><!-- 9 -->
+ </string-array>
+</resources>
diff --git a/packages/Keyguard/res/values/attrs.xml b/packages/Keyguard/res/values/attrs.xml
new file mode 100644
index 0000000..e045dd2
--- /dev/null
+++ b/packages/Keyguard/res/values/attrs.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Formatting note: terminate all comments with a period, to avoid breaking
+ the documentation output. To suppress comment lines from the documentation
+ output, insert an eat-comment element after the comment lines.
+-->
+
+<resources>
+ <!-- Standard gravity constant that a child supplies to its parent.
+ Defines how the child view should be positioned, on both the X and Y axes, within its enclosing layout. -->
+ <attr name="layout_gravity">
+ <!-- Push object to the top of its container, not changing its size. -->
+ <flag name="top" value="0x30" />
+ <!-- Push object to the bottom of its container, not changing its size. -->
+ <flag name="bottom" value="0x50" />
+ <!-- Push object to the left of its container, not changing its size. -->
+ <flag name="left" value="0x03" />
+ <!-- Push object to the right of its container, not changing its size. -->
+ <flag name="right" value="0x05" />
+ <!-- Place object in the vertical center of its container, not changing its size. -->
+ <flag name="center_vertical" value="0x10" />
+ <!-- Grow the vertical size of the object if needed so it completely fills its container. -->
+ <flag name="fill_vertical" value="0x70" />
+ <!-- Place object in the horizontal center of its container, not changing its size. -->
+ <flag name="center_horizontal" value="0x01" />
+ <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->
+ <flag name="fill_horizontal" value="0x07" />
+ <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->
+ <flag name="center" value="0x11" />
+ <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->
+ <flag name="fill" value="0x77" />
+ <!-- Additional option that can be set to have the top and/or bottom edges of
+ the child clipped to its container's bounds.
+ The clip will be based on the vertical gravity: a top gravity will clip the bottom
+ edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->
+ <flag name="clip_vertical" value="0x80" />
+ <!-- Additional option that can be set to have the left and/or right edges of
+ the child clipped to its container's bounds.
+ The clip will be based on the horizontal gravity: a left gravity will clip the right
+ edge, a right gravity will clip the left edge, and neither will clip both edges. -->
+ <flag name="clip_horizontal" value="0x08" />
+ <!-- Push object to the beginning of its container, not changing its size. -->
+ <flag name="start" value="0x00800003" />
+ <!-- Push object to the end of its container, not changing its size. -->
+ <flag name="end" value="0x00800005" />
+ </attr>
+
+
+ <!-- PagedView specific attributes. These attributes are used to customize
+ a PagedView view in XML files. -->
+ <declare-styleable name="PagedView">
+ <!-- The space between adjacent pages of the PagedView. -->
+ <attr name="pageSpacing" format="dimension" />
+ <!-- The padding for the scroll indicator area -->
+ <attr name="scrollIndicatorPaddingLeft" format="dimension" />
+ <attr name="scrollIndicatorPaddingRight" format="dimension" />
+ </declare-styleable>
+
+ <declare-styleable name="KeyguardGlowStripView">
+ <attr name="dotSize" format="dimension" />
+ <attr name="numDots" format="integer" />
+ <attr name="glowDot" format="reference" />
+ <attr name="leftToRight" format="boolean" />
+ </declare-styleable>
+
+ <!-- Some child types have special behavior. -->
+ <attr name="layout_childType">
+ <!-- No special behavior. Layout will proceed as normal. -->
+ <enum name="none" value="0" />
+ <!-- Widget container.
+ This will be resized in response to certain events. -->
+ <enum name="widget" value="1" />
+ <!-- Security challenge container.
+ This will be dismissed/shown in response to certain events,
+ possibly obscuring widget elements. -->
+ <enum name="challenge" value="2" />
+ <!-- User switcher.
+ This will consume space from the total layout area. -->
+ <enum name="userSwitcher" value="3" />
+ <!-- Scrim. This will block access to child views that
+ come before it in the child list in bouncer mode. -->
+ <enum name="scrim" value="4" />
+ <!-- The home for widgets. All widgets will be descendents of this. -->
+ <enum name="widgets" value="5" />
+ <!-- This is a handle that is used for expanding the
+ security challenge container when it is collapsed. -->
+ <enum name="expandChallengeHandle" value="6" />
+ <!-- Delete drop target. This will be the drop target to delete pages. -->
+ <enum name="pageDeleteDropTarget" value="7" />
+ </attr>
+
+ <declare-styleable name="SlidingChallengeLayout_Layout">
+ <attr name="layout_childType" />
+ <attr name="layout_maxHeight" format="dimension" />
+ </declare-styleable>
+
+ <declare-styleable name="MultiPaneChallengeLayout">
+ <!-- Influences how layout_centerWithinArea behaves -->
+ <attr name="android:orientation" />
+ </declare-styleable>
+
+ <declare-styleable name="MultiPaneChallengeLayout_Layout">
+ <!-- Percentage of the screen this child should consume or center within.
+ If 0/default, the view will be measured by standard rules
+ as if this were a FrameLayout. -->
+ <attr name="layout_centerWithinArea" format="float" />
+ <attr name="layout_childType" />
+ <attr name="layout_gravity" />
+ <attr name="layout_maxWidth" format="dimension" />
+ <attr name="layout_maxHeight" />
+ </declare-styleable>
+
+ <declare-styleable name="KeyguardSecurityViewFlipper_Layout">
+ <attr name="layout_maxWidth" />
+ <attr name="layout_maxHeight" />
+ </declare-styleable>
+
+ <declare-styleable name="NumPadKey">
+ <attr name="digit" format="integer" />
+ <attr name="textView" format="reference" />
+ </declare-styleable>
+</resources>
diff --git a/packages/Keyguard/res/values/bools.xml b/packages/Keyguard/res/values/bools.xml
new file mode 100644
index 0000000..a9f69e5
--- /dev/null
+++ b/packages/Keyguard/res/values/bools.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<resources>
+ <bool name="kg_enable_camera_default_widget">true</bool>
+ <bool name="kg_center_small_widgets_vertically">false</bool>
+ <bool name="kg_top_align_page_shrink_on_bouncer_visible">true</bool>
+ <bool name="kg_show_ime_at_screen_on">true</bool>
+</resources>
diff --git a/packages/Keyguard/res/values/colors.xml b/packages/Keyguard/res/values/colors.xml
new file mode 100644
index 0000000..0c56a43
--- /dev/null
+++ b/packages/Keyguard/res/values/colors.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- Keyguard colors -->
+ <color name="keyguard_avatar_frame_color">#ffffffff</color>
+ <color name="keyguard_avatar_frame_shadow_color">#80000000</color>
+ <color name="keyguard_avatar_nick_color">#ffffffff</color>
+ <color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color>
+ <color name="kg_widget_pager_gradient">#ffffffff</color>
+
+ <!-- FaceLock -->
+ <color name="facelock_spotlight_mask">#CC000000</color>
+</resources>
diff --git a/packages/Keyguard/res/values/config.xml b/packages/Keyguard/res/values/config.xml
new file mode 100644
index 0000000..de17c4b
--- /dev/null
+++ b/packages/Keyguard/res/values/config.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Package name for default keyguard appwidget [DO NOT TRANSLATE] -->
+ <string name="widget_default_package_name"></string>
+
+ <!-- Class name for default keyguard appwidget [DO NOT TRANSLATE] -->
+ <string name="widget_default_class_name"></string>
+
+ <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
+ <bool name="config_disableMenuKeyInLockScreen">false</bool>
+
+</resources>
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
new file mode 100644
index 0000000..fde63c4
--- /dev/null
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.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.
+*/
+-->
+<resources>
+ <!-- Default height of a key in the password keyboard for alpha (used by keyguard) -->
+ <dimen name="password_keyboard_key_height_alpha">56dip</dimen>
+ <!-- Default height of a key in the password keyboard for numeric (used by keyguard) -->
+ <dimen name="password_keyboard_key_height_numeric">56dip</dimen>
+ <!-- Default correction for the space key in the password keyboard (used by keyguard) -->
+ <dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen>
+ <!-- Default horizontal gap between keys in the password keyboard (used by keyguard) -->
+ <dimen name="password_keyboard_horizontalGap">3dip</dimen>
+ <!-- Default vertical gap between keys in the password keyboard (used by keyguard) -->
+ <dimen name="password_keyboard_verticalGap">9dip</dimen>
+
+ <!-- Size of lockscreen outerring on unsecure unlock LockScreen -->
+ <dimen name="keyguard_lockscreen_outerring_diameter">270dp</dimen>
+
+ <!-- Default target placement radius for GlowPadView. Should be 1/2 of outerring diameter. -->
+ <dimen name="glowpadview_target_placement_radius">135dip</dimen>
+
+ <!-- Default glow radius for GlowPadView -->
+ <dimen name="glowpadview_glow_radius">75dip</dimen>
+
+ <!-- Default distance beyond which GlowPadView snaps to the matching target -->
+ <dimen name="glowpadview_snap_margin">40dip</dimen>
+
+ <!-- Default distance from each snap target that GlowPadView considers a "hit" -->
+ <dimen name="glowpadview_inner_radius">15dip</dimen>
+
+ <!-- Size of clock font in LockScreen on Unsecure unlock screen. -->
+ <dimen name="keyguard_lockscreen_clock_font_size">80dip</dimen>
+
+ <!-- Size of status line font on Unsecure unlock LockScreen. -->
+ <dimen name="keyguard_lockscreen_status_line_font_size">14dip</dimen>
+
+ <!-- Size of right margin on Unsecure unlock LockScreen -->
+ <dimen name="keyguard_lockscreen_status_line_font_right_margin">42dip</dimen>
+
+ <!-- Size of top margin on Clock font to edge on unlock LockScreen -->
+ <dimen name="keyguard_lockscreen_status_line_clockfont_top_margin">22dip</dimen>
+
+ <!-- Size of top margin on Clock font to edge on unlock LockScreen -->
+ <dimen name="keyguard_lockscreen_status_line_clockfont_bottom_margin">12dip</dimen>
+
+ <!-- Padding on left margin of PIN text entry field to center it when del button is showing -->
+ <dimen name="keyguard_lockscreen_pin_margin_left">40dip</dimen>
+
+ <!-- Height of FaceUnlock view in keyguard -->
+ <dimen name="face_unlock_height">330dip</dimen>
+
+ <!-- Keyguard dimensions -->
+ <!-- TEMP -->
+ <dimen name="kg_security_panel_height">600dp</dimen>
+
+ <!-- Height of security view in keyguard. -->
+ <dimen name="kg_security_view_height">480dp</dimen>
+
+ <!-- Width of widget view in keyguard. -->
+ <dimen name="kg_widget_view_width">0dp</dimen>
+
+ <!-- Height of widget view in keyguard. -->
+ <dimen name="kg_widget_view_height">0dp</dimen>
+
+ <!-- Size of the clock font in keyguard's status view -->
+ <dimen name="kg_status_clock_font_size">75dp</dimen>
+
+ <!-- Size of the date font in keyguard's status view -->
+ <dimen name="kg_status_date_font_size">15dp</dimen>
+
+ <!-- Size of the generic status lines keyguard's status view -->
+ <dimen name="kg_status_line_font_size">13dp</dimen>
+
+ <!-- Size of margin on the right of keyguard's status view -->
+ <dimen name="kg_status_line_font_right_margin">16dp</dimen>
+
+ <!-- Top margin for the clock view -->
+ <dimen name="kg_clock_top_margin">-16dp</dimen>
+
+ <!-- Horizontal gap between keys in PIN and SIM PIN numeric keyboards in keyguard -->
+ <dimen name="kg_key_horizontal_gap">0dp</dimen>
+
+ <!-- Horizontal gap between keys in PIN and SIM PIN numeric keyboards in keyguard -->
+ <dimen name="kg_key_vertical_gap">0dp</dimen>
+
+ <!-- Horizontal gap between keys in PIN and SIM PIN numeric keyboards in keyguard -->
+ <dimen name="kg_pin_key_height">60dp</dimen>
+
+ <!-- Space reserved at the bottom of secure views (pin/pattern/password/SIM pin/SIM puk) -->
+ <dimen name="kg_secure_padding_height">46dp</dimen>
+
+ <!-- The height of the runway lights strip -->
+ <dimen name="kg_runway_lights_height">7dp</dimen>
+
+ <!-- The height of the runway lights strip -->
+ <dimen name="kg_runway_lights_vertical_padding">2dp</dimen>
+
+ <!-- Horizontal padding for the widget pager -->
+ <dimen name="kg_widget_pager_horizontal_padding">16dp</dimen>
+
+ <!-- Top padding for the widget pager -->
+ <dimen name="kg_widget_pager_top_padding">0dp</dimen>
+
+ <!-- Bottom padding for the widget pager -->
+ <dimen name="kg_widget_pager_bottom_padding">64dp</dimen>
+
+ <!-- Top margin for the runway lights. We add a negative margin in large
+ devices to account for the widget pager padding -->
+ <dimen name="kg_runway_lights_top_margin">0dp</dimen>
+
+ <!-- Touch slop for the global toggle accessibility gesture -->
+ <dimen name="accessibility_touch_slop">80dip</dimen>
+
+ <!-- Width of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) -->
+ <dimen name="keyguard_security_width">320dp</dimen>
+
+ <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) -->
+ <dimen name="keyguard_security_height">400dp</dimen>
+
+ <!-- Margin around the various security views -->
+ <dimen name="keyguard_security_view_margin">8dp</dimen>
+
+ <!-- Margin around the various security views -->
+ <dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
+
+ <!-- Stroke width of the frame for the circular avatars. -->
+ <dimen name="keyguard_avatar_frame_stroke_width">2dp</dimen>
+
+ <!-- Shadow radius under the frame for the circular avatars. -->
+ <dimen name="keyguard_avatar_frame_shadow_radius">1dp</dimen>
+
+ <!-- Size of the avator on hte multiuser lockscreen. -->
+ <dimen name="keyguard_avatar_size">66dp</dimen>
+
+ <!-- Size of the text under the avator on the multiuser lockscreen. -->
+ <dimen name="keyguard_avatar_name_size">10sp</dimen>
+
+ <!-- Size of the region along the edge of the screen that will accept
+ swipes to scroll the widget area. -->
+ <dimen name="kg_edge_swipe_region_size">24dp</dimen>
+
+ <!-- If the height if keyguard drops below this threshold (most likely
+ due to the appearance of the IME), then drop the multiuser selector. -->
+ <dimen name="kg_squashed_layout_threshold">600dp</dimen>
+
+ <!-- The height of widgets which do not support vertical resizing. This is only
+ used on tablets; on phones, this size is determined by the space left by the
+ security mode. -->
+ <dimen name="kg_small_widget_height">160dp</dimen>
+
+</resources>
diff --git a/packages/Keyguard/res/values/integers.xml b/packages/Keyguard/res/values/integers.xml
new file mode 100644
index 0000000..053fc85
--- /dev/null
+++ b/packages/Keyguard/res/values/integers.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <integer name="kg_carousel_angle">75</integer>
+ <integer name="kg_security_flip_duration">100</integer>
+ <integer name="kg_security_fade_duration">100</integer>
+ <integer name="kg_glowpad_rotation_offset">0</integer>
+</resources>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
new file mode 100644
index 0000000..5cf05f8
--- /dev/null
+++ b/packages/Keyguard/res/values/strings.xml
@@ -0,0 +1,327 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Instructions telling the user to enter their SIM PIN to unlock the keyguard.
+ Displayed in one line in a large font. -->
+ <string name="keyguard_password_enter_pin_code">Type PIN code</string>
+
+ <!-- Instructions telling the user to enter their SIM PUK to unlock the keyguard.
+ Displayed in one line in a large font. -->
+ <string name="keyguard_password_enter_puk_code">Type PUK and new PIN code</string>
+
+ <!-- Prompt to enter SIM PUK in Edit Text Box in unlock screen -->
+ <string name="keyguard_password_enter_puk_prompt">PUK code</string>
+ <!-- Prompt to enter New SIM PIN in Edit Text Box in unlock screen -->
+ <string name="keyguard_password_enter_pin_prompt">New PIN code</string>
+
+ <!-- Displayed as hint in passwordEntry EditText on PasswordUnlockScreen [CHAR LIMIT=30]-->
+ <string name="keyguard_password_entry_touch_hint"><font size="17">Touch to type password</font></string>
+
+ <!-- Instructions telling the user to enter their text password to unlock the keyguard.
+ Displayed in one line in a large font. -->
+ <string name="keyguard_password_enter_password_code">Type password to unlock</string>
+
+ <!-- Instructions telling the user to enter their PIN password to unlock the keyguard.
+ Displayed in one line in a large font. -->
+ <string name="keyguard_password_enter_pin_password_code">Type PIN to unlock</string>
+
+ <!-- Instructions telling the user that they entered the wrong pin while trying
+ to unlock the keyguard. Displayed in one line in a large font. -->
+ <string name="keyguard_password_wrong_pin_code">Incorrect PIN code.</string>
+
+ <!-- Instructions telling the user how to unlock the phone. -->
+ <string name="keyguard_label_text">To unlock, press Menu then 0.</string>
+
+ <!-- Shown when face unlock failed multiple times so we're just using the backup -->
+ <string name="faceunlock_multiple_failures">Maximum Face Unlock attempts exceeded</string>
+
+ <!-- When the lock screen is showing, the phone is plugged in and the battery is fully
+ charged, say that it is charged. -->
+ <string name="keyguard_charged">Charged</string>
+
+ <!-- When the lock screen is showing and the phone plugged in, and the battery
+ is not fully charged, show the current charge %. -->
+ <string name="keyguard_plugged_in">Charging, <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g></string>
+
+ <!-- When the lock screen is showing and the battery is low, warn user to plug
+ in the phone soon. -->
+ <string name="keyguard_low_battery">Connect your charger.</string>
+
+ <!-- On the keyguard screen, when pattern lock is disabled, only tell them to press menu to unlock. This is shown in small font at the bottom. -->
+ <string name="keyguard_instructions_when_pattern_disabled">Press Menu to unlock.</string>
+
+ <!-- SIM messages --><skip />
+ <!-- When the user inserts a sim card from an unsupported network, it becomes network locked -->
+ <string name="keyguard_network_locked_message">Network locked</string>
+ <!-- Shown when there is no SIM card. -->
+ <string name="keyguard_missing_sim_message_short">No SIM card</string>
+ <!-- Shown when there is no SIM card. -->
+ <string name="keyguard_missing_sim_message" product="tablet">No SIM card in tablet.</string>
+ <!-- Shown when there is no SIM card. -->
+ <string name="keyguard_missing_sim_message" product="default">No SIM card in phone.</string>
+ <!-- Shown to ask the user to insert a SIM card. -->
+ <string name="keyguard_missing_sim_instructions">Insert a SIM card.</string>
+ <!-- Shown to ask the user to insert a SIM card when sim is missing or not readable. -->
+ <string name="keyguard_missing_sim_instructions_long">The SIM card is missing or not readable. Insert a SIM card.</string>
+ <!-- Shown when SIM card is permanently disabled. -->
+ <string name="keyguard_permanent_disabled_sim_message_short">Unusable SIM card.</string>
+ <!-- Shown to inform the user to SIM card is permanently disabled. -->
+ <string name="keyguard_permanent_disabled_sim_instructions">Your SIM card has been permanently disabled.\n
+ Contact your wireless service provider for another SIM card.</string>
+ <!-- Shown to tell the user that their SIM is locked and they must unlock it. -->
+ <string name="keyguard_sim_locked_message">SIM card is locked.</string>
+ <!-- When the user enters a wrong sim pin too many times, it becomes PUK locked (Pin Unlock Kode) -->
+ <string name="keyguard_sim_puk_locked_message">SIM card is PUK-locked.</string>
+ <!-- For the unlock screen, When the user enters a sim unlock code, it takes a little while to check
+ whether it is valid, and to unlock the sim if it is valid. we display a
+ progress dialog in the meantime. this is the emssage. -->
+ <string name="keyguard_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</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>
+ <!-- Accessibility description of the add widget button. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_add_widget">Add widget.</string>
+ <!-- Accessibility description of the empty sidget slot (place holder for a new widget). [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_widget_empty_slot">Empty</string>
+ <!-- Accessibility description of the event of expanding an unlock area. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_unlock_area_expanded">Unlock area expanded.</string>
+ <!-- Accessibility description of the event of collapsing an unlock area. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_unlock_area_collapsed">Unlock area collapsed.</string>
+ <!-- Accessibility description of a lock screen widget. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_widget"><xliff:g id="widget_index">%1$s</xliff:g> widget.</string>
+ <!-- Accessibility description of the lock screen user selector widget. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_user_selector">User selector</string>
+ <!-- Accessibility description of the lock screen status widget. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_status">Status</string>
+ <!-- Accessibility description of the camera widget. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_camera">Camera</string>
+ <!-- Accessibility description of the lock media control widget. [CHAR_LIMIT=none] -->
+ <string name="keygaurd_accessibility_media_controls">Media controls</string>
+ <!-- Accessibility description of widget reordering start. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_widget_reorder_start">Widget reordering started.</string>
+ <!-- Accessibility description of widget reordering end. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_widget_reorder_end">Widget reordering ended.</string>
+ <!-- Accessibility description of the a widget deletion event. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_widget_deleted">Widget <xliff:g id="widget_index">%1$s</xliff:g> deleted.</string>
+ <!-- Accessibility description of the button to expand the lock area. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_expand_lock_area">Expand unlock area.</string>
+ <!-- Accessibility description of the slide unlock. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_slide_unlock">Slide unlock.</string>
+ <!-- Accessibility description of the pattern unlock. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_pattern_unlock">Pattern unlock.</string>
+ <!-- Accessibility description of the face unlock. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_face_unlock">Face unlock.</string>
+ <!-- Accessibility description of the pin lock. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_pin_unlock">Pin unlock.</string>
+ <!-- Accessibility description of the password lock. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_password_unlock">Password unlock.</string>
+ <!-- Accessibility description of the unlock pattern area. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_pattern_area">Pattern area.</string>
+ <!-- Accessibility description of the unlock slide area. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_slide_area">Slide area.</string>
+
+ <!-- Shown on transport control of lockscreen. Pressing button goes to previous track. -->
+ <string name="keyguard_accessibility_transport_prev_description">Previous track button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button goes to next track. -->
+ <string name="keyguard_accessibility_transport_next_description">Next track button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
+ <string name="keyguard_accessibility_transport_pause_description">Pause button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
+ <string name="keyguard_accessibility_transport_play_description">Play button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
+ <string name="keyguard_accessibility_transport_stop_description">Stop button</string>
+
+ <!-- Password keyboard strings. Used by LockScreen and Settings --><skip />
+ <!-- Label for "switch to symbols" key. Must be short to fit on key! -->
+ <string name="password_keyboard_label_symbol_key">\?123</string>
+ <!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
+ <string name="password_keyboard_label_alpha_key">ABC</string>
+ <!-- Label for ALT modifier key. Must be short to fit on key! -->
+ <string name="password_keyboard_label_alt_key">ALT</string>
+
+ <!-- KeyboardView - accessibility support --><skip />
+ <!-- Description of the Alt button in a KeyboardView. [CHAR LIMIT=NONE] -->
+ <string name="keyboardview_keycode_alt">Alt</string>
+ <!-- Description of the Cancel button in a KeyboardView. [CHAR LIMIT=NONE] -->
+ <string name="keyboardview_keycode_cancel">Cancel</string>
+ <!-- Description of the Delete button in a KeyboardView. [CHAR LIMIT=NONE] -->
+ <string name="keyboardview_keycode_delete">Delete</string>
+ <!-- Description of the Done button in a KeyboardView. [CHAR LIMIT=NONE] -->
+ <string name="keyboardview_keycode_done">Done</string>
+ <!-- Description of the Mode change button in a KeyboardView. [CHAR LIMIT=NONE] -->
+ <string name="keyboardview_keycode_mode_change">Mode change</string>
+ <!-- Description of the Shift button in a KeyboardView. [CHAR LIMIT=NONE] -->
+ <string name="keyboardview_keycode_shift">Shift</string>
+ <!-- Description of the Enter button in a KeyboardView. [CHAR LIMIT=NONE] -->
+ <string name="keyboardview_keycode_enter">Enter</string>
+
+ <!-- Description of the unlock target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+ <string name="description_target_unlock">Unlock</string>
+ <!-- Description of the camera target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+ <string name="description_target_camera">Camera</string>
+ <!-- Description of the silent target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+ <string name="description_target_silent">Silent</string>
+ <!-- Description of the sound on target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+ <string name="description_target_soundon">Sound on</string>
+ <!-- Description of the unlock target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+ <string name="description_target_search">Search</string>
+
+ <!-- Description of the up direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+ <string name="description_direction_up">Slide up for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+ <!-- Description of the down direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+ <string name="description_direction_down">Slide down for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+ <!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+ <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+ <!-- Description of the right direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+ <string name="description_direction_right">Slide right for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+
+ <!-- Text spoken when the current user is switched if accessibility is enabled. [CHAR LIMIT=none] -->
+ <string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
+
+ <!-- Label shown on emergency call button in keyguard -->
+ <string name="kg_emergency_call_label">Emergency call</string>
+ <!-- Message shown in pattern unlock after some number of unsuccessful attempts -->
+ <string name="kg_forgot_pattern_button_text">Forgot Pattern</string>
+ <!-- Message shown when user enters wrong pattern -->
+ <string name="kg_wrong_pattern">Wrong Pattern</string>
+ <!-- Message shown when user enters wrong password -->
+ <string name="kg_wrong_password">Wrong Password</string>
+ <!-- Message shown when user enters wrong PIN -->
+ <string name="kg_wrong_pin">Wrong PIN</string>
+ <!-- Countdown message shown after too many failed unlock attempts -->
+ <string name="kg_too_many_failed_attempts_countdown">Try again in <xliff:g id="number">%d</xliff:g> seconds.</string>
+ <!-- Instructions for using the pattern unlock screen -->
+ <string name="kg_pattern_instructions">Draw your pattern</string>
+ <!-- Instructions for using the SIM PIN unlock screen -->
+ <string name="kg_sim_pin_instructions">Enter SIM PIN</string>
+ <!-- Instructions for using the PIN unlock screen -->
+ <string name="kg_pin_instructions">Enter PIN</string>
+ <!-- Instructions for using the password unlock screen -->
+ <string name="kg_password_instructions">Enter Password</string>
+ <!-- Hint shown in the PUK screen that asks the user to enter the PUK code given to them by their provider -->
+ <string name="kg_puk_enter_puk_hint">SIM is now disabled. Enter PUK code to continue. Contact carrier for details.</string>
+ <!-- Hint shown in the PUK unlock screen PIN TextView -->
+ <string name="kg_puk_enter_pin_hint">Enter desired PIN code</string>
+ <!-- Message shown when the user needs to confirm the PIN they just entered in the PUK screen -->
+ <string name="kg_enter_confirm_pin_hint">Confirm desired PIN code</string>
+ <!-- Message shown in dialog while the device is unlocking the SIM card -->
+ <string name="kg_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
+ <!-- Message shown when the user enters the wrong PIN code -->
+ <string name="kg_password_wrong_pin_code">Incorrect PIN code.</string>
+ <!-- Message shown when the user enters an invalid SIM pin password in PUK screen -->
+ <string name="kg_invalid_sim_pin_hint">Type a PIN that is 4 to 8 numbers.</string>
+ <!-- Message shown when the user enters an invalid PUK code in the PUK screen -->
+ <string name="kg_invalid_sim_puk_hint">PUK code should be 8 numbers or more.</string>
+ <!-- Message shown when the user enters an invalid PUK code -->
+ <string name="kg_invalid_puk">Re-enter the correct PUK code. Repeated attempts will permanently disable the SIM.</string>
+ <!-- String shown in PUK screen when PIN codes don't match -->
+ <string name="kg_invalid_confirm_pin_hint" product="default">PIN codes does not match</string>
+ <!-- Message shown when the user exceeds the maximum number of pattern attempts -->
+ <string name="kg_login_too_many_attempts">Too many pattern attempts</string>
+ <!-- Instructions show in account unlock screen allowing user to enter their email password -->
+ <string name="kg_login_instructions">To unlock, sign in with your Google account.</string>
+ <!-- Hint shown in TextView in account unlock screen of keyguard -->
+ <string name="kg_login_username_hint">Username (email)</string>
+ <!-- Hint shown in TextView in account unlock screen of keyguard -->
+ <string name="kg_login_password_hint">Password</string>
+ <!-- Label shown on sign in button on account unlock screen of keyguard -->
+ <string name="kg_login_submit_button">Sign in</string>
+ <!-- Message shown when the user enters an invalid username/password combination in account unlock screen of keyguard -->
+ <string name="kg_login_invalid_input">Invalid username or password.</string>
+ <!-- Hint text shown when user has too many failed password attempts in account unlock screen of keyguard -->
+ <string name="kg_login_account_recovery_hint">Forgot your username or password\?\nVisit <b>google.com/accounts/recovery</b>.</string>
+ <!-- Message shown while device checks username/password in account unlock screen of keyguard -->
+ <string name="kg_login_checking_password">Checking account\u2026</string>
+ <!-- Message shown in dialog when max number of attempts are reached for PIN screen of keyguard -->
+ <string name="kg_too_many_failed_pin_attempts_dialog_message">
+ You have incorrectly typed your PIN <xliff:g id="number">%d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+ <!-- Message shown in dialog when max number of attempts are reached for password screen of keyguard -->
+ <string name="kg_too_many_failed_password_attempts_dialog_message">
+ You have incorrectly typed your password <xliff:g id="number">%d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message">
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+ <!-- Message shown when user is almost at the limit of password attempts where the device will be wiped. -->
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet">
+ You have incorrectly attempted to unlock the tablet <xliff:g id="number">%d</xliff:g> times.
+ After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ the tablet will be reset to factory default and all user data will be lost.
+ </string>
+ <!-- Message shown when user is almost at the limit of password attempts where the device will be wiped. -->
+ <string name="kg_failed_attempts_almost_at_wipe" product="default">
+ You have incorrectly attempted to unlock the phone <xliff:g id="number">%d</xliff:g> times.
+ After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ the phone will be reset to factory default and all user data will be lost.
+ </string>
+ <!-- Message shown in dialog when user has exceeded the maximum attempts and the device will now be wiped -->
+ <string name="kg_failed_attempts_now_wiping" product="tablet">
+ You have incorrectly attempted to unlock the tablet <xliff:g id="number">%d</xliff:g> times.
+ The tablet will now be reset to factory default.
+ </string>
+ <!-- Message shown in dialog when user has exceeded the maximum attempts and the device will now be wiped -->
+ <string name="kg_failed_attempts_now_wiping" product="default">
+ You have incorrectly attempted to unlock the phone <xliff:g id="number">%d</xliff:g> times.
+ The phone will now be reset to factory default.
+ </string>
+ <!-- Message shown in dialog when user is almost at the limit where they will be
+ locked out and may have to enter an alternate username/password to unlock the phone -->
+ <string name="kg_failed_attempts_almost_at_login" product="tablet">
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
+ After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ you will be asked to unlock your tablet using an email account.\n\n
+ Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+ <!-- Message shown in dialog when user is almost at the limit where they will be
+ locked out and may have to enter an alternate username/password to unlock the phone -->
+ <string name="kg_failed_attempts_almost_at_login" product="default">
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
+ After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ you will be asked to unlock your phone using an email account.\n\n
+ Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+ <!-- Sequence of characters used to separate message strings in keyguard. Typically just em-dash
+ with spaces on either side. [CHAR LIMIT=3] -->
+ <string name="kg_text_message_separator" product="default">" \u2014 "</string>
+ <!-- The delete-widget drop target button text -->
+ <string name="kg_reordering_delete_drop_target_text">Remove</string>
+
+ <!-- Transport control strings -->
+ <!-- Shown on transport control of lockscreen. Pressing button goes to previous track. -->
+ <string name="keyguard_transport_prev_description">Previous track button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button goes to next track. -->
+ <string name="keyguard_transport_next_description">Next track button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
+ <string name="keyguard_transport_pause_description">Pause button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
+ <string name="keyguard_transport_play_description">Play button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
+ <string name="keyguard_transport_stop_description">Stop button</string>
+
+ <!-- On the keyguard screen, it shows the carrier the phone is connected to.
+ This is displayed if the phone is not connected to a carrier.-->
+ <string name="keyguard_carrier_default">No service.</string>
+
+</resources>
diff --git a/packages/Keyguard/res/values/styles.xml b/packages/Keyguard/res/values/styles.xml
new file mode 100644
index 0000000..16a3f3f
--- /dev/null
+++ b/packages/Keyguard/res/values/styles.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+ <!-- Keyguard PIN pad styles -->
+ <style name="Widget.Button.NumPadKey"
+ parent="@android:style/Widget.Button">
+ <item name="android:singleLine">true</item>
+ <item name="android:padding">6dip</item>
+ <item name="android:gravity">left|center_vertical</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ <item name="android:textSize">34dp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#ffffff</item>
+ <item name="android:paddingBottom">10dp</item>
+ <item name="android:paddingLeft">20dp</item>
+ </style>
+ <style name="TextAppearance.NumPadKey"
+ parent="@android:style/TextAppearance">
+ <item name="android:textSize">34dp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#ffffff</item>
+ </style>
+ <style name="TextAppearance.NumPadKey.Klondike">
+ <item name="android:textSize">20dp</item>
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#80ffffff</item>
+ </style>
+
+ <!-- Standard animations for a non-full-screen window or activity. -->
+ <style name="Animation.LockScreen" parent="@android:style/Animation">
+ <item name="android:windowEnterAnimation">@anim/lock_screen_enter</item>
+ <item name="android:windowExitAnimation">@anim/lock_screen_exit</item>
+ </style>
+
+</resources>
diff --git a/packages/Keyguard/scripts/copy_profile_icons.sh b/packages/Keyguard/scripts/copy_profile_icons.sh
new file mode 100755
index 0000000..5416101
--- /dev/null
+++ b/packages/Keyguard/scripts/copy_profile_icons.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+for user in `adb $* shell ls /data/system/users | grep -v xml`
+do
+ user=${user/$'\r'/}
+ adb shell mkdir /data/user/${user}/users
+ for photo in `adb $* shell ls /data/system/users | grep -v xml`
+ do
+ photo=${photo/$'\r'/}
+ adb shell mkdir /data/user/${user}/users/${photo}
+ adb pull /data/system/users/${photo}/photo.png
+ adb push photo.png /data/user/${user}/users/${photo}
+ done
+done
diff --git a/packages/Keyguard/scripts/new_merge.py b/packages/Keyguard/scripts/new_merge.py
new file mode 100755
index 0000000..70fafec
--- /dev/null
+++ b/packages/Keyguard/scripts/new_merge.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import difflib
+import filecmp
+import tempfile
+from optparse import OptionParser
+from subprocess import call
+from subprocess import Popen
+from subprocess import PIPE
+
+def which(program):
+ def executable(path):
+ return os.path.isfile(path) and os.access(path, os.X_OK)
+
+ path, file = os.path.split(program)
+ if path and executable(program):
+ return program
+ else:
+ for path in os.environ["PATH"].split(os.pathsep):
+ exe = os.path.join(path, program)
+ if executable(exe):
+ return exe
+ return ""
+
+DIFF_TOOLS=["meld", "kdiff3", "xdiff", "diffmerge.sh", "diff"]
+
+PROTO_SRC="./src/com/android/keyguard/"
+PROTO_RES="./res/"
+
+TEMP_FILE1="/tmp/tempFile1.txt"
+TEMP_FILE2="/tmp/tempFile2.txt"
+
+FW_SRC="../../../../frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/"
+FW_RES="../../../../frameworks/base/core/res/res/"
+
+FW_PKG="com.android.internal.policy.impl.keyguard"
+PROTO_PKG="com.android.keyguard"
+
+FW_RES_IMPORT="import com.android.internal.R;"
+
+# Find a differ
+DIFF_TOOL=""
+if ("DIFF_TOOL" in os.environ and len(os.environ["DIFF_TOOL"]) > 0):
+ DIFF_TOOL=which(os.environ["DIFF_TOOL"])
+if len(DIFF_TOOL) == 0:
+ for differ in DIFF_TOOLS:
+ DIFF_TOOL=which(differ)
+ if len(DIFF_TOOL) > 0:
+ break
+
+print "Using differ", DIFF_TOOL
+
+#Anything file which contains any string in this list as a substring will be ommitted
+IGNORE=["LockHotnessActivity.java", "unified_lock_activity.xml", "optionmenu.xml"]
+WATCH=[]
+
+def dirCompare(sourceDir, destDir, ext, run_in_reverse):
+ sourceFiles = getFileList(sourceDir, ext)
+ destFiles = getFileList(destDir, ext)
+ for file in sourceFiles:
+ print file
+ destFile = destDir + file
+ sourceFile = sourceDir + file
+ if (file in destFiles):
+ if run_in_reverse:
+ prepareFileForCompare(sourceFile, TEMP_FILE1, FW_RES_IMPORT, FW_PKG, PROTO_PKG)
+ prepareFileForCompare(destFile, TEMP_FILE2, FW_RES_IMPORT,)
+ else:
+ prepareFileForCompare(destFile, TEMP_FILE1, FW_RES_IMPORT, FW_PKG, PROTO_PKG)
+ prepareFileForCompare(sourceFile, TEMP_FILE2, FW_RES_IMPORT,)
+ if (filecmp.cmp(TEMP_FILE1, TEMP_FILE2)):
+ print "File %s is the same in proto and framework" %(file)
+ else:
+ print "Running diff for: %s" %(file)
+ diff(sourceFile, destFile)
+ else:
+ print "File %s does not exist in framework" %(file)
+ if not run_in_reverse:
+ diff(sourceFile, destFile)
+
+def main(argv):
+ run_in_reverse = False
+ if len(argv) > 1:
+ if argv[1] == '--help' or argv[1] == '-h':
+ print ('Usage: %s [<commit>]' % argv[0])
+ print ('\tdiff to framework, ' +
+ 'optionally restricting to files in <commit>')
+ sys.exit(0)
+ elif argv[1] == '--reverse':
+ print "Running in reverse"
+ run_in_reverse = True
+ else:
+ print ("**** Pulling file list from: %s" % argv[1])
+ pipe = Popen(['git', 'diff', '--name-only', argv[1]], stdout=PIPE).stdout
+ for line in iter(pipe.readline,''):
+ path = line.rstrip()
+ file = path[path.rfind('/') + 1:]
+ print '**** watching: %s' % file
+ WATCH.append(file);
+ pipe.close()
+
+ if run_in_reverse:
+ #dirCompare(FW_RES, PROTO_RES, ".xml", run_in_reverse)
+ print ("**** Source files:")
+ dirCompare(FW_SRC, PROTO_SRC, ".java", run_in_reverse)
+ else:
+ #dirCompare(PROTO_RES, FW_RES, ".xml", run_in_reverse)
+ print ("**** Source files:")
+ dirCompare(PROTO_SRC, FW_SRC, ".java", run_in_reverse)
+
+ if (os.path.exists(TEMP_FILE1)):
+ os.remove(TEMP_FILE1)
+
+ if (os.path.exists(TEMP_FILE2)):
+ os.remove(TEMP_FILE2)
+
+def getFileList(rootdir, extension):
+ fileList = []
+
+ for root, subFolders, files in os.walk(rootdir):
+ for file in files:
+ f = os.path.join(root,file)
+ if (os.path.splitext(f)[1] == extension and (not inIgnore(f))):
+ fileList.append(f[len(rootdir):])
+ return fileList
+
+
+def prepareFileForCompare(inFile, outFile, skip="", replace="", withText=""):
+ # Delete the outfile, so we're starting with a new file
+ if (os.path.exists(outFile)):
+ os.remove(outFile)
+
+ fin = open(inFile)
+ fout = open(outFile, "w")
+ for line in fin:
+ # Ignore any lines containing the ignore string ("import com.android.internal.R;) and
+ # ignore any lines containing only whitespace.
+ if (line.find(skip) < 0 and len(line.strip(' \t\n\r')) > 0):
+ # For comparison, for framework files, we replace the fw package with the
+ # proto package, since these aren't relevant.
+ if len(replace) > 0:
+ fout.write(line.replace(replace, withText))
+ else:
+ fout.write(line)
+ fin.close()
+ fout.close()
+
+def diff(file1, file2):
+ call([DIFF_TOOL, file1, file2])
+
+def inIgnore(file):
+ for ignore in IGNORE:
+ if file.find(ignore) >= 0:
+ return True
+ if len(WATCH) > 0:
+ for watch in WATCH:
+ if file.find(watch) >= 0:
+ return False
+ return True
+ return False
+
+if __name__=="__main__":
+ main(sys.argv)
diff --git a/packages/Keyguard/src/com/android/keyguard/BiometricSensorUnlock.java b/packages/Keyguard/src/com/android/keyguard/BiometricSensorUnlock.java
new file mode 100644
index 0000000..230ef81
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/BiometricSensorUnlock.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.view.View;
+
+interface BiometricSensorUnlock {
+ /**
+ * Initializes the view provided for the biometric unlock UI to work within. The provided area
+ * completely covers the backup unlock mechanism.
+ * @param biometricUnlockView View provided for the biometric unlock UI.
+ */
+ public void initializeView(View biometricUnlockView);
+
+ /**
+ * Indicates whether the biometric unlock is running. Before
+ * {@link BiometricSensorUnlock#start} is called, isRunning() returns false. After a successful
+ * call to {@link BiometricSensorUnlock#start}, isRunning() returns true until the biometric
+ * unlock completes, {@link BiometricSensorUnlock#stop} has been called, or an error has
+ * forced the biometric unlock to stop.
+ * @return whether the biometric unlock is currently running.
+ */
+ public boolean isRunning();
+
+ /**
+ * Stops and removes the biometric unlock and shows the backup unlock
+ */
+ public void stopAndShowBackup();
+
+ /**
+ * Binds to the biometric unlock service and starts the unlock procedure. Called on the UI
+ * thread.
+ * @return false if it can't be started or the backup should be used.
+ */
+ public boolean start();
+
+ /**
+ * Stops the biometric unlock procedure and unbinds from the service. Called on the UI thread.
+ * @return whether the biometric unlock was running when called.
+ */
+ public boolean stop();
+
+ /**
+ * Cleans up any resources used by the biometric unlock.
+ */
+ public void cleanUp();
+
+ /**
+ * Gets the Device Policy Manager quality of the biometric unlock sensor
+ * (e.g., PASSWORD_QUALITY_BIOMETRIC_WEAK).
+ * @return biometric unlock sensor quality, as defined by Device Policy Manager.
+ */
+ public int getQuality();
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java b/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java
new file mode 100644
index 0000000..146c092
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+
+import com.android.keyguard.KeyguardActivityLauncher.CameraWidgetInfo;
+
+public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnClickListener {
+ private static final String TAG = CameraWidgetFrame.class.getSimpleName();
+ private static final boolean DEBUG = KeyguardHostView.DEBUG;
+ private static final int WIDGET_ANIMATION_DURATION = 250; // ms
+ private static final int WIDGET_WAIT_DURATION = 650; // ms
+ private static final int RECOVERY_DELAY = 1000; // ms
+
+ interface Callbacks {
+ void onLaunchingCamera();
+ void onCameraLaunchedSuccessfully();
+ void onCameraLaunchedUnsuccessfully();
+ }
+
+ private final Handler mHandler = new Handler();
+ private final KeyguardActivityLauncher mActivityLauncher;
+ private final Callbacks mCallbacks;
+ private final CameraWidgetInfo mWidgetInfo;
+ private final WindowManager mWindowManager;
+ private final Point mRenderedSize = new Point();
+ private final int[] mTmpLoc = new int[2];
+ private final Rect mTmpRect = new Rect();
+
+ private long mLaunchCameraStart;
+ private boolean mActive;
+ private boolean mTransitioning;
+ private boolean mDown;
+
+ private FixedSizeFrameLayout mPreview;
+ private View mFullscreenPreview;
+
+ private final Runnable mTransitionToCameraRunnable = new Runnable() {
+ @Override
+ public void run() {
+ transitionToCamera();
+ }};
+
+ private final Runnable mTransitionToCameraEndAction = new Runnable() {
+ @Override
+ public void run() {
+ if (!mTransitioning)
+ return;
+ Handler worker = getWorkerHandler() != null ? getWorkerHandler() : mHandler;
+ mLaunchCameraStart = SystemClock.uptimeMillis();
+ if (DEBUG) Log.d(TAG, "Launching camera at " + mLaunchCameraStart);
+ mActivityLauncher.launchCamera(worker, mSecureCameraActivityStartedRunnable);
+ }};
+
+ private final Runnable mPostTransitionToCameraEndAction = new Runnable() {
+ @Override
+ public void run() {
+ mHandler.post(mTransitionToCameraEndAction);
+ }};
+
+ private final Runnable mRecoverRunnable = new Runnable() {
+ @Override
+ public void run() {
+ recover();
+ }};
+
+ private final Runnable mRenderRunnable = new Runnable() {
+ @Override
+ public void run() {
+ render();
+ }};
+
+ private final Runnable mSecureCameraActivityStartedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ onSecureCameraActivityStarted();
+ }
+ };
+
+ private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
+ private boolean mShowing;
+ void onKeyguardVisibilityChanged(boolean showing) {
+ if (mShowing == showing)
+ return;
+ mShowing = showing;
+ CameraWidgetFrame.this.onKeyguardVisibilityChanged(mShowing);
+ };
+ };
+
+ private static final class FixedSizeFrameLayout extends FrameLayout {
+ int width;
+ int height;
+
+ FixedSizeFrameLayout(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChildren(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ setMeasuredDimension(width, height);
+ }
+ }
+
+ private CameraWidgetFrame(Context context, Callbacks callbacks,
+ KeyguardActivityLauncher activityLauncher,
+ CameraWidgetInfo widgetInfo, View previewWidget) {
+ super(context);
+ mCallbacks = callbacks;
+ mActivityLauncher = activityLauncher;
+ mWidgetInfo = widgetInfo;
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ KeyguardUpdateMonitor.getInstance(context).registerCallback(mCallback);
+
+ mPreview = new FixedSizeFrameLayout(context);
+ mPreview.addView(previewWidget);
+ addView(mPreview);
+
+ View clickBlocker = new View(context);
+ clickBlocker.setBackgroundColor(Color.TRANSPARENT);
+ clickBlocker.setOnClickListener(this);
+ addView(clickBlocker);
+
+ setContentDescription(context.getString(R.string.keyguard_accessibility_camera));
+ if (DEBUG) Log.d(TAG, "new CameraWidgetFrame instance " + instanceId());
+ }
+
+ public static CameraWidgetFrame create(Context context, Callbacks callbacks,
+ KeyguardActivityLauncher launcher) {
+ if (context == null || callbacks == null || launcher == null)
+ return null;
+
+ CameraWidgetInfo widgetInfo = launcher.getCameraWidgetInfo();
+ if (widgetInfo == null)
+ return null;
+ View previewWidget = getPreviewWidget(context, widgetInfo);
+ if (previewWidget == null)
+ return null;
+
+ return new CameraWidgetFrame(context, callbacks, launcher, widgetInfo, previewWidget);
+ }
+
+ private static View getPreviewWidget(Context context, CameraWidgetInfo widgetInfo) {
+ return widgetInfo.layoutId > 0 ?
+ inflateWidgetView(context, widgetInfo) :
+ inflateGenericWidgetView(context);
+ }
+
+ private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) {
+ if (DEBUG) Log.d(TAG, "inflateWidgetView: " + widgetInfo.contextPackage);
+ View widgetView = null;
+ Exception exception = null;
+ try {
+ Context cameraContext = context.createPackageContext(
+ widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
+ LayoutInflater cameraInflater = (LayoutInflater)
+ cameraContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ cameraInflater = cameraInflater.cloneInContext(cameraContext);
+ widgetView = cameraInflater.inflate(widgetInfo.layoutId, null, false);
+ } catch (NameNotFoundException e) {
+ exception = e;
+ } catch (RuntimeException e) {
+ exception = e;
+ }
+ if (exception != null) {
+ Log.w(TAG, "Error creating camera widget view", exception);
+ }
+ return widgetView;
+ }
+
+ private static View inflateGenericWidgetView(Context context) {
+ if (DEBUG) Log.d(TAG, "inflateGenericWidgetView");
+ ImageView iv = new ImageView(context);
+ iv.setImageResource(R.drawable.ic_lockscreen_camera);
+ iv.setScaleType(ScaleType.CENTER);
+ iv.setBackgroundColor(Color.argb(127, 0, 0, 0));
+ return iv;
+ }
+
+ private void render() {
+ final View root = getRootView();
+ final int width = root.getWidth();
+ final int height = root.getHeight();
+ if (mRenderedSize.x == width && mRenderedSize.y == height) {
+ if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s", width, height));
+ return;
+ }
+ if (width == 0 || height == 0) {
+ return;
+ }
+
+ mPreview.width = width;
+ mPreview.height = height;
+ mPreview.requestLayout();
+
+ final int thisWidth = getWidth() - getPaddingLeft() - getPaddingRight();
+ final int thisHeight = getHeight() - getPaddingTop() - getPaddingBottom();
+
+ final float pvScaleX = (float) thisWidth / width;
+ final float pvScaleY = (float) thisHeight / height;
+ final float pvScale = Math.min(pvScaleX, pvScaleY);
+
+ final int pvWidth = (int) (pvScale * width);
+ final int pvHeight = (int) (pvScale * height);
+
+ final float pvTransX = pvWidth < thisWidth ? (thisWidth - pvWidth) / 2 : 0;
+ final float pvTransY = pvHeight < thisHeight ? (thisHeight - pvHeight) / 2 : 0;
+
+ mPreview.setPivotX(0);
+ mPreview.setPivotY(0);
+ mPreview.setScaleX(pvScale);
+ mPreview.setScaleY(pvScale);
+ mPreview.setTranslationX(pvTransX);
+ mPreview.setTranslationY(pvTransY);
+
+ mRenderedSize.set(width, height);
+ if (DEBUG) Log.d(TAG, String.format("Rendered camera widget size=%sx%s instance=%s",
+ width, height, instanceId()));
+ }
+
+ private void transitionToCamera() {
+ if (mTransitioning || mDown) return;
+
+ mTransitioning = true;
+
+ enableWindowExitAnimation(false);
+
+ mPreview.getLocationInWindow(mTmpLoc);
+ final float pvHeight = mPreview.getHeight() * mPreview.getScaleY();
+ final float pvCenter = mTmpLoc[1] + pvHeight / 2f;
+
+ final ViewGroup root = (ViewGroup) getRootView();
+ if (mFullscreenPreview == null) {
+ mFullscreenPreview = getPreviewWidget(mContext, mWidgetInfo);
+ mFullscreenPreview.setClickable(false);
+ root.addView(mFullscreenPreview);
+ }
+
+ root.getWindowVisibleDisplayFrame(mTmpRect);
+ final float fsHeight = mTmpRect.height();
+ final float fsCenter = mTmpRect.top + fsHeight / 2;
+
+ final float fsScaleY = pvHeight / fsHeight;
+ final float fsTransY = pvCenter - fsCenter;
+ final float fsScaleX = mPreview.getScaleX();
+
+ mPreview.setVisibility(View.GONE);
+ mFullscreenPreview.setVisibility(View.VISIBLE);
+ mFullscreenPreview.setTranslationY(fsTransY);
+ mFullscreenPreview.setScaleX(fsScaleX);
+ mFullscreenPreview.setScaleY(fsScaleY);
+ mFullscreenPreview
+ .animate()
+ .scaleX(1)
+ .scaleY(1)
+ .translationX(0)
+ .translationY(0)
+ .setDuration(WIDGET_ANIMATION_DURATION)
+ .withEndAction(mPostTransitionToCameraEndAction)
+ .start();
+ mCallbacks.onLaunchingCamera();
+ }
+
+ private void recover() {
+ if (DEBUG) Log.d(TAG, "recovering at " + SystemClock.uptimeMillis());
+ mCallbacks.onCameraLaunchedUnsuccessfully();
+ reset();
+ }
+
+ @Override
+ public void setOnLongClickListener(OnLongClickListener l) {
+ // ignore
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (DEBUG) Log.d(TAG, "clicked");
+ if (mTransitioning) return;
+ if (mActive) {
+ cancelTransitionToCamera();
+ transitionToCamera();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if (DEBUG) Log.d(TAG, "onDetachedFromWindow: instance " + instanceId()
+ + " at " + SystemClock.uptimeMillis());
+ super.onDetachedFromWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mCallback);
+ cancelTransitionToCamera();
+ mHandler.removeCallbacks(mRecoverRunnable);
+ }
+
+ @Override
+ public void onActive(boolean isActive) {
+ mActive = isActive;
+ if (mActive) {
+ rescheduleTransitionToCamera();
+ } else {
+ reset();
+ }
+ }
+
+ @Override
+ public boolean onUserInteraction(MotionEvent event) {
+ if (mTransitioning) {
+ if (DEBUG) Log.d(TAG, "onUserInteraction eaten: mTransitioning");
+ return true;
+ }
+
+ getLocationOnScreen(mTmpLoc);
+ int rawBottom = mTmpLoc[1] + getHeight();
+ if (event.getRawY() > rawBottom) {
+ if (DEBUG) Log.d(TAG, "onUserInteraction eaten: below widget");
+ return true;
+ }
+
+ int action = event.getAction();
+ mDown = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE;
+ if (mActive) {
+ rescheduleTransitionToCamera();
+ }
+ if (DEBUG) Log.d(TAG, "onUserInteraction observed, not eaten");
+ return false;
+ }
+
+ @Override
+ protected void onFocusLost() {
+ if (DEBUG) Log.d(TAG, "onFocusLost at " + SystemClock.uptimeMillis());
+ cancelTransitionToCamera();
+ super.onFocusLost();
+ }
+
+ public void onScreenTurnedOff() {
+ if (DEBUG) Log.d(TAG, "onScreenTurnedOff");
+ reset();
+ }
+
+ private void rescheduleTransitionToCamera() {
+ if (DEBUG) Log.d(TAG, "rescheduleTransitionToCamera at " + SystemClock.uptimeMillis());
+ mHandler.removeCallbacks(mTransitionToCameraRunnable);
+ mHandler.postDelayed(mTransitionToCameraRunnable, WIDGET_WAIT_DURATION);
+ }
+
+ private void cancelTransitionToCamera() {
+ if (DEBUG) Log.d(TAG, "cancelTransitionToCamera at " + SystemClock.uptimeMillis());
+ mHandler.removeCallbacks(mTransitionToCameraRunnable);
+ }
+
+ private void onCameraLaunched() {
+ mCallbacks.onCameraLaunchedSuccessfully();
+ reset();
+ }
+
+ private void reset() {
+ if (DEBUG) Log.d(TAG, "reset at " + SystemClock.uptimeMillis());
+ mLaunchCameraStart = 0;
+ mTransitioning = false;
+ mDown = false;
+ cancelTransitionToCamera();
+ mHandler.removeCallbacks(mRecoverRunnable);
+ mPreview.setVisibility(View.VISIBLE);
+ if (mFullscreenPreview != null) {
+ mFullscreenPreview.animate().cancel();
+ mFullscreenPreview.setVisibility(View.GONE);
+ }
+ enableWindowExitAnimation(true);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ if (DEBUG) Log.d(TAG, String.format("onSizeChanged new=%sx%s old=%sx%s at %s",
+ w, h, oldw, oldh, SystemClock.uptimeMillis()));
+ mHandler.post(mRenderRunnable);
+ super.onSizeChanged(w, h, oldw, oldh);
+ }
+
+ @Override
+ public void onBouncerShowing(boolean showing) {
+ if (showing) {
+ mTransitioning = false;
+ mHandler.post(mRecoverRunnable);
+ }
+ }
+
+ private void enableWindowExitAnimation(boolean isEnabled) {
+ View root = getRootView();
+ ViewGroup.LayoutParams lp = root.getLayoutParams();
+ if (!(lp instanceof WindowManager.LayoutParams))
+ return;
+ WindowManager.LayoutParams wlp = (WindowManager.LayoutParams) lp;
+ int newWindowAnimations = isEnabled ? R.style.Animation_LockScreen : 0;
+ if (newWindowAnimations != wlp.windowAnimations) {
+ if (DEBUG) Log.d(TAG, "setting windowAnimations to: " + newWindowAnimations
+ + " at " + SystemClock.uptimeMillis());
+ wlp.windowAnimations = newWindowAnimations;
+ mWindowManager.updateViewLayout(root, wlp);
+ }
+ }
+
+ private void onKeyguardVisibilityChanged(boolean showing) {
+ if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged " + showing
+ + " at " + SystemClock.uptimeMillis());
+ if (mTransitioning && !showing) {
+ mTransitioning = false;
+ mHandler.removeCallbacks(mRecoverRunnable);
+ if (mLaunchCameraStart > 0) {
+ long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
+ if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
+ mLaunchCameraStart = 0;
+ onCameraLaunched();
+ }
+ }
+ }
+
+ private void onSecureCameraActivityStarted() {
+ if (DEBUG) Log.d(TAG, "onSecureCameraActivityStarted at " + SystemClock.uptimeMillis());
+ mHandler.postDelayed(mRecoverRunnable, RECOVERY_DELAY);
+ }
+
+ private String instanceId() {
+ return Integer.toHexString(hashCode());
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
new file mode 100644
index 0000000..9f0a042
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.widget.LockPatternUtils;
+
+public class CarrierText extends TextView {
+ private static CharSequence mSeparator;
+
+ private LockPatternUtils mLockPatternUtils;
+
+ private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
+ private CharSequence mPlmn;
+ private CharSequence mSpn;
+ private State mSimState;
+
+ @Override
+ public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+ mPlmn = plmn;
+ mSpn = spn;
+ updateCarrierText(mSimState, mPlmn, mSpn);
+ }
+
+ @Override
+ public void onSimStateChanged(IccCardConstants.State simState) {
+ mSimState = simState;
+ updateCarrierText(mSimState, mPlmn, mSpn);
+ }
+ };
+ /**
+ * The status of this lock screen. Primarily used for widgets on LockScreen.
+ */
+ private static enum StatusMode {
+ Normal, // Normal case (sim card present, it's not locked)
+ NetworkLocked, // SIM card is 'network locked'.
+ SimMissing, // SIM card is missing.
+ SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
+ SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
+ SimLocked, // SIM card is currently locked
+ SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
+ SimNotReady; // SIM is not ready yet. May never be on devices w/o a SIM.
+ }
+
+ public CarrierText(Context context) {
+ this(context, null);
+ }
+
+ public CarrierText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mLockPatternUtils = new LockPatternUtils(mContext);
+ }
+
+ protected void updateCarrierText(State simState, CharSequence plmn, CharSequence spn) {
+ CharSequence text = getCarrierTextForSimState(simState, plmn, spn);
+ if (KeyguardViewManager.USE_UPPER_CASE) {
+ setText(text != null ? text.toString().toUpperCase() : null);
+ } else {
+ setText(text);
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSeparator = getResources().getString(R.string.kg_text_message_separator);
+ setSelected(true); // Allow marquee to work.
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mCallback);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mCallback);
+ }
+
+ /**
+ * Top-level function for creating carrier text. Makes text based on simState, PLMN
+ * and SPN as well as device capabilities, such as being emergency call capable.
+ *
+ * @param simState
+ * @param plmn
+ * @param spn
+ * @return
+ */
+ private CharSequence getCarrierTextForSimState(IccCardConstants.State simState,
+ CharSequence plmn, CharSequence spn) {
+ CharSequence carrierText = null;
+ StatusMode status = getStatusForIccState(simState);
+ switch (status) {
+ case Normal:
+ carrierText = concatenate(plmn, spn);
+ break;
+
+ case SimNotReady:
+ carrierText = null; // nothing to display yet.
+ break;
+
+ case NetworkLocked:
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ mContext.getText(R.string.keyguard_network_locked_message), plmn);
+ break;
+
+ case SimMissing:
+ // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
+ // This depends on mPlmn containing the text "Emergency calls only" when the radio
+ // has some connectivity. Otherwise, it should be null or empty and just show
+ // "No SIM card"
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ getContext().getText(R.string.keyguard_missing_sim_message_short),
+ plmn);
+ break;
+
+ case SimPermDisabled:
+ carrierText = getContext().getText(
+ R.string.keyguard_permanent_disabled_sim_message_short);
+ break;
+
+ case SimMissingLocked:
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ getContext().getText(R.string.keyguard_missing_sim_message_short),
+ plmn);
+ break;
+
+ case SimLocked:
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ getContext().getText(R.string.keyguard_sim_locked_message),
+ plmn);
+ break;
+
+ case SimPukLocked:
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ getContext().getText(R.string.keyguard_sim_puk_locked_message),
+ plmn);
+ break;
+ }
+
+ return carrierText;
+ }
+
+ /*
+ * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
+ */
+ private CharSequence makeCarrierStringOnEmergencyCapable(
+ CharSequence simMessage, CharSequence emergencyCallMessage) {
+ if (mLockPatternUtils.isEmergencyCallCapable()) {
+ return concatenate(simMessage, emergencyCallMessage);
+ }
+ return simMessage;
+ }
+
+ /**
+ * Determine the current status of the lock screen given the SIM state and other stuff.
+ */
+ private StatusMode getStatusForIccState(IccCardConstants.State simState) {
+ // Since reading the SIM may take a while, we assume it is present until told otherwise.
+ if (simState == null) {
+ return StatusMode.Normal;
+ }
+
+ final boolean missingAndNotProvisioned =
+ !KeyguardUpdateMonitor.getInstance(mContext).isDeviceProvisioned()
+ && (simState == IccCardConstants.State.ABSENT ||
+ simState == IccCardConstants.State.PERM_DISABLED);
+
+ // Assume we're NETWORK_LOCKED if not provisioned
+ simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState;
+ switch (simState) {
+ case ABSENT:
+ return StatusMode.SimMissing;
+ case NETWORK_LOCKED:
+ return StatusMode.SimMissingLocked;
+ case NOT_READY:
+ return StatusMode.SimNotReady;
+ case PIN_REQUIRED:
+ return StatusMode.SimLocked;
+ case PUK_REQUIRED:
+ return StatusMode.SimPukLocked;
+ case READY:
+ return StatusMode.Normal;
+ case PERM_DISABLED:
+ return StatusMode.SimPermDisabled;
+ case UNKNOWN:
+ return StatusMode.SimMissing;
+ }
+ return StatusMode.SimMissing;
+ }
+
+ private static CharSequence concatenate(CharSequence plmn, CharSequence spn) {
+ final boolean plmnValid = !TextUtils.isEmpty(plmn);
+ final boolean spnValid = !TextUtils.isEmpty(spn);
+ if (plmnValid && spnValid) {
+ return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
+ } else if (plmnValid) {
+ return plmn;
+ } else if (spnValid) {
+ return spn;
+ } else {
+ return "";
+ }
+ }
+
+ private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simState,
+ String plmn, String spn) {
+ int carrierHelpTextId = 0;
+ StatusMode status = getStatusForIccState(simState);
+ switch (status) {
+ case NetworkLocked:
+ carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
+ break;
+
+ case SimMissing:
+ carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
+ break;
+
+ case SimPermDisabled:
+ carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
+ break;
+
+ case SimMissingLocked:
+ carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
+ break;
+
+ case Normal:
+ case SimLocked:
+ case SimPukLocked:
+ break;
+ }
+
+ return mContext.getText(carrierHelpTextId);
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java
new file mode 100644
index 0000000..677f1f1
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+/**
+ * Interface implemented by ViewGroup-derived layouts that implement
+ * special logic for presenting security challenges to the user.
+ */
+public interface ChallengeLayout {
+ /**
+ * @return true if the security challenge area of this layout is currently visible
+ */
+ boolean isChallengeShowing();
+
+ /**
+ * @return true if the challenge area significantly overlaps other content
+ */
+ boolean isChallengeOverlapping();
+
+ /**
+ * Show or hide the challenge layout.
+ *
+ * If you want to show the challenge layout in bouncer mode where applicable,
+ * use {@link #showBouncer()} instead.
+ *
+ * @param b true to show, false to hide
+ */
+ void showChallenge(boolean b);
+
+ /**
+ * Show the bouncer challenge. This may block access to other child views.
+ */
+ void showBouncer();
+
+ /**
+ * Hide the bouncer challenge if it is currently showing.
+ * This may restore previously blocked access to other child views.
+ */
+ void hideBouncer();
+
+ /**
+ * Returns true if the challenge is currently in bouncer mode,
+ * potentially blocking access to other child views.
+ */
+ boolean isBouncing();
+
+ /**
+ * Returns the duration of the bounce animation.
+ */
+ int getBouncerAnimationDuration();
+
+ /**
+ * Set a listener that will respond to changes in bouncer state.
+ *
+ * @param listener listener to register
+ */
+ void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener);
+
+ /**
+ * Listener interface that reports changes in bouncer state.
+ * The bouncer is
+ */
+ public interface OnBouncerStateChangedListener {
+ /**
+ * Called when the bouncer state changes.
+ * The bouncer is activated when the user must pass a security challenge
+ * to proceed with the requested action.
+ *
+ * <p>This differs from simply showing or hiding the security challenge
+ * as the bouncer will prevent interaction with other elements of the UI.
+ * If the user attempts to escape from the bouncer, it will be dismissed,
+ * this method will be called with false as the parameter, and the action
+ * should be canceled. If the security component reports a successful
+ * authentication and the containing code calls hideBouncer() as a result,
+ * this method will also be called with a false parameter. It is up to the
+ * caller of hideBouncer to be ready for this.</p>
+ *
+ * @param bouncerActive true if the bouncer is now active,
+ * false if the bouncer was dismissed.
+ */
+ public void onBouncerStateChanged(boolean bouncerActive);
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/CheckLongPressHelper.java b/packages/Keyguard/src/com/android/keyguard/CheckLongPressHelper.java
new file mode 100644
index 0000000..52e7cd5
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/CheckLongPressHelper.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+public class CheckLongPressHelper {
+ private View mView;
+ private boolean mHasPerformedLongPress;
+ private CheckForLongPress mPendingCheckForLongPress;
+ private float mDownX, mDownY;
+ private int mLongPressTimeout;
+ private int mScaledTouchSlop;
+
+ class CheckForLongPress implements Runnable {
+ public void run() {
+ if ((mView.getParent() != null) && mView.hasWindowFocus()
+ && !mHasPerformedLongPress) {
+ if (mView.performLongClick()) {
+ mView.setPressed(false);
+ mHasPerformedLongPress = true;
+ }
+ }
+ }
+ }
+
+ public CheckLongPressHelper(View v) {
+ mScaledTouchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
+ mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
+ mView = v;
+ }
+
+ public void postCheckForLongPress(MotionEvent ev) {
+ mDownX = ev.getX();
+ mDownY = ev.getY();
+ mHasPerformedLongPress = false;
+
+ if (mPendingCheckForLongPress == null) {
+ mPendingCheckForLongPress = new CheckForLongPress();
+ }
+ mView.postDelayed(mPendingCheckForLongPress, mLongPressTimeout);
+ }
+
+ public void onMove(MotionEvent ev) {
+ float x = ev.getX();
+ float y = ev.getY();
+ boolean xMoved = Math.abs(mDownX - x) > mScaledTouchSlop;
+ boolean yMoved = Math.abs(mDownY - y) > mScaledTouchSlop;
+
+ if (xMoved || yMoved) {
+ cancelLongPress();
+ }
+ }
+
+ public void cancelLongPress() {
+ mHasPerformedLongPress = false;
+ if (mPendingCheckForLongPress != null) {
+ mView.removeCallbacks(mPendingCheckForLongPress);
+ mPendingCheckForLongPress = null;
+ }
+ }
+
+ public boolean hasPerformedLongPress() {
+ return mHasPerformedLongPress;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/ClockView.java b/packages/Keyguard/src/com/android/keyguard/ClockView.java
new file mode 100644
index 0000000..ad85e9a
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/ClockView.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.graphics.Typeface;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.lang.ref.WeakReference;
+import java.text.DateFormatSymbols;
+import java.util.Calendar;
+
+/**
+ * Displays the time
+ */
+public class ClockView extends RelativeLayout {
+ private static final String ANDROID_CLOCK_FONT_FILE = "/system/fonts/AndroidClock.ttf";
+ private final static String M12 = "h:mm";
+ private final static String M24 = "HH:mm";
+
+ private Calendar mCalendar;
+ private String mFormat;
+ private TextView mTimeView;
+ private AmPm mAmPm;
+ private ContentObserver mFormatChangeObserver;
+ private int mAttached = 0; // for debugging - tells us whether attach/detach is unbalanced
+
+ /* called by system on minute ticks */
+ private final Handler mHandler = new Handler();
+ private BroadcastReceiver mIntentReceiver;
+
+ private static class TimeChangedReceiver extends BroadcastReceiver {
+ private WeakReference<ClockView> mClock;
+ private Context mContext;
+
+ public TimeChangedReceiver(ClockView clock) {
+ mClock = new WeakReference<ClockView>(clock);
+ mContext = clock.getContext();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Post a runnable to avoid blocking the broadcast.
+ final boolean timezoneChanged =
+ intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED);
+ final ClockView clock = mClock.get();
+ if (clock != null) {
+ clock.mHandler.post(new Runnable() {
+ public void run() {
+ if (timezoneChanged) {
+ clock.mCalendar = Calendar.getInstance();
+ }
+ clock.updateTime();
+ }
+ });
+ } else {
+ try {
+ mContext.unregisterReceiver(this);
+ } catch (RuntimeException e) {
+ // Shouldn't happen
+ }
+ }
+ }
+ };
+
+ static class AmPm {
+ private TextView mAmPmTextView;
+ private String mAmString, mPmString;
+
+ AmPm(View parent, Typeface tf) {
+ // No longer used, uncomment if we decide to use AM/PM indicator again
+ // mAmPmTextView = (TextView) parent.findViewById(R.id.am_pm);
+ if (mAmPmTextView != null && tf != null) {
+ mAmPmTextView.setTypeface(tf);
+ }
+
+ String[] ampm = new DateFormatSymbols().getAmPmStrings();
+ mAmString = ampm[0];
+ mPmString = ampm[1];
+ }
+
+ void setShowAmPm(boolean show) {
+ if (mAmPmTextView != null) {
+ mAmPmTextView.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ void setIsMorning(boolean isMorning) {
+ if (mAmPmTextView != null) {
+ mAmPmTextView.setText(isMorning ? mAmString : mPmString);
+ }
+ }
+ }
+
+ private static class FormatChangeObserver extends ContentObserver {
+ private WeakReference<ClockView> mClock;
+ private Context mContext;
+ public FormatChangeObserver(ClockView clock) {
+ super(new Handler());
+ mClock = new WeakReference<ClockView>(clock);
+ mContext = clock.getContext();
+ }
+ @Override
+ public void onChange(boolean selfChange) {
+ ClockView digitalClock = mClock.get();
+ if (digitalClock != null) {
+ digitalClock.setDateFormat();
+ digitalClock.updateTime();
+ } else {
+ try {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ } catch (RuntimeException e) {
+ // Shouldn't happen
+ }
+ }
+ }
+ }
+
+ public ClockView(Context context) {
+ this(context, null);
+ }
+
+ public ClockView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTimeView = (TextView) findViewById(R.id.clock_text);
+ mTimeView.setTypeface(Typeface.createFromFile(ANDROID_CLOCK_FONT_FILE));
+ mAmPm = new AmPm(this, null);
+ mCalendar = Calendar.getInstance();
+ setDateFormat();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mAttached++;
+
+ /* monitor time ticks, time changed, timezone */
+ if (mIntentReceiver == null) {
+ mIntentReceiver = new TimeChangedReceiver(this);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.OWNER, filter, null, null );
+ }
+
+ /* monitor 12/24-hour display preference */
+ if (mFormatChangeObserver == null) {
+ mFormatChangeObserver = new FormatChangeObserver(this);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.CONTENT_URI, true, mFormatChangeObserver);
+ }
+
+ updateTime();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ mAttached--;
+
+ if (mIntentReceiver != null) {
+ mContext.unregisterReceiver(mIntentReceiver);
+ }
+ if (mFormatChangeObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(
+ mFormatChangeObserver);
+ }
+
+ mFormatChangeObserver = null;
+ mIntentReceiver = null;
+ }
+
+ void updateTime(Calendar c) {
+ mCalendar = c;
+ updateTime();
+ }
+
+ public void updateTime() {
+ mCalendar.setTimeInMillis(System.currentTimeMillis());
+
+ CharSequence newTime = DateFormat.format(mFormat, mCalendar);
+ mTimeView.setText(newTime);
+ mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0);
+ }
+
+ private void setDateFormat() {
+ mFormat = android.text.format.DateFormat.is24HourFormat(getContext()) ? M24 : M12;
+ mAmPm.setShowAmPm(mFormat.equals(M12));
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
new file mode 100644
index 0000000..6badaaf
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * This class implements a smart emergency button that updates itself based
+ * on telephony state. When the phone is idle, it is an emergency call button.
+ * When there's a call in progress, it presents an appropriate message and
+ * allows the user to return to the call.
+ */
+public class EmergencyButton extends Button {
+
+ private static final int EMERGENCY_CALL_TIMEOUT = 10000; // screen timeout after starting e.d.
+ private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+
+ KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+
+ @Override
+ public void onSimStateChanged(State simState) {
+ int phoneState = KeyguardUpdateMonitor.getInstance(mContext).getPhoneState();
+ updateEmergencyCallButton(simState, phoneState);
+ }
+
+ void onPhoneStateChanged(int phoneState) {
+ State simState = KeyguardUpdateMonitor.getInstance(mContext).getSimState();
+ updateEmergencyCallButton(simState, phoneState);
+ };
+ };
+ private LockPatternUtils mLockPatternUtils;
+ private PowerManager mPowerManager;
+
+ public EmergencyButton(Context context) {
+ this(context, null);
+ }
+
+ public EmergencyButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mLockPatternUtils = new LockPatternUtils(mContext);
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ takeEmergencyCallAction();
+ }
+ });
+ int phoneState = KeyguardUpdateMonitor.getInstance(mContext).getPhoneState();
+ State simState = KeyguardUpdateMonitor.getInstance(mContext).getSimState();
+ updateEmergencyCallButton(simState, phoneState);
+ }
+
+ /**
+ * Shows the emergency dialer or returns the user to the existing call.
+ */
+ public void takeEmergencyCallAction() {
+ // TODO: implement a shorter timeout once new PowerManager API is ready.
+ // should be the equivalent to the old userActivity(EMERGENCY_CALL_TIMEOUT)
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
+ if (TelephonyManager.getDefault().getCallState()
+ == TelephonyManager.CALL_STATE_OFFHOOK) {
+ mLockPatternUtils.resumeCall();
+ } else {
+ Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ getContext().startActivityAsUser(intent,
+ new UserHandle(mLockPatternUtils.getCurrentUser()));
+ }
+ }
+
+ private void updateEmergencyCallButton(State simState, int phoneState) {
+ boolean enabled = false;
+ if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) {
+ enabled = true; // always show "return to call" if phone is off-hook
+ } else if (mLockPatternUtils.isEmergencyCallCapable()) {
+ boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext).isSimLocked();
+ if (simLocked) {
+ // Some countries can't handle emergency calls while SIM is locked.
+ enabled = mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked();
+ } else {
+ // True if we need to show a secure screen (pin/pattern/SIM pin/SIM puk);
+ // hides emergency button on "Slide" screen if device is not secure.
+ enabled = mLockPatternUtils.isSecure();
+ }
+ }
+ mLockPatternUtils.updateEmergencyCallButtonState(this, phoneState, enabled,
+ KeyguardViewManager.USE_UPPER_CASE, false);
+ }
+
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java b/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java
new file mode 100644
index 0000000..6d392fc
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.keyguard.R;
+
+public class EmergencyCarrierArea extends LinearLayout {
+
+ private CarrierText mCarrierText;
+ private EmergencyButton mEmergencyButton;
+
+ public EmergencyCarrierArea(Context context) {
+ super(context);
+ }
+
+ public EmergencyCarrierArea(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mCarrierText = (CarrierText) findViewById(R.id.carrier_text);
+ mEmergencyButton = (EmergencyButton) findViewById(R.id.emergency_call_button);
+
+ // The emergency button overlaps the carrier text, only noticeable when highlighted.
+ // So temporarily hide the carrier text while the emergency button is pressed.
+ mEmergencyButton.setOnTouchListener(new OnTouchListener(){
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch(event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mCarrierText.animate().alpha(0);
+ break;
+ case MotionEvent.ACTION_UP:
+ mCarrierText.animate().alpha(1);
+ break;
+ }
+ return false;
+ }});
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/FaceUnlock.java b/packages/Keyguard/src/com/android/keyguard/FaceUnlock.java
new file mode 100644
index 0000000..689366b
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/FaceUnlock.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import com.android.internal.policy.IFaceLockCallback;
+import com.android.internal.policy.IFaceLockInterface;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.View;
+
+public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "FULLockscreen";
+
+ private final Context mContext;
+ private final LockPatternUtils mLockPatternUtils;
+
+ // TODO: is mServiceRunning needed or can we just use mIsRunning or check if mService is null?
+ private boolean mServiceRunning = false;
+ // TODO: now that the code has been restructure to do almost all operations from a handler, this
+ // lock may no longer be necessary.
+ private final Object mServiceRunningLock = new Object();
+ private IFaceLockInterface mService;
+ private boolean mBoundToService = false;
+ private View mFaceUnlockView;
+
+ private Handler mHandler;
+ private final int MSG_SERVICE_CONNECTED = 0;
+ private final int MSG_SERVICE_DISCONNECTED = 1;
+ private final int MSG_UNLOCK = 2;
+ private final int MSG_CANCEL = 3;
+ private final int MSG_REPORT_FAILED_ATTEMPT = 4;
+ private final int MSG_POKE_WAKELOCK = 5;
+
+ // TODO: This was added for the purpose of adhering to what the biometric interface expects
+ // the isRunning() function to return. However, it is probably not necessary to have both
+ // mRunning and mServiceRunning. I'd just rather wait to change that logic.
+ private volatile boolean mIsRunning = false;
+
+ // So the user has a consistent amount of time when brought to the backup method from Face
+ // Unlock
+ private final int BACKUP_LOCK_TIMEOUT = 5000;
+
+ KeyguardSecurityCallback mKeyguardScreenCallback;
+
+ /**
+ * Stores some of the structures that Face Unlock will need to access and creates the handler
+ * will be used to execute messages on the UI thread.
+ */
+ public FaceUnlock(Context context) {
+ mContext = context;
+ mLockPatternUtils = new LockPatternUtils(context);
+ mHandler = new Handler(this);
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback) {
+ mKeyguardScreenCallback = keyguardScreenCallback;
+ }
+
+ /**
+ * Stores and displays the view that Face Unlock is allowed to draw within.
+ * TODO: since the layout object will eventually be shared by multiple biometric unlock
+ * methods, we will have to add our other views (background, cancel button) here.
+ */
+ public void initializeView(View biometricUnlockView) {
+ Log.d(TAG, "initializeView()");
+ mFaceUnlockView = biometricUnlockView;
+ }
+
+ /**
+ * Indicates whether Face Unlock is currently running.
+ */
+ public boolean isRunning() {
+ return mIsRunning;
+ }
+
+ /**
+ * Dismisses face unlock and goes to the backup lock
+ */
+ public void stopAndShowBackup() {
+ if (DEBUG) Log.d(TAG, "stopAndShowBackup()");
+ mHandler.sendEmptyMessage(MSG_CANCEL);
+ }
+
+ /**
+ * Binds to the Face Unlock service. Face Unlock will be started when the bind completes. The
+ * Face Unlock view is displayed to hide the backup lock while the service is starting up.
+ * Called on the UI thread.
+ */
+ public boolean start() {
+ if (DEBUG) Log.d(TAG, "start()");
+ if (mHandler.getLooper() != Looper.myLooper()) {
+ Log.e(TAG, "start() called off of the UI thread");
+ }
+
+ if (mIsRunning) {
+ Log.w(TAG, "start() called when already running");
+ }
+
+ if (!mBoundToService) {
+ Log.d(TAG, "Binding to Face Unlock service for user="
+ + mLockPatternUtils.getCurrentUser());
+ mContext.bindServiceAsUser(new Intent(IFaceLockInterface.class.getName()),
+ mConnection,
+ Context.BIND_AUTO_CREATE,
+ new UserHandle(mLockPatternUtils.getCurrentUser()));
+ mBoundToService = true;
+ } else {
+ Log.w(TAG, "Attempt to bind to Face Unlock when already bound");
+ }
+
+ mIsRunning = true;
+ return true;
+ }
+
+ /**
+ * Stops Face Unlock and unbinds from the service. Called on the UI thread.
+ */
+ public boolean stop() {
+ if (DEBUG) Log.d(TAG, "stop()");
+ if (mHandler.getLooper() != Looper.myLooper()) {
+ Log.e(TAG, "stop() called from non-UI thread");
+ }
+
+ // Clearing any old service connected messages.
+ mHandler.removeMessages(MSG_SERVICE_CONNECTED);
+
+ boolean mWasRunning = mIsRunning;
+
+ stopUi();
+
+ if (mBoundToService) {
+ if (mService != null) {
+ try {
+ mService.unregisterCallback(mFaceUnlockCallback);
+ } catch (RemoteException e) {
+ // Not much we can do
+ }
+ }
+ Log.d(TAG, "Unbinding from Face Unlock service");
+ mContext.unbindService(mConnection);
+ mBoundToService = false;
+ } else {
+ // This is usually not an error when this happens. Sometimes we will tell it to
+ // unbind multiple times because it's called from both onWindowFocusChanged and
+ // onDetachedFromWindow.
+ if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound");
+ }
+ mIsRunning = false;
+ return mWasRunning;
+ }
+
+ /**
+ * Frees up resources used by Face Unlock and stops it if it is still running.
+ */
+ public void cleanUp() {
+ if (DEBUG) Log.d(TAG, "cleanUp()");
+ if (mService != null) {
+ try {
+ mService.unregisterCallback(mFaceUnlockCallback);
+ } catch (RemoteException e) {
+ // Not much we can do
+ }
+ stopUi();
+ mService = null;
+ }
+ }
+
+ /**
+ * Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK.
+ */
+ public int getQuality() {
+ return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
+ }
+
+ /**
+ * Handles messages such that everything happens on the UI thread in a deterministic order.
+ * Calls from the Face Unlock service come from binder threads. Calls from lockscreen typically
+ * come from the UI thread. This makes sure there are no race conditions between those calls.
+ */
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SERVICE_CONNECTED:
+ handleServiceConnected();
+ break;
+ case MSG_SERVICE_DISCONNECTED:
+ handleServiceDisconnected();
+ break;
+ case MSG_UNLOCK:
+ handleUnlock(msg.arg1);
+ break;
+ case MSG_CANCEL:
+ handleCancel();
+ break;
+ case MSG_REPORT_FAILED_ATTEMPT:
+ handleReportFailedAttempt();
+ break;
+ case MSG_POKE_WAKELOCK:
+ handlePokeWakelock(msg.arg1);
+ break;
+ default:
+ Log.e(TAG, "Unhandled message");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Tells the service to start its UI via an AIDL interface. Called when the
+ * onServiceConnected() callback is received.
+ */
+ void handleServiceConnected() {
+ Log.d(TAG, "handleServiceConnected()");
+
+ // It is possible that an unbind has occurred in the time between the bind and when this
+ // function is reached. If an unbind has already occurred, proceeding on to call startUi()
+ // can result in a fatal error. Note that the onServiceConnected() callback is
+ // asynchronous, so this possibility would still exist if we executed this directly in
+ // onServiceConnected() rather than using a handler.
+ if (!mBoundToService) {
+ Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound");
+ return;
+ }
+
+ try {
+ mService.registerCallback(mFaceUnlockCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString());
+ mService = null;
+ mBoundToService = false;
+ mIsRunning = false;
+ return;
+ }
+
+ if (mFaceUnlockView != null) {
+ IBinder windowToken = mFaceUnlockView.getWindowToken();
+ if (windowToken != null) {
+ // When switching between portrait and landscape view while Face Unlock is running,
+ // the screen will eventually go dark unless we poke the wakelock when Face Unlock
+ // is restarted.
+ mKeyguardScreenCallback.userActivity(0);
+
+ int[] position;
+ position = new int[2];
+ mFaceUnlockView.getLocationInWindow(position);
+ startUi(windowToken, position[0], position[1], mFaceUnlockView.getWidth(),
+ mFaceUnlockView.getHeight());
+ } else {
+ Log.e(TAG, "windowToken is null in handleServiceConnected()");
+ }
+ }
+ }
+
+ /**
+ * Called when the onServiceDisconnected() callback is received. This should not happen during
+ * normal operation. It indicates an error has occurred.
+ */
+ void handleServiceDisconnected() {
+ Log.e(TAG, "handleServiceDisconnected()");
+ // TODO: this lock may no longer be needed now that everything is being called from a
+ // handler
+ synchronized (mServiceRunningLock) {
+ mService = null;
+ mServiceRunning = false;
+ }
+ mBoundToService = false;
+ mIsRunning = false;
+ }
+
+ /**
+ * Stops the Face Unlock service and tells the device to grant access to the user.
+ */
+ void handleUnlock(int authenticatedUserId) {
+ if (DEBUG) Log.d(TAG, "handleUnlock()");
+ stop();
+ int currentUserId = mLockPatternUtils.getCurrentUser();
+ if (authenticatedUserId == currentUserId) {
+ if (DEBUG) Log.d(TAG, "Unlocking for user " + authenticatedUserId);
+ mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
+ mKeyguardScreenCallback.dismiss(true);
+ } else {
+ Log.d(TAG, "Ignoring unlock for authenticated user (" + authenticatedUserId +
+ ") because the current user is " + currentUserId);
+ }
+ }
+
+ /**
+ * Stops the Face Unlock service and goes to the backup lock.
+ */
+ void handleCancel() {
+ if (DEBUG) Log.d(TAG, "handleCancel()");
+ // We are going to the backup method, so we don't want to see Face Unlock again until the
+ // next time the user visits keyguard.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
+
+ mKeyguardScreenCallback.showBackupSecurity();
+ stop();
+ mKeyguardScreenCallback.userActivity(BACKUP_LOCK_TIMEOUT);
+ }
+
+ /**
+ * Increments the number of failed Face Unlock attempts.
+ */
+ void handleReportFailedAttempt() {
+ if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()");
+ // We are going to the backup method, so we don't want to see Face Unlock again until the
+ // next time the user visits keyguard.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
+
+ mKeyguardScreenCallback.reportFailedUnlockAttempt();
+ }
+
+ /**
+ * If the screen is on, pokes the wakelock to keep the screen alive and active for a specific
+ * amount of time.
+ */
+ void handlePokeWakelock(int millis) {
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ if (powerManager.isScreenOn()) {
+ mKeyguardScreenCallback.userActivity(millis);
+ }
+ }
+
+ /**
+ * Implements service connection methods.
+ */
+ private ServiceConnection mConnection = new ServiceConnection() {
+ /**
+ * Called when the Face Unlock service connects after calling bind().
+ */
+ public void onServiceConnected(ComponentName className, IBinder iservice) {
+ Log.d(TAG, "Connected to Face Unlock service");
+ mService = IFaceLockInterface.Stub.asInterface(iservice);
+ mHandler.sendEmptyMessage(MSG_SERVICE_CONNECTED);
+ }
+
+ /**
+ * Called if the Face Unlock service unexpectedly disconnects. This indicates an error.
+ */
+ public void onServiceDisconnected(ComponentName className) {
+ Log.e(TAG, "Unexpected disconnect from Face Unlock service");
+ mHandler.sendEmptyMessage(MSG_SERVICE_DISCONNECTED);
+ }
+ };
+
+ /**
+ * Tells the Face Unlock service to start displaying its UI and start processing.
+ */
+ private void startUi(IBinder windowToken, int x, int y, int w, int h) {
+ if (DEBUG) Log.d(TAG, "startUi()");
+ synchronized (mServiceRunningLock) {
+ if (!mServiceRunning) {
+ Log.d(TAG, "Starting Face Unlock");
+ try {
+ mService.startUi(windowToken, x, y, w, h,
+ mLockPatternUtils.isBiometricWeakLivelinessEnabled());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString());
+ return;
+ }
+ mServiceRunning = true;
+ } else {
+ Log.w(TAG, "startUi() attempted while running");
+ }
+ }
+ }
+
+ /**
+ * Tells the Face Unlock service to stop displaying its UI and stop processing.
+ */
+ private void stopUi() {
+ if (DEBUG) Log.d(TAG, "stopUi()");
+ // Note that attempting to stop Face Unlock when it's not running is not an issue.
+ // Face Unlock can return, which stops it and then we try to stop it when the
+ // screen is turned off. That's why we check.
+ synchronized (mServiceRunningLock) {
+ if (mServiceRunning) {
+ Log.d(TAG, "Stopping Face Unlock");
+ try {
+ mService.stopUi();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString());
+ }
+ mServiceRunning = false;
+ } else {
+ // This is usually not an error when this happens. Sometimes we will tell it to
+ // stop multiple times because it's called from both onWindowFocusChanged and
+ // onDetachedFromWindow.
+ if (DEBUG) Log.d(TAG, "stopUi() attempted while not running");
+ }
+ }
+ }
+
+ /**
+ * Implements the AIDL biometric unlock service callback interface.
+ */
+ private final IFaceLockCallback mFaceUnlockCallback = new IFaceLockCallback.Stub() {
+ /**
+ * Called when Face Unlock wants to grant access to the user.
+ */
+ public void unlock() {
+ if (DEBUG) Log.d(TAG, "unlock()");
+ Message message = mHandler.obtainMessage(MSG_UNLOCK, UserHandle.getCallingUserId(), -1);
+ mHandler.sendMessage(message);
+ }
+
+ /**
+ * Called when Face Unlock wants to go to the backup.
+ */
+ public void cancel() {
+ if (DEBUG) Log.d(TAG, "cancel()");
+ mHandler.sendEmptyMessage(MSG_CANCEL);
+ }
+
+ /**
+ * Called when Face Unlock wants to increment the number of failed attempts.
+ */
+ public void reportFailedAttempt() {
+ if (DEBUG) Log.d(TAG, "reportFailedAttempt()");
+ mHandler.sendEmptyMessage(MSG_REPORT_FAILED_ATTEMPT);
+ }
+
+ /**
+ * Called when Face Unlock wants to keep the screen alive and active for a specific amount
+ * of time.
+ */
+ public void pokeWakelock(int millis) {
+ if (DEBUG) Log.d(TAG, "pokeWakelock() for " + millis + "ms");
+ Message message = mHandler.obtainMessage(MSG_POKE_WAKELOCK, millis, -1);
+ mHandler.sendMessage(message);
+ }
+
+ };
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
new file mode 100644
index 0000000..fb2eeda
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * Base class for PIN and password unlock screens.
+ */
+public abstract class KeyguardAbsKeyInputView extends LinearLayout
+ implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+ protected KeyguardSecurityCallback mCallback;
+ protected TextView mPasswordEntry;
+ protected LockPatternUtils mLockPatternUtils;
+ protected SecurityMessageDisplay mSecurityMessageDisplay;
+ protected View mEcaView;
+ private Drawable mBouncerFrame;
+ protected boolean mEnableHaptics;
+
+ // To avoid accidental lockout due to events while the device in in the pocket, ignore
+ // any passwords with length less than or equal to this length.
+ protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+
+ public KeyguardAbsKeyInputView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ if (hasWindowFocus) {
+ reset();
+ }
+ }
+
+ public void reset() {
+ // start fresh
+ mPasswordEntry.setText("");
+ mPasswordEntry.requestFocus();
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ } else {
+ resetState();
+ }
+ }
+
+ protected abstract int getPasswordTextViewId();
+ protected abstract void resetState();
+
+ @Override
+ protected void onFinishInflate() {
+ mLockPatternUtils = new LockPatternUtils(mContext);
+
+ mPasswordEntry = (TextView) findViewById(getPasswordTextViewId());
+ mPasswordEntry.setOnEditorActionListener(this);
+ mPasswordEntry.addTextChangedListener(this);
+
+ // Set selected property on so the view can send accessibility events.
+ mPasswordEntry.setSelected(true);
+
+ // Poke the wakelock any time the text is selected or modified
+ mPasswordEntry.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ mCallback.userActivity(0); // TODO: customize timeout for text?
+ }
+ });
+
+ mPasswordEntry.addTextChangedListener(new TextWatcher() {
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ public void afterTextChanged(Editable s) {
+ if (mCallback != null) {
+ mCallback.userActivity(0);
+ }
+ }
+ });
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
+ mEcaView = findViewById(R.id.keyguard_selector_fade_container);
+ View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame);
+ if (bouncerFrameView != null) {
+ mBouncerFrame = bouncerFrameView.getBackground();
+ }
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ // send focus to the password field
+ return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
+ }
+
+ /*
+ * Override this if you have a different string for "wrong password"
+ *
+ * Note that PIN/PUK have their own implementation of verifyPasswordAndUnlock and so don't need this
+ */
+ protected int getWrongPasswordStringId() {
+ return R.string.kg_wrong_password;
+ }
+
+ protected void verifyPasswordAndUnlock() {
+ String entry = mPasswordEntry.getText().toString();
+ if (mLockPatternUtils.checkPassword(entry)) {
+ mCallback.reportSuccessfulUnlockAttempt();
+ mCallback.dismiss(true);
+ } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
+ // to avoid accidental lockout, only count attempts that are long enough to be a
+ // real password. This may require some tweaking.
+ mCallback.reportFailedUnlockAttempt();
+ if (0 == (mCallback.getFailedAttempts()
+ % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
+ handleAttemptLockout(deadline);
+ }
+ mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true);
+ }
+ mPasswordEntry.setText("");
+ }
+
+ // Prevent user from using the PIN/Password entry until scheduled deadline.
+ protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mPasswordEntry.setEnabled(false);
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ int secondsRemaining = (int) (millisUntilFinished / 1000);
+ mSecurityMessageDisplay.setMessage(
+ R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining);
+ }
+
+ @Override
+ public void onFinish() {
+ mSecurityMessageDisplay.setMessage("", false);
+ resetState();
+ }
+ }.start();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ mCallback.userActivity(0);
+ return false;
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // Check if this was the result of hitting the enter key
+ if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT) {
+ verifyPasswordAndUnlock();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+
+ }
+
+ @Override
+ public void onResume(int reason) {
+ reset();
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ if (mCallback != null) {
+ mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS);
+ }
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+
+ // Cause a VIRTUAL_KEY vibration
+ public void doHapticKeyClick() {
+ if (mEnableHaptics) {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
+ }
+
+ @Override
+ public void showBouncer(int duration) {
+ KeyguardSecurityViewHelper.
+ showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
+ }
+
+ @Override
+ public void hideBouncer(int duration) {
+ KeyguardSecurityViewHelper.
+ hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
+ }
+}
+
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java
new file mode 100644
index 0000000..6b8be69
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.LoginFilter;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import java.io.IOException;
+
+/**
+ * When the user forgets their password a bunch of times, we fall back on their
+ * account's login/password to unlock the phone (and reset their lock pattern).
+ */
+public class KeyguardAccountView extends LinearLayout implements KeyguardSecurityView,
+ View.OnClickListener, TextWatcher {
+ private static final int AWAKE_POKE_MILLIS = 30000;
+ private static final String LOCK_PATTERN_PACKAGE = "com.android.settings";
+ private static final String LOCK_PATTERN_CLASS = LOCK_PATTERN_PACKAGE + ".ChooseLockGeneric";
+
+ private KeyguardSecurityCallback mCallback;
+ private LockPatternUtils mLockPatternUtils;
+ private EditText mLogin;
+ private EditText mPassword;
+ private Button mOk;
+ public boolean mEnableFallback;
+ private SecurityMessageDisplay mSecurityMessageDisplay;
+
+ /**
+ * Shown while making asynchronous check of password.
+ */
+ private ProgressDialog mCheckingDialog;
+
+ public KeyguardAccountView(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardAccountView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardAccountView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mLockPatternUtils = new LockPatternUtils(getContext());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mLogin = (EditText) findViewById(R.id.login);
+ mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } );
+ mLogin.addTextChangedListener(this);
+
+ mPassword = (EditText) findViewById(R.id.password);
+ mPassword.addTextChangedListener(this);
+
+ mOk = (Button) findViewById(R.id.ok);
+ mOk.setOnClickListener(this);
+
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
+ reset();
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+
+ public void afterTextChanged(Editable s) {
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ if (mCallback != null) {
+ mCallback.userActivity(AWAKE_POKE_MILLIS);
+ }
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction,
+ Rect previouslyFocusedRect) {
+ // send focus to the login field
+ return mLogin.requestFocus(direction, previouslyFocusedRect);
+ }
+
+ public boolean needsInput() {
+ return true;
+ }
+
+ public void reset() {
+ // start fresh
+ mLogin.setText("");
+ mPassword.setText("");
+ mLogin.requestFocus();
+ boolean permLocked = mLockPatternUtils.isPermanentlyLocked();
+ mSecurityMessageDisplay.setMessage(permLocked ? R.string.kg_login_too_many_attempts :
+ R.string.kg_login_instructions, permLocked ? true : false);
+ }
+
+ /** {@inheritDoc} */
+ public void cleanUp() {
+ if (mCheckingDialog != null) {
+ mCheckingDialog.hide();
+ }
+ mCallback = null;
+ mLockPatternUtils = null;
+ }
+
+ public void onClick(View v) {
+ mCallback.userActivity(0);
+ if (v == mOk) {
+ asyncCheckPassword();
+ }
+ }
+
+ private void postOnCheckPasswordResult(final boolean success) {
+ // ensure this runs on UI thread
+ mLogin.post(new Runnable() {
+ public void run() {
+ if (success) {
+ // clear out forgotten password
+ mLockPatternUtils.setPermanentlyLocked(false);
+ mLockPatternUtils.setLockPatternEnabled(false);
+ mLockPatternUtils.saveLockPattern(null);
+
+ // launch the 'choose lock pattern' activity so
+ // the user can pick a new one if they want to
+ Intent intent = new Intent();
+ intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivityAsUser(intent,
+ new UserHandle(mLockPatternUtils.getCurrentUser()));
+ mCallback.reportSuccessfulUnlockAttempt();
+
+ // dismiss keyguard
+ mCallback.dismiss(true);
+ } else {
+ mSecurityMessageDisplay.setMessage(R.string.kg_login_invalid_input, true);
+ mPassword.setText("");
+ mCallback.reportFailedUnlockAttempt();
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN
+ && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ if (mLockPatternUtils.isPermanentlyLocked()) {
+ mCallback.dismiss(false);
+ } else {
+ // TODO: mCallback.forgotPattern(false);
+ }
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ /**
+ * Given the string the user entered in the 'username' field, find
+ * the stored account that they probably intended. Prefer, in order:
+ *
+ * - an exact match for what was typed, or
+ * - a case-insensitive match for what was typed, or
+ * - if they didn't include a domain, an exact match of the username, or
+ * - if they didn't include a domain, a case-insensitive
+ * match of the username.
+ *
+ * If there is a tie for the best match, choose neither --
+ * the user needs to be more specific.
+ *
+ * @return an account name from the database, or null if we can't
+ * find a single best match.
+ */
+ private Account findIntendedAccount(String username) {
+ Account[] accounts = AccountManager.get(mContext).getAccountsByTypeAsUser("com.google",
+ new UserHandle(mLockPatternUtils.getCurrentUser()));
+
+ // Try to figure out which account they meant if they
+ // typed only the username (and not the domain), or got
+ // the case wrong.
+
+ Account bestAccount = null;
+ int bestScore = 0;
+ for (Account a: accounts) {
+ int score = 0;
+ if (username.equals(a.name)) {
+ score = 4;
+ } else if (username.equalsIgnoreCase(a.name)) {
+ score = 3;
+ } else if (username.indexOf('@') < 0) {
+ int i = a.name.indexOf('@');
+ if (i >= 0) {
+ String aUsername = a.name.substring(0, i);
+ if (username.equals(aUsername)) {
+ score = 2;
+ } else if (username.equalsIgnoreCase(aUsername)) {
+ score = 1;
+ }
+ }
+ }
+ if (score > bestScore) {
+ bestAccount = a;
+ bestScore = score;
+ } else if (score == bestScore) {
+ bestAccount = null;
+ }
+ }
+ return bestAccount;
+ }
+
+ private void asyncCheckPassword() {
+ mCallback.userActivity(AWAKE_POKE_MILLIS);
+ final String login = mLogin.getText().toString();
+ final String password = mPassword.getText().toString();
+ Account account = findIntendedAccount(login);
+ if (account == null) {
+ postOnCheckPasswordResult(false);
+ return;
+ }
+ getProgressDialog().show();
+ Bundle options = new Bundle();
+ options.putString(AccountManager.KEY_PASSWORD, password);
+ AccountManager.get(mContext).confirmCredentialsAsUser(account, options, null /* activity */,
+ new AccountManagerCallback<Bundle>() {
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ mCallback.userActivity(AWAKE_POKE_MILLIS);
+ final Bundle result = future.getResult();
+ final boolean verified = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
+ postOnCheckPasswordResult(verified);
+ } catch (OperationCanceledException e) {
+ postOnCheckPasswordResult(false);
+ } catch (IOException e) {
+ postOnCheckPasswordResult(false);
+ } catch (AuthenticatorException e) {
+ postOnCheckPasswordResult(false);
+ } finally {
+ mLogin.post(new Runnable() {
+ public void run() {
+ getProgressDialog().hide();
+ }
+ });
+ }
+ }
+ }, null /* handler */, new UserHandle(mLockPatternUtils.getCurrentUser()));
+ }
+
+ private Dialog getProgressDialog() {
+ if (mCheckingDialog == null) {
+ mCheckingDialog = new ProgressDialog(mContext);
+ mCheckingDialog.setMessage(
+ mContext.getString(R.string.kg_login_checking_password));
+ mCheckingDialog.setIndeterminate(true);
+ mCheckingDialog.setCancelable(false);
+ mCheckingDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ return mCheckingDialog;
+ }
+
+ @Override
+ public void onPause() {
+
+ }
+
+ @Override
+ public void onResume(int reason) {
+ reset();
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
+ @Override
+ public void showBouncer(int duration) {
+ }
+
+ @Override
+ public void hideBouncer(int duration) {
+ }
+}
+
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java b/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java
new file mode 100644
index 0000000..9a1aa5b
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
+import android.app.IActivityManager.WaitResult;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
+
+import java.util.List;
+
+public abstract class KeyguardActivityLauncher {
+ private static final String TAG = KeyguardActivityLauncher.class.getSimpleName();
+ private static final boolean DEBUG = KeyguardHostView.DEBUG;
+ private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
+ private static final Intent SECURE_CAMERA_INTENT =
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+ .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ private static final Intent INSECURE_CAMERA_INTENT =
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+
+ abstract Context getContext();
+
+ abstract KeyguardSecurityCallback getCallback();
+
+ abstract LockPatternUtils getLockPatternUtils();
+
+ public static class CameraWidgetInfo {
+ public String contextPackage;
+ public int layoutId;
+ }
+
+ public CameraWidgetInfo getCameraWidgetInfo() {
+ CameraWidgetInfo info = new CameraWidgetInfo();
+ Intent intent = getCameraIntent();
+ PackageManager packageManager = getContext().getPackageManager();
+ final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
+ intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
+ if (appList.size() == 0) {
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found");
+ return null;
+ }
+ ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
+ PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
+ getLockPatternUtils().getCurrentUser());
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved);
+ if (wouldLaunchResolverActivity(resolved, appList)) {
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver");
+ return info;
+ }
+ if (resolved == null || resolved.activityInfo == null) {
+ return null;
+ }
+ if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found");
+ return info;
+ }
+ int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
+ if (layoutId == 0) {
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified");
+ return info;
+ }
+ info.contextPackage = resolved.activityInfo.packageName;
+ info.layoutId = layoutId;
+ return info;
+ }
+
+ public void launchCamera(Handler worker, Runnable onSecureCameraStarted) {
+ LockPatternUtils lockPatternUtils = getLockPatternUtils();
+ if (lockPatternUtils.isSecure()) {
+ // Launch the secure version of the camera
+ if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
+ // TODO: Show disambiguation dialog instead.
+ // For now, we'll treat this like launching any other app from secure keyguard.
+ // When they do, user sees the system's ResolverActivity which lets them choose
+ // which secure camera to use.
+ launchActivity(SECURE_CAMERA_INTENT, false, false, null, null);
+ } else {
+ launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted);
+ }
+ } else {
+ // Launch the normal camera
+ launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null);
+ }
+ }
+
+ public void launchWidgetPicker(int appWidgetId) {
+ Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK);
+
+ pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false);
+ pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
+
+ Bundle options = new Bundle();
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
+ pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
+ pickIntent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+ launchActivity(pickIntent, false, false, null, null);
+ }
+
+ /**
+ * Launches the said intent for the current foreground user.
+ *
+ * @param intent
+ * @param showsWhileLocked true if the activity can be run on top of keyguard.
+ * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
+ * @param useDefaultAnimations true if default transitions should be used, else suppressed.
+ * @param worker if supplied along with onStarted, used to launch the blocking activity call.
+ * @param onStarted if supplied along with worker, called after activity is started.
+ */
+ public void launchActivity(final Intent intent,
+ boolean showsWhileLocked,
+ boolean useDefaultAnimations,
+ final Handler worker,
+ final Runnable onStarted) {
+
+ final Context context = getContext();
+ final Bundle animation = useDefaultAnimations ? null
+ : ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle();
+ launchActivityWithAnimation(intent, showsWhileLocked, animation, worker, onStarted);
+ }
+
+ public void launchActivityWithAnimation(final Intent intent,
+ boolean showsWhileLocked,
+ final Bundle animation,
+ final Handler worker,
+ final Runnable onStarted) {
+
+ LockPatternUtils lockPatternUtils = getLockPatternUtils();
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ boolean isSecure = lockPatternUtils.isSecure();
+ if (!isSecure || showsWhileLocked) {
+ if (!isSecure) {
+ dismissKeyguardOnNextActivity();
+ }
+ try {
+ if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s",
+ intent, SystemClock.uptimeMillis()));
+ startActivityForCurrentUser(intent, animation, worker, onStarted);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity not found for intent + " + intent.getAction());
+ }
+ } else {
+ // Create a runnable to start the activity and ask the user to enter their
+ // credentials.
+ KeyguardSecurityCallback callback = getCallback();
+ callback.setOnDismissAction(new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ dismissKeyguardOnNextActivity();
+ startActivityForCurrentUser(intent, animation, worker, onStarted);
+ return true;
+ }
+ });
+ callback.dismiss(false);
+ }
+ }
+
+ private void dismissKeyguardOnNextActivity() {
+ try {
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ Log.w(TAG, "can't dismiss keyguard on launch");
+ }
+ }
+
+ private void startActivityForCurrentUser(final Intent intent, final Bundle options,
+ Handler worker, final Runnable onStarted) {
+ final UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
+ if (worker == null || onStarted == null) {
+ getContext().startActivityAsUser(intent, options, user);
+ return;
+ }
+ // if worker + onStarted are supplied, run blocking activity launch call in the background
+ worker.post(new Runnable(){
+ @Override
+ public void run() {
+ try {
+ WaitResult result = ActivityManagerNative.getDefault().startActivityAndWait(
+ null /*caller*/,
+ null /*caller pkg*/,
+ intent,
+ intent.resolveTypeIfNeeded(getContext().getContentResolver()),
+ null /*resultTo*/,
+ null /*resultWho*/,
+ 0 /*requestCode*/,
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ null /*profileFile*/,
+ null /*profileFd*/,
+ options,
+ user.getIdentifier());
+ if (DEBUG) Log.d(TAG, String.format("waitResult[%s,%s,%s,%s] at %s",
+ result.result, result.thisTime, result.totalTime, result.who,
+ SystemClock.uptimeMillis()));
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error starting activity", e);
+ return;
+ }
+ try {
+ onStarted.run();
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onStarted callback", t);
+ }
+ }});
+ }
+
+ private Intent getCameraIntent() {
+ return getLockPatternUtils().isSecure() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
+ }
+
+ private boolean wouldLaunchResolverActivity(Intent intent) {
+ PackageManager packageManager = getContext().getPackageManager();
+ ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
+ PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
+ List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
+ intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
+ return wouldLaunchResolverActivity(resolved, appList);
+ }
+
+ private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
+ // If the list contains the above resolved activity, then it can't be
+ // ResolverActivity itself.
+ for (int i = 0; i < appList.size(); i++) {
+ ResolveInfo tmp = appList.get(i);
+ if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
+ && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardCircleFramedDrawable.java b/packages/Keyguard/src/com/android/keyguard/KeyguardCircleFramedDrawable.java
new file mode 100644
index 0000000..63b61ad
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardCircleFramedDrawable.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import android.util.Log;
+
+class KeyguardCircleFramedDrawable extends Drawable {
+
+ private final Bitmap mBitmap;
+ private final int mSize;
+ private final Paint mPaint;
+ private final float mShadowRadius;
+ private final float mStrokeWidth;
+ private final int mFrameColor;
+ private final int mHighlightColor;
+ private final int mFrameShadowColor;
+
+ private float mScale;
+ private Path mFramePath;
+ private Rect mSrcRect;
+ private RectF mDstRect;
+ private RectF mFrameRect;
+ private boolean mPressed;
+
+ public KeyguardCircleFramedDrawable(Bitmap bitmap, int size,
+ int frameColor, float strokeWidth,
+ int frameShadowColor, float shadowRadius,
+ int highlightColor) {
+ super();
+ mSize = size;
+ mShadowRadius = shadowRadius;
+ mFrameColor = frameColor;
+ mFrameShadowColor = frameShadowColor;
+ mStrokeWidth = strokeWidth;
+ mHighlightColor = highlightColor;
+
+ mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(mBitmap);
+
+ final int width = bitmap.getWidth();
+ final int height = bitmap.getHeight();
+ final int square = Math.min(width, height);
+
+ final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square);
+ final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
+ circleRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f);
+ circleRect.inset(mShadowRadius, mShadowRadius);
+
+ final Path fillPath = new Path();
+ fillPath.addArc(circleRect, 0f, 360f);
+
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+ // opaque circle matte
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setColor(Color.BLACK);
+ mPaint.setStyle(Paint.Style.FILL);
+ canvas.drawPath(fillPath, mPaint);
+
+ // mask in the icon where the bitmap is opaque
+ mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
+ canvas.drawBitmap(bitmap, cropRect, circleRect, mPaint);
+
+ // prepare paint for frame drawing
+ mPaint.setXfermode(null);
+
+ mScale = 1f;
+
+ mSrcRect = new Rect(0, 0, mSize, mSize);
+ mDstRect = new RectF(0, 0, mSize, mSize);
+ mFrameRect = new RectF(mDstRect);
+ mFramePath = new Path();
+ }
+
+ public void reset() {
+ mScale = 1f;
+ mPressed = false;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ // clear background
+ final float outside = Math.min(canvas.getWidth(), canvas.getHeight());
+ final float inside = mScale * outside;
+ final float pad = (outside - inside) / 2f;
+
+ mDstRect.set(pad, pad, outside - pad, outside - pad);
+ canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+
+ mFrameRect.set(mDstRect);
+ mFrameRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f);
+ mFrameRect.inset(mShadowRadius, mShadowRadius);
+
+ mFramePath.reset();
+ mFramePath.addArc(mFrameRect, 0f, 360f);
+
+ // white frame
+ if (mPressed) {
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(Color.argb((int) (0.33f * 255),
+ Color.red(mHighlightColor),
+ Color.green(mHighlightColor),
+ Color.blue(mHighlightColor)));
+ canvas.drawPath(mFramePath, mPaint);
+ }
+ mPaint.setStrokeWidth(mStrokeWidth);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setColor(mPressed ? mHighlightColor : mFrameColor);
+ mPaint.setShadowLayer(mShadowRadius, 0f, 0f, mFrameShadowColor);
+ canvas.drawPath(mFramePath, mPaint);
+ }
+
+ public void setScale(float scale) {
+ mScale = scale;
+ }
+
+ public float getScale() {
+ return mScale;
+ }
+
+ public void setPressed(boolean pressed) {
+ mPressed = pressed;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ }
+
+ public boolean verifyParams(float iconSize, int frameColor, float stroke,
+ int frameShadowColor, float shadowRadius, int highlightColor) {
+ return mSize == iconSize
+ && mFrameColor == frameColor
+ && mStrokeWidth == stroke
+ && mFrameShadowColor == frameShadowColor
+ && mShadowRadius == shadowRadius
+ && mHighlightColor == highlightColor;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
new file mode 100644
index 0000000..3e499b2
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.IRotationWatcher;
+import android.view.IWindowManager;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import java.lang.Math;
+
+public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecurityView {
+
+ private static final String TAG = "FULKeyguardFaceUnlockView";
+ private static final boolean DEBUG = false;
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+ private LockPatternUtils mLockPatternUtils;
+ private BiometricSensorUnlock mBiometricUnlock;
+ private View mFaceUnlockAreaView;
+ private ImageButton mCancelButton;
+ private SecurityMessageDisplay mSecurityMessageDisplay;
+ private View mEcaView;
+ private Drawable mBouncerFrame;
+
+ private boolean mIsShowing = false;
+ private final Object mIsShowingLock = new Object();
+
+ private int mLastRotation;
+ private boolean mWatchingRotation;
+ private final IWindowManager mWindowManager =
+ IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+
+ private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() {
+ public void onRotationChanged(int rotation) {
+ if (DEBUG) Log.d(TAG, "onRotationChanged(): " + mLastRotation + "->" + rotation);
+
+ // If the difference between the new rotation value and the previous rotation value is
+ // equal to 2, the rotation change was 180 degrees. This stops the biometric unlock
+ // and starts it in the new position. This is not performed for 90 degree rotations
+ // since a 90 degree rotation is a configuration change, which takes care of this for
+ // us.
+ if (Math.abs(rotation - mLastRotation) == 2) {
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.stop();
+ maybeStartBiometricUnlock();
+ }
+ }
+ mLastRotation = rotation;
+ }
+ };
+
+ public KeyguardFaceUnlockView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardFaceUnlockView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ initializeBiometricUnlockView();
+
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
+ mEcaView = findViewById(R.id.keyguard_selector_fade_container);
+ View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame);
+ if (bouncerFrameView != null) {
+ mBouncerFrame = bouncerFrameView.getBackground();
+ }
+ }
+
+ @Override
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mKeyguardSecurityCallback = callback;
+ // TODO: formalize this in the interface or factor it out
+ ((FaceUnlock)mBiometricUnlock).setKeyguardCallback(callback);
+ }
+
+ @Override
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ if (DEBUG) Log.d(TAG, "onDetachedFromWindow()");
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.stop();
+ }
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback);
+ if (mWatchingRotation) {
+ try {
+ mWindowManager.removeRotationWatcher(mRotationWatcher);
+ mWatchingRotation = false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception when removing rotation watcher");
+ }
+ }
+ }
+
+ @Override
+ public void onPause() {
+ if (DEBUG) Log.d(TAG, "onPause()");
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.stop();
+ }
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback);
+ if (mWatchingRotation) {
+ try {
+ mWindowManager.removeRotationWatcher(mRotationWatcher);
+ mWatchingRotation = false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception when removing rotation watcher");
+ }
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ if (DEBUG) Log.d(TAG, "onResume()");
+ mIsShowing = KeyguardUpdateMonitor.getInstance(mContext).isKeyguardVisible();
+ if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
+ maybeStartBiometricUnlock();
+ }
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
+
+ // Registers a callback which handles stopping the biometric unlock and restarting it in
+ // the new position for a 180 degree rotation change.
+ if (!mWatchingRotation) {
+ try {
+ mLastRotation = mWindowManager.watchRotation(mRotationWatcher);
+ mWatchingRotation = true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception when adding rotation watcher");
+ }
+ }
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mKeyguardSecurityCallback;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mBiometricUnlock.initializeView(mFaceUnlockAreaView);
+ }
+
+ private void initializeBiometricUnlockView() {
+ if (DEBUG) Log.d(TAG, "initializeBiometricUnlockView()");
+ mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view);
+ if (mFaceUnlockAreaView != null) {
+ mBiometricUnlock = new FaceUnlock(mContext);
+
+ mCancelButton = (ImageButton) findViewById(R.id.face_unlock_cancel_button);
+ mCancelButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mBiometricUnlock.stopAndShowBackup();
+ }
+ });
+ } else {
+ Log.w(TAG, "Couldn't find biometric unlock view");
+ }
+ }
+
+ /**
+ * Starts the biometric unlock if it should be started based on a number of factors. If it
+ * should not be started, it either goes to the back up, or remains showing to prepare for
+ * it being started later.
+ */
+ private void maybeStartBiometricUnlock() {
+ if (DEBUG) Log.d(TAG, "maybeStartBiometricUnlock()");
+ if (mBiometricUnlock != null) {
+ KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final boolean backupIsTimedOut = (
+ monitor.getFailedUnlockAttempts() >=
+ LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(
+ Context.POWER_SERVICE);
+
+ boolean isShowing;
+ synchronized(mIsShowingLock) {
+ isShowing = mIsShowing;
+ }
+
+ // Don't start it if the screen is off or if it's not showing, but keep this view up
+ // because we want it here and ready for when the screen turns on or when it does start
+ // showing.
+ if (!powerManager.isScreenOn() || !isShowing) {
+ mBiometricUnlock.stop(); // It shouldn't be running but calling this can't hurt.
+ return;
+ }
+
+ // Although these same conditions are handled in KeyguardSecurityModel, they are still
+ // necessary here. When a tablet is rotated 90 degrees, a configuration change is
+ // triggered and everything is torn down and reconstructed. That means
+ // KeyguardSecurityModel gets a chance to take care of the logic and doesn't even
+ // reconstruct KeyguardFaceUnlockView if the biometric unlock should be suppressed.
+ // However, for a 180 degree rotation, no configuration change is triggered, so only
+ // the logic here is capable of suppressing Face Unlock.
+ if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
+ && monitor.isAlternateUnlockEnabled()
+ && !monitor.getMaxBiometricUnlockAttemptsReached()
+ && !backupIsTimedOut) {
+ mBiometricUnlock.start();
+ } else {
+ mBiometricUnlock.stopAndShowBackup();
+ }
+ }
+ }
+
+ KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
+ // We need to stop the biometric unlock when a phone call comes in
+ @Override
+ public void onPhoneStateChanged(int phoneState) {
+ if (DEBUG) Log.d(TAG, "onPhoneStateChanged(" + phoneState + ")");
+ if (phoneState == TelephonyManager.CALL_STATE_RINGING) {
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.stopAndShowBackup();
+ }
+ }
+ }
+
+ @Override
+ public void onUserSwitching(int userId) {
+ if (DEBUG) Log.d(TAG, "onUserSwitched(" + userId + ")");
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.stop();
+ }
+ // No longer required; static value set by KeyguardViewMediator
+ // mLockPatternUtils.setCurrentUser(userId);
+ }
+
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ if (DEBUG) Log.d(TAG, "onUserSwitchComplete(" + userId + ")");
+ if (mBiometricUnlock != null) {
+ maybeStartBiometricUnlock();
+ }
+ }
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
+ boolean wasShowing = false;
+ synchronized(mIsShowingLock) {
+ wasShowing = mIsShowing;
+ mIsShowing = showing;
+ }
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(
+ Context.POWER_SERVICE);
+ if (mBiometricUnlock != null) {
+ if (!showing && wasShowing) {
+ mBiometricUnlock.stop();
+ } else if (showing && powerManager.isScreenOn() && !wasShowing) {
+ maybeStartBiometricUnlock();
+ }
+ }
+ }
+ };
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
+ @Override
+ public void showBouncer(int duration) {
+ KeyguardSecurityViewHelper.
+ showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
+ }
+
+ @Override
+ public void hideBouncer(int duration) {
+ KeyguardSecurityViewHelper.
+ hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
+ }
+
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardGlowStripView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardGlowStripView.java
new file mode 100644
index 0000000..98a44a6
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardGlowStripView.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.LinearLayout;
+
+/**
+ * A layout which animates a strip of horizontal, pulsing dots on request. This is used
+ * to indicate the presence of pages to the left / right.
+ */
+public class KeyguardGlowStripView extends LinearLayout {
+ private static final int DURATION = 500;
+
+ private static final float SLIDING_WINDOW_SIZE = 0.4f;
+ private int mDotStripTop;
+ private int mHorizontalDotGap;
+
+ private int mDotSize;
+ private int mNumDots;
+ private Drawable mDotDrawable;
+ private boolean mLeftToRight = true;
+
+ private float mAnimationProgress = 0f;
+ private boolean mDrawDots = false;
+ private ValueAnimator mAnimator;
+ private Interpolator mDotAlphaInterpolator = new DecelerateInterpolator(0.5f);
+
+ public KeyguardGlowStripView(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardGlowStripView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardGlowStripView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyguardGlowStripView);
+ mDotSize = a.getDimensionPixelSize(R.styleable.KeyguardGlowStripView_dotSize, mDotSize);
+ mNumDots = a.getInt(R.styleable.KeyguardGlowStripView_numDots, mNumDots);
+ mDotDrawable = a.getDrawable(R.styleable.KeyguardGlowStripView_glowDot);
+ mLeftToRight = a.getBoolean(R.styleable.KeyguardGlowStripView_leftToRight, mLeftToRight);
+ }
+
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ int availableWidth = w - getPaddingLeft() - getPaddingRight();
+ mHorizontalDotGap = (availableWidth - mDotSize * mNumDots) / (mNumDots - 1);
+ mDotStripTop = getPaddingTop();
+ invalidate();
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+
+ if (!mDrawDots) return;
+
+ int xOffset = getPaddingLeft();
+ mDotDrawable.setBounds(0, 0, mDotSize, mDotSize);
+
+ for (int i = 0; i < mNumDots; i++) {
+ // We fudge the relative position to provide a fade in of the first dot and a fade
+ // out of the final dot.
+ float relativeDotPosition = SLIDING_WINDOW_SIZE / 2 + ((1.0f * i) / (mNumDots - 1)) *
+ (1 - SLIDING_WINDOW_SIZE);
+ float distance = Math.abs(relativeDotPosition - mAnimationProgress);
+ float alpha = Math.max(0, 1 - distance / (SLIDING_WINDOW_SIZE / 2));
+
+ alpha = mDotAlphaInterpolator.getInterpolation(alpha);
+
+ canvas.save();
+ canvas.translate(xOffset, mDotStripTop);
+ mDotDrawable.setAlpha((int) (alpha * 255));
+ mDotDrawable.draw(canvas);
+ canvas.restore();
+ xOffset += mDotSize + mHorizontalDotGap;
+ }
+ }
+
+ public void makeEmGo() {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ float from = mLeftToRight ? 0f : 1f;
+ float to = mLeftToRight ? 1f : 0f;
+ mAnimator = ValueAnimator.ofFloat(from, to);
+ mAnimator.setDuration(DURATION);
+ mAnimator.setInterpolator(new LinearInterpolator());
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDrawDots = false;
+ // make sure we draw one frame at the end with everything gone.
+ invalidate();
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mDrawDots = true;
+ }
+ });
+ mAnimator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mAnimationProgress = (Float) animation.getAnimatedValue();
+ invalidate();
+ }
+ });
+ mAnimator.start();
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
new file mode 100644
index 0000000..2904f4c
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -0,0 +1,1638 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AlertDialog;
+import android.app.SearchManager;
+import android.app.admin.DevicePolicyManager;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.media.RemoteControlClient;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
+import android.widget.RemoteViews.OnClickHandler;
+
+import java.io.File;
+import java.util.List;
+
+public class KeyguardHostView extends KeyguardViewBase {
+ private static final String TAG = "KeyguardHostView";
+ // Transport control states.
+ static final int TRANSPORT_GONE = 0;
+ static final int TRANSPORT_INVISIBLE = 1;
+ static final int TRANSPORT_VISIBLE = 2;
+
+ private int mTransportState = TRANSPORT_GONE;
+
+ // Use this to debug all of keyguard
+ public static boolean DEBUG = KeyguardViewMediator.DEBUG;
+ public static boolean DEBUGXPORT = true; // debug music transport control
+
+ // Found in KeyguardAppWidgetPickActivity.java
+ static final int APPWIDGET_HOST_ID = 0x4B455947;
+
+ private final int MAX_WIDGETS = 5;
+
+ private AppWidgetHost mAppWidgetHost;
+ private AppWidgetManager mAppWidgetManager;
+ private KeyguardWidgetPager mAppWidgetContainer;
+ private KeyguardSecurityViewFlipper mSecurityViewContainer;
+ private KeyguardSelectorView mKeyguardSelectorView;
+ private KeyguardTransportControlView mTransportControl;
+ private boolean mIsVerifyUnlockOnly;
+ private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
+ private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
+ private int mAppWidgetToShow;
+
+ private boolean mCheckAppWidgetConsistencyOnBootCompleted = false;
+ private boolean mCleanupAppWidgetsOnBootCompleted = false;
+
+ protected OnDismissAction mDismissAction;
+
+ protected int mFailedAttempts;
+ private LockPatternUtils mLockPatternUtils;
+
+ private KeyguardSecurityModel mSecurityModel;
+ private KeyguardViewStateManager mViewStateManager;
+
+ private Rect mTempRect = new Rect();
+
+ private int mDisabledFeatures;
+
+ private boolean mCameraDisabled;
+
+ private boolean mSafeModeEnabled;
+
+ private boolean mUserSetupCompleted;
+
+ // User for whom this host view was created. Final because we should never change the
+ // id without reconstructing an instance of KeyguardHostView. See note below...
+ private final int mUserId;
+
+ private KeyguardMultiUserSelectorView mKeyguardMultiUserSelectorView;
+
+ protected int mClientGeneration;
+
+ protected boolean mShowSecurityWhenReturn;
+
+ /*package*/ interface UserSwitcherCallback {
+ void hideSecurityView(int duration);
+ void showSecurityView();
+ void showUnlockHint();
+ void userActivity();
+ }
+
+ /*package*/ interface OnDismissAction {
+ /* returns true if the dismiss should be deferred */
+ boolean onDismiss();
+ }
+
+ public KeyguardHostView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardHostView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ if (DEBUG) Log.e(TAG, "KeyguardHostView()");
+
+ mLockPatternUtils = new LockPatternUtils(context);
+
+ // Note: This depends on KeyguardHostView getting reconstructed every time the
+ // user switches, since mUserId will be used for the entire session.
+ // Once created, keyguard should *never* re-use this instance with another user.
+ // In other words, mUserId should never change - hence it's marked final.
+ mUserId = mLockPatternUtils.getCurrentUser();
+
+ DevicePolicyManager dpm =
+ (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null) {
+ mDisabledFeatures = getDisabledFeatures(dpm);
+ mCameraDisabled = dpm.getCameraDisabled(null);
+ }
+
+ mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
+
+ // These need to be created with the user context...
+ Context userContext = null;
+ try {
+ final String packageName = "system";
+ userContext = mContext.createPackageContextAsUser(packageName, 0,
+ new UserHandle(mUserId));
+
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ // This should never happen, but it's better to have no widgets than to crash.
+ userContext = context;
+ }
+
+ mAppWidgetHost = new AppWidgetHost(userContext, APPWIDGET_HOST_ID, mOnClickHandler,
+ Looper.myLooper());
+
+ cleanupAppWidgetIds();
+
+ mAppWidgetManager = AppWidgetManager.getInstance(userContext);
+
+ mSecurityModel = new KeyguardSecurityModel(context);
+
+ mViewStateManager = new KeyguardViewStateManager(this);
+
+ mUserSetupCompleted = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+
+ // Ensure we have the current state *before* we call showAppropriateWidgetPage()
+ getInitialTransportState();
+
+ if (mSafeModeEnabled) {
+ Log.v(TAG, "Keyguard widgets disabled by safe mode");
+ }
+ if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
+ Log.v(TAG, "Keyguard widgets disabled by DPM");
+ }
+ if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) {
+ Log.v(TAG, "Keyguard secure camera disabled by DPM");
+ }
+ }
+
+ private void getInitialTransportState() {
+ DisplayClientState dcs = KeyguardUpdateMonitor.getInstance(mContext)
+ .getCachedDisplayClientState();
+ mTransportState = (dcs.clearing ? TRANSPORT_GONE :
+ (isMusicPlaying(dcs.playbackState) ? TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
+
+ if (DEBUG) Log.v(TAG, "Initial transport state: "
+ + mTransportState + ", pbstate=" + dcs.playbackState);
+ }
+
+ private void cleanupAppWidgetIds() {
+ // Since this method may delete a widget (which we can't do until boot completed) we
+ // may have to defer it until after boot complete.
+ if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
+ mCleanupAppWidgetsOnBootCompleted = true;
+ return;
+ }
+ if (!mSafeModeEnabled && !widgetsDisabledByDpm()) {
+ // Clean up appWidgetIds that are bound to lockscreen, but not actually used
+ // This is only to clean up after another bug: we used to not call
+ // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code
+ // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks
+ // that are triggered by deleteAppWidgetId, which is why we're doing this
+ int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets();
+ int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds();
+ for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) {
+ int appWidgetId = appWidgetIdsBoundToHost[i];
+ if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) {
+ Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id "
+ + appWidgetId);
+ mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ }
+ }
+ }
+ }
+
+ private static boolean contains(int[] array, int target) {
+ for (int value : array) {
+ if (value == target) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onBootCompleted() {
+ if (mCheckAppWidgetConsistencyOnBootCompleted) {
+ checkAppWidgetConsistency();
+ mSwitchPageRunnable.run();
+ mCheckAppWidgetConsistencyOnBootCompleted = false;
+ }
+ if (mCleanupAppWidgetsOnBootCompleted) {
+ cleanupAppWidgetIds();
+ mCleanupAppWidgetsOnBootCompleted = false;
+ }
+ }
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ if (mKeyguardMultiUserSelectorView != null) {
+ mKeyguardMultiUserSelectorView.finalizeActiveUserView(true);
+ }
+ }
+ @Override
+ void onMusicClientIdChanged(
+ int clientGeneration, boolean clearing, android.app.PendingIntent intent) {
+ // Set transport state to invisible until we know music is playing (below)
+ if (DEBUGXPORT && (mClientGeneration != clientGeneration || clearing)) {
+ Log.v(TAG, (clearing ? "hide" : "show") + " transport, gen:" + clientGeneration);
+ }
+ mClientGeneration = clientGeneration;
+ final int newState = (clearing ? TRANSPORT_GONE
+ : (mTransportState == TRANSPORT_VISIBLE ?
+ TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
+ if (newState != mTransportState) {
+ mTransportState = newState;
+ if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
+ KeyguardHostView.this.post(mSwitchPageRunnable);
+ }
+ }
+ @Override
+ public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
+ if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
+ if (mTransportState != TRANSPORT_GONE) {
+ final int newState = (isMusicPlaying(playbackState) ?
+ TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
+ if (newState != mTransportState) {
+ mTransportState = newState;
+ if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
+ KeyguardHostView.this.post(mSwitchPageRunnable);
+ }
+ }
+ }
+ };
+
+ private static final boolean isMusicPlaying(int playbackState) {
+ // This should agree with the list in AudioService.isPlaystateActive()
+ switch (playbackState) {
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+ case RemoteControlClient.PLAYSTATE_REWINDING:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private SlidingChallengeLayout mSlidingChallengeLayout;
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ boolean result = super.onTouchEvent(ev);
+ mTempRect.set(0, 0, 0, 0);
+ offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
+ ev.offsetLocation(mTempRect.left, mTempRect.top);
+ result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
+ ev.offsetLocation(-mTempRect.left, -mTempRect.top);
+ return result;
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.keyguardDoneDrawing();
+ }
+ }
+
+ private int getWidgetPosition(int id) {
+ final KeyguardWidgetPager appWidgetContainer = mAppWidgetContainer;
+ final int children = appWidgetContainer.getChildCount();
+ for (int i = 0; i < children; i++) {
+ final View content = appWidgetContainer.getWidgetPageAt(i).getContent();
+ if (content != null && content.getId() == id) {
+ return i;
+ } else if (content == null) {
+ // Attempt to track down bug #8886916
+ Log.w(TAG, "*** Null content at " + "i=" + i + ",id=" + id + ",N=" + children);
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ // Grab instances of and make any necessary changes to the main layouts. Create
+ // view state manager and wire up necessary listeners / callbacks.
+ View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target);
+ mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
+ mAppWidgetContainer.setVisibility(VISIBLE);
+ mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
+ mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget);
+ mAppWidgetContainer.setMinScale(0.5f);
+
+ mSlidingChallengeLayout = (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+ if (mSlidingChallengeLayout != null) {
+ mSlidingChallengeLayout.setOnChallengeScrolledListener(mViewStateManager);
+ }
+ mAppWidgetContainer.setViewStateManager(mViewStateManager);
+ mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
+
+ ChallengeLayout challenge = mSlidingChallengeLayout != null ? mSlidingChallengeLayout :
+ (ChallengeLayout) findViewById(R.id.multi_pane_challenge);
+ challenge.setOnBouncerStateChangedListener(mViewStateManager);
+ mAppWidgetContainer.setBouncerAnimationDuration(challenge.getBouncerAnimationDuration());
+ mViewStateManager.setPagedView(mAppWidgetContainer);
+ mViewStateManager.setChallengeLayout(challenge);
+ mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
+ mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
+ mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
+
+ setBackButtonEnabled(false);
+
+ addDefaultWidgets();
+
+ addWidgetsFromSettings();
+ if (!shouldEnableAddWidget()) {
+ mAppWidgetContainer.setAddWidgetEnabled(false);
+ }
+ checkAppWidgetConsistency();
+ mSwitchPageRunnable.run();
+ // This needs to be called after the pages are all added.
+ mViewStateManager.showUsabilityHints();
+
+ showPrimarySecurityScreen(false);
+ updateSecurityViews();
+ }
+
+ private void setBackButtonEnabled(boolean enabled) {
+ if (mContext instanceof Activity) return; // always enabled in activity mode
+ setSystemUiVisibility(enabled ?
+ getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_BACK :
+ getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
+ }
+
+ private boolean shouldEnableAddWidget() {
+ return numWidgets() < MAX_WIDGETS && mUserSetupCompleted;
+ }
+
+ private int getDisabledFeatures(DevicePolicyManager dpm) {
+ int disabledFeatures = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
+ if (dpm != null) {
+ final int currentUser = mLockPatternUtils.getCurrentUser();
+ disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
+ }
+ return disabledFeatures;
+ }
+
+ private boolean widgetsDisabledByDpm() {
+ return (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
+ }
+
+ private boolean cameraDisabledByDpm() {
+ return mCameraDisabled
+ || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
+ }
+
+ private void updateSecurityViews() {
+ int children = mSecurityViewContainer.getChildCount();
+ for (int i = 0; i < children; i++) {
+ updateSecurityView(mSecurityViewContainer.getChildAt(i));
+ }
+ }
+
+ private void updateSecurityView(View view) {
+ if (view instanceof KeyguardSecurityView) {
+ KeyguardSecurityView ksv = (KeyguardSecurityView) view;
+ ksv.setKeyguardCallback(mCallback);
+ ksv.setLockPatternUtils(mLockPatternUtils);
+ if (mViewStateManager.isBouncing()) {
+ ksv.showBouncer(0);
+ } else {
+ ksv.hideBouncer(0);
+ }
+ } else {
+ Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
+ }
+ }
+
+ void setLockPatternUtils(LockPatternUtils utils) {
+ mSecurityModel.setLockPatternUtils(utils);
+ mLockPatternUtils = utils;
+ updateSecurityViews();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mAppWidgetHost.startListening();
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mAppWidgetHost.stopListening();
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
+ }
+
+ void addWidget(AppWidgetHostView view, int pageIndex) {
+ mAppWidgetContainer.addWidget(view, pageIndex);
+ }
+
+ private KeyguardWidgetPager.Callbacks mWidgetCallbacks
+ = new KeyguardWidgetPager.Callbacks() {
+ @Override
+ public void userActivity() {
+ KeyguardHostView.this.userActivity();
+ }
+
+ @Override
+ public void onUserActivityTimeoutChanged() {
+ KeyguardHostView.this.onUserActivityTimeoutChanged();
+ }
+
+ @Override
+ public void onAddView(View v) {
+ if (!shouldEnableAddWidget()) {
+ mAppWidgetContainer.setAddWidgetEnabled(false);
+ }
+ }
+
+ @Override
+ public void onRemoveView(View v, boolean deletePermanently) {
+ if (deletePermanently) {
+ final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+ if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID &&
+ appWidgetId != LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
+ mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ }
+ }
+ }
+
+ @Override
+ public void onRemoveViewAnimationCompleted() {
+ if (shouldEnableAddWidget()) {
+ mAppWidgetContainer.setAddWidgetEnabled(true);
+ }
+ }
+ };
+
+ public void initializeSwitchingUserState(boolean switching) {
+ if (!switching && mKeyguardMultiUserSelectorView != null) {
+ mKeyguardMultiUserSelectorView.finalizeActiveUserView(false);
+ }
+ }
+
+ public void userActivity() {
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.userActivity();
+ }
+ }
+
+ public void onUserActivityTimeoutChanged() {
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.onUserActivityTimeoutChanged();
+ }
+ }
+
+ @Override
+ public long getUserActivityTimeout() {
+ // Currently only considering user activity timeouts needed by widgets.
+ // Could also take into account longer timeouts for certain security views.
+ if (mAppWidgetContainer != null) {
+ return mAppWidgetContainer.getUserActivityTimeout();
+ }
+ return -1;
+ }
+
+ private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
+
+ public void userActivity(long timeout) {
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.userActivity(timeout);
+ }
+ }
+
+ public void dismiss(boolean authenticated) {
+ showNextSecurityScreenOrFinish(authenticated);
+ }
+
+ public boolean isVerifyUnlockOnly() {
+ return mIsVerifyUnlockOnly;
+ }
+
+ public void reportSuccessfulUnlockAttempt() {
+ KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
+ mLockPatternUtils.reportSuccessfulPasswordAttempt();
+ }
+
+ public void reportFailedUnlockAttempt() {
+ if (mCurrentSecuritySelection == SecurityMode.Biometric) {
+ KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt();
+ } else {
+ KeyguardHostView.this.reportFailedUnlockAttempt();
+ }
+ }
+
+ public int getFailedAttempts() {
+ return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
+ }
+
+ @Override
+ public void showBackupSecurity() {
+ KeyguardHostView.this.showBackupSecurityScreen();
+ }
+
+ @Override
+ public void setOnDismissAction(OnDismissAction action) {
+ KeyguardHostView.this.setOnDismissAction(action);
+ }
+
+ };
+
+ private void showDialog(String title, String message) {
+ final AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(title)
+ .setMessage(message)
+ .setNeutralButton(R.string.ok, null)
+ .create();
+ if (!(mContext instanceof Activity)) {
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ dialog.show();
+ }
+
+ private void showTimeoutDialog() {
+ int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ int messageId = 0;
+
+ switch (mSecurityModel.getSecurityMode()) {
+ case Pattern:
+ messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
+ break;
+ case PIN:
+ messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
+ break;
+ case Password:
+ messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
+ break;
+ }
+
+ if (messageId != 0) {
+ final String message = mContext.getString(messageId,
+ KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
+ timeoutInSeconds);
+ showDialog(null, message);
+ }
+ }
+
+ private void showAlmostAtWipeDialog(int attempts, int remaining) {
+ int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
+ attempts, remaining);
+ showDialog(null, message);
+ }
+
+ private void showWipeDialog(int attempts) {
+ String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
+ showDialog(null, message);
+ }
+
+ private void showAlmostAtAccountLoginDialog() {
+ final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
+ - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
+ String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
+ count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
+ showDialog(null, message);
+ }
+
+ private void reportFailedUnlockAttempt() {
+ final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
+
+ if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
+
+ SecurityMode mode = mSecurityModel.getSecurityMode();
+ final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
+
+ final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
+ .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
+
+ final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
+ - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
+
+ final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
+ (failedAttemptsBeforeWipe - failedAttempts)
+ : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
+
+ boolean showTimeout = false;
+ if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+ // If we reach this code, it means the user has installed a DevicePolicyManager
+ // that requests device wipe after N attempts. Once we get below the grace
+ // period, we'll post this dialog every time as a clear warning until the
+ // bombshell hits and the device is wiped.
+ if (remainingBeforeWipe > 0) {
+ showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
+ } else {
+ // Too many attempts. The device will be wiped shortly.
+ Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
+ showWipeDialog(failedAttempts);
+ }
+ } else {
+ showTimeout =
+ (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
+ if (usingPattern && mEnableFallback) {
+ if (failedAttempts == failedAttemptWarning) {
+ showAlmostAtAccountLoginDialog();
+ showTimeout = false; // don't show both dialogs
+ } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
+ mLockPatternUtils.setPermanentlyLocked(true);
+ showSecurityScreen(SecurityMode.Account);
+ // don't show timeout dialog because we show account unlock screen next
+ showTimeout = false;
+ }
+ }
+ }
+ monitor.reportFailedUnlockAttempt();
+ mLockPatternUtils.reportFailedPasswordAttempt();
+ if (showTimeout) {
+ showTimeoutDialog();
+ }
+ }
+
+ /**
+ * Shows the primary security screen for the user. This will be either the multi-selector
+ * or the user's security method.
+ * @param turningOff true if the device is being turned off
+ */
+ void showPrimarySecurityScreen(boolean turningOff) {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
+ if (!turningOff &&
+ KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
+ // If we're not turning off, then allow biometric alternate.
+ // We'll reload it when the device comes back on.
+ securityMode = mSecurityModel.getAlternateFor(securityMode);
+ }
+ showSecurityScreen(securityMode);
+ }
+
+ /**
+ * Shows the backup security screen for the current security mode. This could be used for
+ * password recovery screens but is currently only used for pattern unlock to show the
+ * account unlock screen and biometric unlock to show the user's normal unlock.
+ */
+ private void showBackupSecurityScreen() {
+ if (DEBUG) Log.d(TAG, "showBackupSecurity()");
+ SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
+ showSecurityScreen(backup);
+ }
+
+ public boolean showNextSecurityScreenIfPresent() {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ // Allow an alternate, such as biometric unlock
+ securityMode = mSecurityModel.getAlternateFor(securityMode);
+ if (SecurityMode.None == securityMode) {
+ return false;
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ return true;
+ }
+ }
+
+ private void showNextSecurityScreenOrFinish(boolean authenticated) {
+ if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
+ boolean finish = false;
+ if (SecurityMode.None == mCurrentSecuritySelection) {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ // Allow an alternate, such as biometric unlock
+ securityMode = mSecurityModel.getAlternateFor(securityMode);
+ if (SecurityMode.None == securityMode) {
+ finish = true; // no security required
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ }
+ } else if (authenticated) {
+ switch (mCurrentSecuritySelection) {
+ case Pattern:
+ case Password:
+ case PIN:
+ case Account:
+ case Biometric:
+ finish = true;
+ break;
+
+ case SimPin:
+ case SimPuk:
+ // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ if (securityMode != SecurityMode.None) {
+ showSecurityScreen(securityMode);
+ } else {
+ finish = true;
+ }
+ break;
+
+ default:
+ Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
+ showPrimarySecurityScreen(false);
+ break;
+ }
+ } else {
+ showPrimarySecurityScreen(false);
+ }
+ if (finish) {
+ // If the alternate unlock was suppressed, it can now be safely
+ // enabled because the user has left keyguard.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
+
+ // If there's a pending runnable because the user interacted with a widget
+ // and we're leaving keyguard, then run it.
+ boolean deferKeyguardDone = false;
+ if (mDismissAction != null) {
+ deferKeyguardDone = mDismissAction.onDismiss();
+ mDismissAction = null;
+ }
+ if (mViewMediatorCallback != null) {
+ if (deferKeyguardDone) {
+ mViewMediatorCallback.keyguardDonePending();
+ } else {
+ mViewMediatorCallback.keyguardDone(true);
+ }
+ }
+ } else {
+ mViewStateManager.showBouncer(true);
+ }
+ }
+
+ private OnClickHandler mOnClickHandler = new OnClickHandler() {
+ @Override
+ public boolean onClickHandler(final View view,
+ final android.app.PendingIntent pendingIntent,
+ final Intent fillInIntent) {
+ if (pendingIntent.isActivity()) {
+ setOnDismissAction(new OnDismissAction() {
+ public boolean onDismiss() {
+ try {
+ // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
+ Context context = view.getContext();
+ ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
+ 0, 0,
+ view.getMeasuredWidth(), view.getMeasuredHeight());
+ context.startIntentSender(
+ pendingIntent.getIntentSender(), fillInIntent,
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
+ } catch (IntentSender.SendIntentException e) {
+ android.util.Log.e(TAG, "Cannot send pending intent: ", e);
+ } catch (Exception e) {
+ android.util.Log.e(TAG, "Cannot send pending intent due to " +
+ "unknown exception: ", e);
+ }
+ return false;
+ }
+ });
+
+ if (mViewStateManager.isChallengeShowing()) {
+ mViewStateManager.showBouncer(true);
+ } else {
+ mCallback.dismiss(false);
+ }
+ return true;
+ } else {
+ return super.onClickHandler(view, pendingIntent, fillInIntent);
+ }
+ };
+ };
+
+ // Used to ignore callbacks from methods that are no longer current (e.g. face unlock).
+ // This avoids unwanted asynchronous events from messing with the state.
+ private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
+
+ @Override
+ public void userActivity(long timeout) {
+ }
+
+ @Override
+ public void showBackupSecurity() {
+ }
+
+ @Override
+ public void setOnDismissAction(OnDismissAction action) {
+ }
+
+ @Override
+ public void reportSuccessfulUnlockAttempt() {
+ }
+
+ @Override
+ public void reportFailedUnlockAttempt() {
+ }
+
+ @Override
+ public boolean isVerifyUnlockOnly() {
+ return false;
+ }
+
+ @Override
+ public int getFailedAttempts() {
+ return 0;
+ }
+
+ @Override
+ public void dismiss(boolean securityVerified) {
+ }
+ };
+
+ /**
+ * Sets an action to perform when keyguard is dismissed.
+ * @param action
+ */
+ protected void setOnDismissAction(OnDismissAction action) {
+ mDismissAction = action;
+ }
+
+ private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
+ final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
+ KeyguardSecurityView view = null;
+ final int children = mSecurityViewContainer.getChildCount();
+ for (int child = 0; child < children; child++) {
+ if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
+ view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
+ break;
+ }
+ }
+ int layoutId = getLayoutIdFor(securityMode);
+ if (view == null && layoutId != 0) {
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
+ View v = inflater.inflate(layoutId, mSecurityViewContainer, false);
+ mSecurityViewContainer.addView(v);
+ updateSecurityView(v);
+ view = (KeyguardSecurityView)v;
+ }
+
+ if (view instanceof KeyguardSelectorView) {
+ KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
+ View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
+ selectorView.setCarrierArea(carrierText);
+ }
+
+ return view;
+ }
+
+ /**
+ * Switches to the given security view unless it's already being shown, in which case
+ * this is a no-op.
+ *
+ * @param securityMode
+ */
+ private void showSecurityScreen(SecurityMode securityMode) {
+ if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
+
+ if (securityMode == mCurrentSecuritySelection) return;
+
+ KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
+ KeyguardSecurityView newView = getSecurityView(securityMode);
+
+ // Enter full screen mode if we're in SIM or Account screen
+ boolean fullScreenEnabled = getResources().getBoolean(R.bool.kg_sim_puk_account_full_screen);
+ boolean isSimOrAccount = securityMode == SecurityMode.SimPin
+ || securityMode == SecurityMode.SimPuk
+ || securityMode == SecurityMode.Account;
+ mAppWidgetContainer.setVisibility(
+ isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
+
+ if (mSlidingChallengeLayout != null) {
+ mSlidingChallengeLayout.setChallengeInteractive(!fullScreenEnabled);
+ }
+
+ // Emulate Activity life cycle
+ if (oldView != null) {
+ oldView.onPause();
+ oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
+ }
+ newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
+ newView.setKeyguardCallback(mCallback);
+
+ final boolean needsInput = newView.needsInput();
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.setNeedsInput(needsInput);
+ }
+
+ // Find and show this child.
+ final int childCount = mSecurityViewContainer.getChildCount();
+
+ mSecurityViewContainer.setInAnimation(
+ AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_in));
+ mSecurityViewContainer.setOutAnimation(
+ AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_out));
+ final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
+ for (int i = 0; i < childCount; i++) {
+ if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
+ mSecurityViewContainer.setDisplayedChild(i);
+ break;
+ }
+ }
+
+ if (securityMode == SecurityMode.None) {
+ // Discard current runnable if we're switching back to the selector view
+ setOnDismissAction(null);
+ }
+ if (securityMode == SecurityMode.Account && !mLockPatternUtils.isPermanentlyLocked()) {
+ // we're showing account as a backup, provide a way to get back to primary
+ setBackButtonEnabled(true);
+ }
+ mCurrentSecuritySelection = securityMode;
+ }
+
+ @Override
+ public void onScreenTurnedOn() {
+ if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
+ showPrimarySecurityScreen(false);
+ getSecurityView(mCurrentSecuritySelection).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();
+
+ if (mViewStateManager != null) {
+ mViewStateManager.showUsabilityHints();
+ }
+ requestFocus();
+ }
+
+ @Override
+ public void onScreenTurnedOff() {
+ if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
+ Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
+ // Once the screen turns off, we no longer consider this to be first boot and we want the
+ // biometric unlock to start next time keyguard is shown.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
+ // We use mAppWidgetToShow to show a particular widget after you add it-- once the screen
+ // turns off we reset that behavior
+ clearAppWidgetToShow();
+ checkAppWidgetConsistency();
+ showPrimarySecurityScreen(true);
+ getSecurityView(mCurrentSecuritySelection).onPause();
+ CameraWidgetFrame cameraPage = findCameraPage();
+ if (cameraPage != null) {
+ cameraPage.onScreenTurnedOff();
+ }
+ clearFocus();
+ }
+
+ public void clearAppWidgetToShow() {
+ mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+
+ @Override
+ public void show() {
+ if (DEBUG) Log.d(TAG, "show()");
+ showPrimarySecurityScreen(false);
+ }
+
+ @Override
+ public void verifyUnlock() {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.keyguardDone(true);
+ }
+ } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
+ && securityMode != KeyguardSecurityModel.SecurityMode.PIN
+ && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
+ // can only verify unlock when in pattern/password mode
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.keyguardDone(false);
+ }
+ } else {
+ // otherwise, go to the unlock screen, see if they can verify it
+ mIsVerifyUnlockOnly = true;
+ showSecurityScreen(securityMode);
+ }
+ }
+
+ private int getSecurityViewIdForMode(SecurityMode securityMode) {
+ switch (securityMode) {
+ case None: return R.id.keyguard_selector_view;
+ case Pattern: return R.id.keyguard_pattern_view;
+ case PIN: return R.id.keyguard_pin_view;
+ case Password: return R.id.keyguard_password_view;
+ case Biometric: return R.id.keyguard_face_unlock_view;
+ case Account: return R.id.keyguard_account_view;
+ case SimPin: return R.id.keyguard_sim_pin_view;
+ case SimPuk: return R.id.keyguard_sim_puk_view;
+ }
+ return 0;
+ }
+
+ private int getLayoutIdFor(SecurityMode securityMode) {
+ switch (securityMode) {
+ case None: return R.layout.keyguard_selector_view;
+ case Pattern: return R.layout.keyguard_pattern_view;
+ case PIN: return R.layout.keyguard_pin_view;
+ case Password: return R.layout.keyguard_password_view;
+ case Biometric: return R.layout.keyguard_face_unlock_view;
+ case Account: return R.layout.keyguard_account_view;
+ case SimPin: return R.layout.keyguard_sim_pin_view;
+ case SimPuk: return R.layout.keyguard_sim_puk_view;
+ default:
+ return 0;
+ }
+ }
+
+ private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) {
+ AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId);
+ if (appWidgetInfo != null) {
+ AppWidgetHostView view = mAppWidgetHost.createView(mContext, appId, appWidgetInfo);
+ addWidget(view, pageIndex);
+ return true;
+ } else {
+ if (updateDbIfFailed) {
+ Log.w(TAG, "*** AppWidgetInfo for app widget id " + appId + " was null for user"
+ + mUserId + ", deleting");
+ mAppWidgetHost.deleteAppWidgetId(appId);
+ mLockPatternUtils.removeAppWidget(appId);
+ }
+ return false;
+ }
+ }
+
+ private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
+ new CameraWidgetFrame.Callbacks() {
+ @Override
+ public void onLaunchingCamera() {
+ setSliderHandleAlpha(0);
+ }
+
+ @Override
+ public void onCameraLaunchedSuccessfully() {
+ if (mAppWidgetContainer.isCameraPage(mAppWidgetContainer.getCurrentPage())) {
+ mAppWidgetContainer.scrollLeft();
+ }
+ setSliderHandleAlpha(1);
+ mShowSecurityWhenReturn = true;
+ }
+
+ @Override
+ public void onCameraLaunchedUnsuccessfully() {
+ setSliderHandleAlpha(1);
+ }
+
+ private void setSliderHandleAlpha(float alpha) {
+ SlidingChallengeLayout slider =
+ (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+ if (slider != null) {
+ slider.setHandleAlpha(alpha);
+ }
+ }
+ };
+
+ private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
+ @Override
+ Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ LockPatternUtils getLockPatternUtils() {
+ return mLockPatternUtils;
+ }
+ };
+
+ private int numWidgets() {
+ final int childCount = mAppWidgetContainer.getChildCount();
+ int widgetCount = 0;
+ for (int i = 0; i < childCount; i++) {
+ if (mAppWidgetContainer.isWidgetPage(i)) {
+ widgetCount++;
+ }
+ }
+ return widgetCount;
+ }
+
+ private void addDefaultWidgets() {
+ if (!mSafeModeEnabled && !widgetsDisabledByDpm()) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false);
+ mAppWidgetContainer.addWidget(addWidget, 0);
+ View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
+ addWidgetButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Pass in an invalid widget id... the picker will allocate an ID for us
+ mActivityLauncher.launchWidgetPicker(AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+ });
+ }
+
+ // We currently disable cameras in safe mode because we support loading 3rd party
+ // cameras we can't trust. TODO: plumb safe mode into camera creation code and only
+ // inflate system-provided camera?
+ if (!mSafeModeEnabled && !cameraDisabledByDpm() && mUserSetupCompleted
+ && mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
+ View cameraWidget =
+ CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
+ if (cameraWidget != null) {
+ mAppWidgetContainer.addWidget(cameraWidget);
+ }
+ }
+
+ enableUserSelectorIfNecessary();
+ }
+
+ /**
+ * Create KeyguardTransportControlView on demand.
+ * @return
+ */
+ private KeyguardTransportControlView getOrCreateTransportControl() {
+ if (mTransportControl == null) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mTransportControl = (KeyguardTransportControlView)
+ inflater.inflate(R.layout.keyguard_transport_control_view, this, false);
+ }
+ return mTransportControl;
+ }
+
+ private int getInsertPageIndex() {
+ View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
+ int insertionIndex = mAppWidgetContainer.indexOfChild(addWidget);
+ if (insertionIndex < 0) {
+ insertionIndex = 0; // no add widget page found
+ } else {
+ insertionIndex++; // place after add widget
+ }
+ return insertionIndex;
+ }
+
+ private void addDefaultStatusWidget(int index) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
+ mAppWidgetContainer.addWidget(statusWidget, index);
+ }
+
+ private void addWidgetsFromSettings() {
+ if (mSafeModeEnabled || widgetsDisabledByDpm()) {
+ return;
+ }
+
+ int insertionIndex = getInsertPageIndex();
+
+ // Add user-selected widget
+ final int[] widgets = mLockPatternUtils.getAppWidgets();
+
+ if (widgets == null) {
+ Log.d(TAG, "Problem reading widgets");
+ } else {
+ for (int i = widgets.length -1; i >= 0; i--) {
+ if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
+ addDefaultStatusWidget(insertionIndex);
+ } else {
+ // We add the widgets from left to right, starting after the first page after
+ // the add page. We count down, since the order will be persisted from right
+ // to left, starting after camera.
+ addWidget(widgets[i], insertionIndex, true);
+ }
+ }
+ }
+ }
+
+ private int allocateIdForDefaultAppWidget() {
+ int appWidgetId;
+ Resources res = getContext().getResources();
+ ComponentName defaultAppWidget = new ComponentName(
+ res.getString(R.string.widget_default_package_name),
+ res.getString(R.string.widget_default_class_name));
+
+ // Note: we don't support configuring the widget
+ appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+ try {
+ mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
+
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
+ mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+ return appWidgetId;
+ }
+ public void checkAppWidgetConsistency() {
+ // Since this method may bind a widget (which we can't do until boot completed) we
+ // may have to defer it until after boot complete.
+ if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
+ mCheckAppWidgetConsistencyOnBootCompleted = true;
+ return;
+ }
+ final int childCount = mAppWidgetContainer.getChildCount();
+ boolean widgetPageExists = false;
+ for (int i = 0; i < childCount; i++) {
+ if (mAppWidgetContainer.isWidgetPage(i)) {
+ widgetPageExists = true;
+ break;
+ }
+ }
+ if (!widgetPageExists) {
+ final int insertPageIndex = getInsertPageIndex();
+
+ final boolean userAddedWidgetsEnabled = !widgetsDisabledByDpm();
+ boolean addedDefaultAppWidget = false;
+
+ if (!mSafeModeEnabled) {
+ if (userAddedWidgetsEnabled) {
+ int appWidgetId = allocateIdForDefaultAppWidget();
+ if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+ addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, true);
+ }
+ } else {
+ // note: even if widgetsDisabledByDpm() returns true, we still bind/create
+ // the default appwidget if possible
+ int appWidgetId = mLockPatternUtils.getFallbackAppWidgetId();
+ if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ appWidgetId = allocateIdForDefaultAppWidget();
+ if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+ mLockPatternUtils.writeFallbackAppWidgetId(appWidgetId);
+ }
+ }
+ if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+ addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, false);
+ if (!addedDefaultAppWidget) {
+ mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ mLockPatternUtils.writeFallbackAppWidgetId(
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+ }
+ }
+ }
+
+ // Use the built-in status/clock view if we can't inflate the default widget
+ if (!addedDefaultAppWidget) {
+ addDefaultStatusWidget(insertPageIndex);
+ }
+
+ // trigger DB updates only if user-added widgets are enabled
+ if (!mSafeModeEnabled && userAddedWidgetsEnabled) {
+ mAppWidgetContainer.onAddView(
+ mAppWidgetContainer.getChildAt(insertPageIndex), insertPageIndex);
+ }
+ }
+ }
+
+ Runnable mSwitchPageRunnable = new Runnable() {
+ @Override
+ public void run() {
+ showAppropriateWidgetPage();
+ }
+ };
+
+ static class SavedState extends BaseSavedState {
+ int transportState;
+ int appWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ this.transportState = in.readInt();
+ this.appWidgetToShow = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(this.transportState);
+ out.writeInt(this.appWidgetToShow);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ if (DEBUG) Log.d(TAG, "onSaveInstanceState, tstate=" + mTransportState);
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ // If the transport is showing, force it to show it on restore.
+ final boolean showing = mTransportControl != null
+ && mAppWidgetContainer.getWidgetPageIndex(mTransportControl) >= 0;
+ ss.transportState = showing ? TRANSPORT_VISIBLE : mTransportState;
+ ss.appWidgetToShow = mAppWidgetToShow;
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (!(state instanceof SavedState)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mTransportState = (ss.transportState);
+ mAppWidgetToShow = ss.appWidgetToShow;
+ if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
+ post(mSwitchPageRunnable);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
+ if (hasWindowFocus && mShowSecurityWhenReturn) {
+ SlidingChallengeLayout slider =
+ (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+ if (slider != null) {
+ slider.setHandleAlpha(1);
+ slider.showChallenge(true);
+ }
+ mShowSecurityWhenReturn = false;
+ }
+ }
+
+ private void showAppropriateWidgetPage() {
+ int state = mTransportState;
+ ensureTransportPresentOrRemoved(state);
+ int pageToShow = getAppropriateWidgetPage(state);
+ mAppWidgetContainer.setCurrentPage(pageToShow);
+ }
+
+ /**
+ * Examines the current state and adds the transport to the widget pager when the state changes.
+ *
+ * Showing the initial transport and keeping it around is a bit tricky because the signals
+ * coming from music players aren't always clear. Here's how the states are handled:
+ *
+ * {@link TRANSPORT_GONE} means we have no reason to show the transport - remove it if present.
+ *
+ * {@link TRANSPORT_INVISIBLE} means we have potential to show the transport because a music
+ * player is registered but not currently playing music (or we don't know the state yet). The
+ * code adds it conditionally on play state.
+ *
+ * {@link #TRANSPORT_VISIBLE} means a music player is active and transport should be showing.
+ *
+ * Once the transport is showing, we always show it until keyguard is dismissed. This state is
+ * maintained by onSave/RestoreInstanceState(). This state is cleared in
+ * {@link KeyguardViewManager#hide} when keyguard is dismissed, which causes the transport to be
+ * gone when keyguard is restarted until we get an update with the current state.
+ *
+ * @param state
+ */
+ private void ensureTransportPresentOrRemoved(int state) {
+ final boolean showing = getWidgetPosition(R.id.keyguard_transport_control) != -1;
+ final boolean visible = state == TRANSPORT_VISIBLE;
+ final boolean shouldBeVisible = state == TRANSPORT_INVISIBLE && isMusicPlaying(state);
+ if (!showing && (visible || shouldBeVisible)) {
+ if (DEBUGXPORT) Log.v(TAG, "add transport");
+ // insert to left of camera if it exists, otherwise after right-most widget
+ int lastWidget = mAppWidgetContainer.getChildCount() - 1;
+ int position = 0; // handle no widget case
+ if (lastWidget >= 0) {
+ position = mAppWidgetContainer.isCameraPage(lastWidget) ?
+ lastWidget : lastWidget + 1;
+ }
+ mAppWidgetContainer.addWidget(getOrCreateTransportControl(), position);
+ } else if (showing && state == TRANSPORT_GONE) {
+ if (DEBUGXPORT) Log.v(TAG, "remove transport");
+ mAppWidgetContainer.removeWidget(getOrCreateTransportControl());
+ mTransportControl = null;
+ }
+ }
+
+ private CameraWidgetFrame findCameraPage() {
+ for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) {
+ if (mAppWidgetContainer.isCameraPage(i)) {
+ return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i);
+ }
+ }
+ return null;
+ }
+
+ boolean isMusicPage(int pageIndex) {
+ return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
+ }
+
+ private int getAppropriateWidgetPage(int musicTransportState) {
+ // assumes at least one widget (besides camera + add)
+ if (mAppWidgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
+ final int childCount = mAppWidgetContainer.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (mAppWidgetContainer.getWidgetPageAt(i).getContentAppWidgetId()
+ == mAppWidgetToShow) {
+ return i;
+ }
+ }
+ mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+ // if music playing, show transport
+ if (musicTransportState == TRANSPORT_VISIBLE) {
+ if (DEBUG) Log.d(TAG, "Music playing, show transport");
+ return mAppWidgetContainer.getWidgetPageIndex(getOrCreateTransportControl());
+ }
+
+ // else show the right-most widget (except for camera)
+ int rightMost = mAppWidgetContainer.getChildCount() - 1;
+ if (mAppWidgetContainer.isCameraPage(rightMost)) {
+ rightMost--;
+ }
+ if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
+ return rightMost;
+ }
+
+ private void enableUserSelectorIfNecessary() {
+ if (!UserManager.supportsMultipleUsers()) {
+ return; // device doesn't support multi-user mode
+ }
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ if (um == null) {
+ Throwable t = new Throwable();
+ t.fillInStackTrace();
+ Log.e(TAG, "user service is null.", t);
+ return;
+ }
+
+ // if there are multiple users, we need to enable to multi-user switcher
+ final List<UserInfo> users = um.getUsers(true);
+ if (users == null) {
+ Throwable t = new Throwable();
+ t.fillInStackTrace();
+ Log.e(TAG, "list of users is null.", t);
+ return;
+ }
+
+ final View multiUserView = findViewById(R.id.keyguard_user_selector);
+ if (multiUserView == null) {
+ Throwable t = new Throwable();
+ t.fillInStackTrace();
+ Log.e(TAG, "can't find user_selector in layout.", t);
+ return;
+ }
+
+ if (users.size() > 1) {
+ if (multiUserView instanceof KeyguardMultiUserSelectorView) {
+ mKeyguardMultiUserSelectorView = (KeyguardMultiUserSelectorView) multiUserView;
+ mKeyguardMultiUserSelectorView.setVisibility(View.VISIBLE);
+ mKeyguardMultiUserSelectorView.addUsers(users);
+ UserSwitcherCallback callback = new UserSwitcherCallback() {
+ @Override
+ public void hideSecurityView(int duration) {
+ mSecurityViewContainer.animate().alpha(0).setDuration(duration);
+ }
+
+ @Override
+ public void showSecurityView() {
+ mSecurityViewContainer.setAlpha(1.0f);
+ }
+
+ @Override
+ public void showUnlockHint() {
+ if (mKeyguardSelectorView != null) {
+ mKeyguardSelectorView.showUsabilityHint();
+ }
+ }
+
+ @Override
+ public void userActivity() {
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.userActivity();
+ }
+ }
+ };
+ mKeyguardMultiUserSelectorView.setCallback(callback);
+ } else {
+ Throwable t = new Throwable();
+ t.fillInStackTrace();
+ if (multiUserView == null) {
+ Log.e(TAG, "could not find the user_selector.", t);
+ } else {
+ Log.e(TAG, "user_selector is the wrong type.", t);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void cleanUp() {
+ // Make sure we let go of all widgets and their package contexts promptly. If we don't do
+ // this, and the associated application is uninstalled, it can cause a soft reboot.
+ int count = mAppWidgetContainer.getChildCount();
+ for (int i = 0; i < count; i++) {
+ KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i);
+ frame.removeAllViews();
+ }
+ }
+
+ /**
+ * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
+ * some cases where we wish to disable it, notably when the menu button placement or technology
+ * is prone to false positives.
+ *
+ * @return true if the menu key should be enabled
+ */
+ private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
+ private boolean shouldEnableMenuKey() {
+ final Resources res = getResources();
+ final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
+ final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
+ final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
+ return !configDisabled || isTestHarness || fileOverride;
+ }
+
+ public void goToWidget(int appWidgetId) {
+ mAppWidgetToShow = appWidgetId;
+ mSwitchPageRunnable.run();
+ }
+
+ public boolean handleMenuKey() {
+ // The following enables the MENU key to work for testing automation
+ if (shouldEnableMenuKey()) {
+ showNextSecurityScreenOrFinish(false);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean handleBackKey() {
+ if (mCurrentSecuritySelection == SecurityMode.Account) {
+ // go back to primary screen and re-disable back
+ setBackButtonEnabled(false);
+ showPrimarySecurityScreen(false /*turningOff*/);
+ return true;
+ }
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ mCallback.dismiss(false);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Dismisses the keyguard by going to the next screen or making it gone.
+ */
+ public void dismiss() {
+ showNextSecurityScreenOrFinish(false);
+ }
+
+ public void showAssistant() {
+ final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
+
+ if (intent == null) return;
+
+ final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit,
+ getHandler(), null);
+
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ mActivityLauncher.launchActivityWithAnimation(
+ intent, false, opts.toBundle(), null, null);
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardLinearLayout.java b/packages/Keyguard/src/com/android/keyguard/KeyguardLinearLayout.java
new file mode 100644
index 0000000..343fdcb
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardLinearLayout.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * A layout that arranges its children into a special type of grid.
+ */
+public class KeyguardLinearLayout extends LinearLayout {
+ int mTopChild = 0;
+
+ public KeyguardLinearLayout(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardLinearLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setTopChild(View child) {
+ int top = indexOfChild(child);
+ mTopChild = top;
+ invalidate();
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
new file mode 100644
index 0000000..ad59c02
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.TextView;
+
+import libcore.util.MutableInt;
+
+import java.lang.ref.WeakReference;
+
+import com.android.internal.widget.LockPatternUtils;
+
+/***
+ * Manages a number of views inside of the given layout. See below for a list of widgets.
+ */
+class KeyguardMessageArea extends TextView {
+ /** Handler token posted with accessibility announcement runnables. */
+ private static final Object ANNOUNCE_TOKEN = new Object();
+
+ /**
+ * Delay before speaking an accessibility announcement. Used to prevent
+ * lift-to-type from interrupting itself.
+ */
+ private static final long ANNOUNCEMENT_DELAY = 250;
+
+ static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
+ static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
+
+ static final int SECURITY_MESSAGE_DURATION = 5000;
+ protected static final int FADE_DURATION = 750;
+
+ private static final String TAG = "KeyguardMessageArea";
+
+ // are we showing battery information?
+ boolean mShowingBatteryInfo = false;
+
+ // is the bouncer up?
+ boolean mShowingBouncer = false;
+
+ // last known plugged in state
+ boolean mCharging = false;
+
+ // last known battery level
+ int mBatteryLevel = 100;
+
+ KeyguardUpdateMonitor mUpdateMonitor;
+
+ // Timeout before we reset the message to show charging/owner info
+ long mTimeout = SECURITY_MESSAGE_DURATION;
+
+ // Shadowed text values
+ protected boolean mBatteryCharged;
+ protected boolean mBatteryIsLow;
+
+ private Handler mHandler;
+
+ CharSequence mMessage;
+ boolean mShowingMessage;
+ private CharSequence mSeparator;
+ private LockPatternUtils mLockPatternUtils;
+
+ Runnable mClearMessageRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mMessage = null;
+ mShowingMessage = false;
+ if (mShowingBouncer) {
+ hideMessage(FADE_DURATION, true);
+ } else {
+ update();
+ }
+ }
+ };
+
+ public static class Helper implements SecurityMessageDisplay {
+ KeyguardMessageArea mMessageArea;
+ Helper(View v) {
+ mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area);
+ if (mMessageArea == null) {
+ throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
+ }
+ }
+
+ public void setMessage(CharSequence msg, boolean important) {
+ if (!TextUtils.isEmpty(msg) && important) {
+ mMessageArea.mMessage = msg;
+ mMessageArea.securityMessageChanged();
+ }
+ }
+
+ public void setMessage(int resId, boolean important) {
+ if (resId != 0 && important) {
+ mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId);
+ mMessageArea.securityMessageChanged();
+ }
+ }
+
+ public void setMessage(int resId, boolean important, Object... formatArgs) {
+ if (resId != 0 && important) {
+ mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs);
+ mMessageArea.securityMessageChanged();
+ }
+ }
+
+ @Override
+ public void showBouncer(int duration) {
+ mMessageArea.hideMessage(duration, false);
+ mMessageArea.mShowingBouncer = true;
+ }
+
+ @Override
+ public void hideBouncer(int duration) {
+ mMessageArea.showMessage(duration);
+ mMessageArea.mShowingBouncer = false;
+ }
+
+ @Override
+ public void setTimeout(int timeoutMs) {
+ mMessageArea.mTimeout = timeoutMs;
+ }
+ }
+
+ private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
+ mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
+ mCharging = status.status == BatteryManager.BATTERY_STATUS_CHARGING
+ || status.status == BatteryManager.BATTERY_STATUS_FULL;
+ mBatteryLevel = status.level;
+ mBatteryCharged = status.isCharged();
+ mBatteryIsLow = status.isBatteryLow();
+ update();
+ }
+ };
+
+ public KeyguardMessageArea(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardMessageArea(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mLockPatternUtils = new LockPatternUtils(context);
+
+ // This is required to ensure marquee works
+ setSelected(true);
+
+ // Registering this callback immediately updates the battery state, among other things.
+ mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
+ mUpdateMonitor.registerCallback(mInfoCallback);
+ mHandler = new Handler(Looper.myLooper());
+
+ mSeparator = getResources().getString(R.string.kg_text_message_separator);
+
+ update();
+ }
+
+ public void securityMessageChanged() {
+ setAlpha(1f);
+ mShowingMessage = true;
+ update();
+ mHandler.removeCallbacks(mClearMessageRunnable);
+ if (mTimeout > 0) {
+ mHandler.postDelayed(mClearMessageRunnable, mTimeout);
+ }
+ mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
+ mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
+ (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
+ }
+
+ /**
+ * Update the status lines based on these rules:
+ * AlarmStatus: Alarm state always gets it's own line.
+ * Status1 is shared between help, battery status and generic unlock instructions,
+ * prioritized in that order.
+ * @param showStatusLines status lines are shown if true
+ */
+ void update() {
+ MutableInt icon = new MutableInt(0);
+ CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
+ setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
+ setText(status);
+ }
+
+ private CharSequence concat(CharSequence... args) {
+ StringBuilder b = new StringBuilder();
+ if (!TextUtils.isEmpty(args[0])) {
+ b.append(args[0]);
+ }
+ for (int i = 1; i < args.length; i++) {
+ CharSequence text = args[i];
+ if (!TextUtils.isEmpty(text)) {
+ if (b.length() > 0) {
+ b.append(mSeparator);
+ }
+ b.append(text);
+ }
+ }
+ return b.toString();
+ }
+
+ CharSequence getCurrentMessage() {
+ return mShowingMessage ? mMessage : null;
+ }
+
+ String getOwnerInfo() {
+ ContentResolver res = getContext().getContentResolver();
+ String info = null;
+ final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled();
+ if (ownerInfoEnabled && !mShowingMessage) {
+ info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser());
+ }
+ return info;
+ }
+
+ private CharSequence getChargeInfo(MutableInt icon) {
+ CharSequence string = null;
+ if (mShowingBatteryInfo && !mShowingMessage) {
+ // Battery status
+ if (mCharging) {
+ // Charging, charged or waiting to charge.
+ string = getContext().getString(mBatteryCharged
+ ? R.string.keyguard_charged
+ : R.string.keyguard_plugged_in, mBatteryLevel);
+ icon.value = CHARGING_ICON;
+ } else if (mBatteryIsLow) {
+ // Battery is low
+ string = getContext().getString(R.string.keyguard_low_battery);
+ icon.value = BATTERY_LOW_ICON;
+ }
+ }
+ return string;
+ }
+
+ private void hideMessage(int duration, boolean thenUpdate) {
+ if (duration > 0) {
+ Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
+ anim.setDuration(duration);
+ if (thenUpdate) {
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ update();
+ }
+ });
+ }
+ anim.start();
+ } else {
+ setAlpha(0f);
+ if (thenUpdate) {
+ update();
+ }
+ }
+ }
+
+ private void showMessage(int duration) {
+ if (duration > 0) {
+ Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f);
+ anim.setDuration(duration);
+ anim.start();
+ } else {
+ setAlpha(1f);
+ }
+ }
+
+ /**
+ * Runnable used to delay accessibility announcements.
+ */
+ private static class AnnounceRunnable implements Runnable {
+ private final WeakReference<View> mHost;
+ private final CharSequence mTextToAnnounce;
+
+ public AnnounceRunnable(View host, CharSequence textToAnnounce) {
+ mHost = new WeakReference<View>(host);
+ mTextToAnnounce = textToAnnounce;
+ }
+
+ @Override
+ public void run() {
+ final View host = mHost.get();
+ if (host != null) {
+ host.announceForAccessibility(mTextToAnnounce);
+ }
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserAvatar.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserAvatar.java
new file mode 100644
index 0000000..aa2ae0e
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserAvatar.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.os.UserManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+class KeyguardMultiUserAvatar extends FrameLayout {
+ private static final String TAG = KeyguardMultiUserAvatar.class.getSimpleName();
+ private static final boolean DEBUG = KeyguardHostView.DEBUG;
+
+ private ImageView mUserImage;
+ private TextView mUserName;
+ private UserInfo mUserInfo;
+ private static final float ACTIVE_ALPHA = 1.0f;
+ private static final float INACTIVE_ALPHA = 1.0f;
+ private static final float ACTIVE_SCALE = 1.5f;
+ private static final float ACTIVE_TEXT_ALPHA = 0f;
+ private static final float INACTIVE_TEXT_ALPHA = 0.5f;
+ private static final int SWITCH_ANIMATION_DURATION = 150;
+
+ private final float mActiveAlpha;
+ private final float mActiveScale;
+ private final float mActiveTextAlpha;
+ private final float mInactiveAlpha;
+ private final float mInactiveTextAlpha;
+ private final float mShadowRadius;
+ private final float mStroke;
+ private final float mIconSize;
+ private final int mFrameColor;
+ private final int mFrameShadowColor;
+ private final int mTextColor;
+ private final int mHighlightColor;
+
+ private boolean mTouched;
+
+ private boolean mActive;
+ private boolean mInit = true;
+ private KeyguardMultiUserSelectorView mUserSelector;
+ private KeyguardCircleFramedDrawable mFramed;
+ private boolean mPressLock;
+ private UserManager mUserManager;
+
+ public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
+ KeyguardMultiUserSelectorView userSelector, UserInfo info) {
+ KeyguardMultiUserAvatar icon = (KeyguardMultiUserAvatar)
+ LayoutInflater.from(context).inflate(resId, userSelector, false);
+
+ icon.init(info, userSelector);
+ return icon;
+ }
+
+ public KeyguardMultiUserAvatar(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardMultiUserAvatar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardMultiUserAvatar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ Resources res = mContext.getResources();
+ mTextColor = res.getColor(R.color.keyguard_avatar_nick_color);
+ mIconSize = res.getDimension(R.dimen.keyguard_avatar_size);
+ mStroke = res.getDimension(R.dimen.keyguard_avatar_frame_stroke_width);
+ mShadowRadius = res.getDimension(R.dimen.keyguard_avatar_frame_shadow_radius);
+ mFrameColor = res.getColor(R.color.keyguard_avatar_frame_color);
+ mFrameShadowColor = res.getColor(R.color.keyguard_avatar_frame_shadow_color);
+ mHighlightColor = res.getColor(R.color.keyguard_avatar_frame_pressed_color);
+ mActiveTextAlpha = ACTIVE_TEXT_ALPHA;
+ mInactiveTextAlpha = INACTIVE_TEXT_ALPHA;
+ mActiveScale = ACTIVE_SCALE;
+ mActiveAlpha = ACTIVE_ALPHA;
+ mInactiveAlpha = INACTIVE_ALPHA;
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+ mTouched = false;
+
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+
+ protected String rewriteIconPath(String path) {
+ return path;
+ }
+
+ public void init(UserInfo user, KeyguardMultiUserSelectorView userSelector) {
+ mUserInfo = user;
+ mUserSelector = userSelector;
+
+ mUserImage = (ImageView) findViewById(R.id.keyguard_user_avatar);
+ mUserName = (TextView) findViewById(R.id.keyguard_user_name);
+
+ mFramed = (KeyguardCircleFramedDrawable)
+ KeyguardViewMediator.getAvatarCache().get(user.id);
+
+ // If we can't find it or the params don't match, create the drawable again
+ if (mFramed == null
+ || !mFramed.verifyParams(mIconSize, mFrameColor, mStroke, mFrameShadowColor,
+ mShadowRadius, mHighlightColor)) {
+ Bitmap icon = null;
+ try {
+ icon = mUserManager.getUserIcon(user.id);
+ } catch (Exception e) {
+ if (DEBUG) Log.d(TAG, "failed to get profile icon " + user, e);
+ }
+
+ if (icon == null) {
+ icon = BitmapFactory.decodeResource(mContext.getResources(),
+ com.android.internal.R.drawable.ic_contact_picture);
+ }
+
+ mFramed = new KeyguardCircleFramedDrawable(icon, (int) mIconSize, mFrameColor, mStroke,
+ mFrameShadowColor, mShadowRadius, mHighlightColor);
+ KeyguardViewMediator.getAvatarCache().put(user.id, mFramed);
+ }
+
+ mFramed.reset();
+
+ mUserImage.setImageDrawable(mFramed);
+ mUserName.setText(mUserInfo.name);
+ setOnClickListener(mUserSelector);
+ mInit = false;
+ }
+
+ public void setActive(boolean active, boolean animate, final Runnable onComplete) {
+ if (mActive != active || mInit) {
+ mActive = active;
+
+ if (active) {
+ KeyguardLinearLayout parent = (KeyguardLinearLayout) getParent();
+ parent.setTopChild(this);
+ // TODO: Create an appropriate asset when string changes are possible.
+ setContentDescription(mUserName.getText()
+ + ". " + mContext.getString(R.string.user_switched, ""));
+ } else {
+ setContentDescription(mUserName.getText());
+ }
+ }
+ updateVisualsForActive(mActive, animate, SWITCH_ANIMATION_DURATION, onComplete);
+ }
+
+ void updateVisualsForActive(boolean active, boolean animate, int duration,
+ final Runnable onComplete) {
+ final float finalAlpha = active ? mActiveAlpha : mInactiveAlpha;
+ final float initAlpha = active ? mInactiveAlpha : mActiveAlpha;
+ final float finalScale = active ? 1f : 1f / mActiveScale;
+ final float initScale = mFramed.getScale();
+ final int finalTextAlpha = active ? (int) (mActiveTextAlpha * 255) :
+ (int) (mInactiveTextAlpha * 255);
+ final int initTextAlpha = active ? (int) (mInactiveTextAlpha * 255) :
+ (int) (mActiveTextAlpha * 255);
+ int textColor = mTextColor;
+ mUserName.setTextColor(textColor);
+
+ if (animate && mTouched) {
+ ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+ va.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float r = animation.getAnimatedFraction();
+ float scale = (1 - r) * initScale + r * finalScale;
+ float alpha = (1 - r) * initAlpha + r * finalAlpha;
+ int textAlpha = (int) ((1 - r) * initTextAlpha + r * finalTextAlpha);
+ mFramed.setScale(scale);
+ mUserImage.setAlpha(alpha);
+ mUserName.setTextColor(Color.argb(textAlpha, 255, 255, 255));
+ mUserImage.invalidate();
+ }
+ });
+ va.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (onComplete != null) {
+ onComplete.run();
+ }
+ }
+ });
+ va.setDuration(duration);
+ va.start();
+ } else {
+ mFramed.setScale(finalScale);
+ mUserImage.setAlpha(finalAlpha);
+ mUserName.setTextColor(Color.argb(finalTextAlpha, 255, 255, 255));
+ if (onComplete != null) {
+ post(onComplete);
+ }
+ }
+
+ mTouched = true;
+ }
+
+ @Override
+ public void setPressed(boolean pressed) {
+ if (mPressLock && !pressed) {
+ return;
+ }
+
+ if (mPressLock || !pressed || isClickable()) {
+ super.setPressed(pressed);
+ mFramed.setPressed(pressed);
+ mUserImage.invalidate();
+ }
+ }
+
+ public void lockPressed(boolean pressed) {
+ mPressLock = pressed;
+ setPressed(pressed);
+ }
+
+ public UserInfo getUserInfo() {
+ return mUserInfo;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserSelectorView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserSelectorView.java
new file mode 100644
index 0000000..7975d8e
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserSelectorView.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+
+public class KeyguardMultiUserSelectorView extends FrameLayout implements View.OnClickListener {
+ private static final String TAG = "KeyguardMultiUserSelectorView";
+
+ private ViewGroup mUsersGrid;
+ private KeyguardMultiUserAvatar mActiveUserAvatar;
+ private KeyguardHostView.UserSwitcherCallback mCallback;
+ private static final int FADE_OUT_ANIMATION_DURATION = 100;
+
+ public KeyguardMultiUserSelectorView(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardMultiUserSelectorView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardMultiUserSelectorView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ protected void onFinishInflate () {
+ mUsersGrid = (ViewGroup) findViewById(R.id.keyguard_users_grid);
+ mUsersGrid.removeAllViews();
+ setClipChildren(false);
+ setClipToPadding(false);
+
+ }
+
+ public void setCallback(KeyguardHostView.UserSwitcherCallback callback) {
+ mCallback = callback;
+ }
+
+ public void addUsers(Collection<UserInfo> userList) {
+ UserInfo activeUser;
+ try {
+ activeUser = ActivityManagerNative.getDefault().getCurrentUser();
+ } catch (RemoteException re) {
+ activeUser = null;
+ }
+
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(userList);
+ Collections.sort(users, mOrderAddedComparator);
+
+ for (UserInfo user: users) {
+ KeyguardMultiUserAvatar uv = createAndAddUser(user);
+ if (user.id == activeUser.id) {
+ mActiveUserAvatar = uv;
+ }
+ uv.setActive(false, false, null);
+ }
+ mActiveUserAvatar.lockPressed(true);
+ }
+
+ public void finalizeActiveUserView(boolean animate) {
+ if (animate) {
+ getHandler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ finalizeActiveUserNow(true);
+ }
+ }, 500);
+ } else {
+ finalizeActiveUserNow(animate);
+ }
+ }
+
+ void finalizeActiveUserNow(boolean animate) {
+ mActiveUserAvatar.lockPressed(false);
+ mActiveUserAvatar.setActive(true, animate, null);
+ }
+
+ Comparator<UserInfo> mOrderAddedComparator = new Comparator<UserInfo>() {
+ @Override
+ public int compare(UserInfo lhs, UserInfo rhs) {
+ return (lhs.serialNumber - rhs.serialNumber);
+ }
+ };
+
+ private KeyguardMultiUserAvatar createAndAddUser(UserInfo user) {
+ KeyguardMultiUserAvatar uv = KeyguardMultiUserAvatar.fromXml(
+ R.layout.keyguard_multi_user_avatar, mContext, this, user);
+ mUsersGrid.addView(uv);
+ return uv;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if(event.getActionMasked() != MotionEvent.ACTION_CANCEL && mCallback != null) {
+ mCallback.userActivity();
+ }
+ return false;
+ }
+
+ private void setAllClickable(boolean clickable)
+ {
+ for(int i = 0; i < mUsersGrid.getChildCount(); i++) {
+ View v = mUsersGrid.getChildAt(i);
+ v.setClickable(clickable);
+ v.setPressed(false);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!(v instanceof KeyguardMultiUserAvatar)) return;
+ final KeyguardMultiUserAvatar avatar = (KeyguardMultiUserAvatar) v;
+ if (avatar.isClickable()) { // catch race conditions
+ if (mActiveUserAvatar == avatar) {
+ // If they click the currently active user, show the unlock hint
+ mCallback.showUnlockHint();
+ return;
+ } else {
+ // Reset the previously active user to appear inactive
+ mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION);
+ setAllClickable(false);
+ avatar.lockPressed(true);
+ mActiveUserAvatar.setActive(false, true, new Runnable() {
+ @Override
+ public void run() {
+ mActiveUserAvatar = avatar;
+ try {
+ ActivityManagerNative.getDefault()
+ .switchUser(avatar.getUserInfo().id);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Couldn't switch user " + re);
+ }
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
new file mode 100644
index 0000000..3d1c3f3
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.text.method.DigitsKeyListener;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView.OnEditorActionListener;
+
+/**
+ * Displays a PIN pad for unlocking.
+ */
+public class KeyguardPINView extends KeyguardAbsKeyInputView
+ implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+
+ public KeyguardPINView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardPINView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ protected void resetState() {
+ if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) {
+ mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true);
+ } else {
+ mSecurityMessageDisplay.setMessage(R.string.kg_pin_instructions, false);
+ }
+ mPasswordEntry.setEnabled(true);
+ }
+
+ @Override
+ protected int getPasswordTextViewId() {
+ return R.id.pinEntry;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ final View ok = findViewById(R.id.key_enter);
+ if (ok != null) {
+ ok.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doHapticKeyClick();
+ if (mPasswordEntry.isEnabled()) {
+ verifyPasswordAndUnlock();
+ }
+ }
+ });
+ ok.setOnHoverListener(new LiftToActivateListener(getContext()));
+ }
+
+ // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
+ // not a separate view
+ View pinDelete = findViewById(R.id.delete_button);
+ if (pinDelete != null) {
+ pinDelete.setVisibility(View.VISIBLE);
+ pinDelete.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ CharSequence str = mPasswordEntry.getText();
+ if (str.length() > 0) {
+ mPasswordEntry.setText(str.subSequence(0, str.length()-1));
+ }
+ }
+ doHapticKeyClick();
+ }
+ });
+ pinDelete.setOnLongClickListener(new View.OnLongClickListener() {
+ public boolean onLongClick(View v) {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ mPasswordEntry.setText("");
+ }
+ doHapticKeyClick();
+ return true;
+ }
+ });
+ }
+
+ mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
+ | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+
+ mPasswordEntry.requestFocus();
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
+ @Override
+ public int getWrongPasswordStringId() {
+ return R.string.kg_wrong_pin;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
new file mode 100644
index 0000000..4e3568b
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.text.method.DigitsKeyListener;
+import android.text.method.TextKeyListener;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.widget.PasswordEntryKeyboardHelper;
+import com.android.internal.widget.PasswordEntryKeyboardView;
+
+import java.util.List;
+/**
+ * Displays an alphanumeric (latin-1) key entry for the user to enter
+ * an unlock password
+ */
+
+public class KeyguardPasswordView extends KeyguardAbsKeyInputView
+ implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+
+ private final boolean mShowImeAtScreenOn;
+
+ InputMethodManager mImm;
+
+ public KeyguardPasswordView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardPasswordView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mShowImeAtScreenOn = context.getResources().
+ getBoolean(R.bool.kg_show_ime_at_screen_on);
+ }
+
+ protected void resetState() {
+ mSecurityMessageDisplay.setMessage(R.string.kg_password_instructions, false);
+ mPasswordEntry.setEnabled(true);
+ }
+
+ @Override
+ protected int getPasswordTextViewId() {
+ return R.id.passwordEntry;
+ }
+
+ @Override
+ public boolean needsInput() {
+ return true;
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ mPasswordEntry.requestFocus();
+ if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
+ mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mImm.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ boolean imeOrDeleteButtonVisible = false;
+
+ mImm = (InputMethodManager) getContext().getSystemService(
+ Context.INPUT_METHOD_SERVICE);
+
+ mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+
+ // Poke the wakelock any time the text is selected or modified
+ mPasswordEntry.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ mCallback.userActivity(0); // TODO: customize timeout for text?
+ }
+ });
+
+ mPasswordEntry.addTextChangedListener(new TextWatcher() {
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ public void afterTextChanged(Editable s) {
+ if (mCallback != null) {
+ mCallback.userActivity(0);
+ }
+ }
+ });
+
+ mPasswordEntry.requestFocus();
+
+ // If there's more than one IME, enable the IME switcher button
+ View switchImeButton = findViewById(R.id.switch_ime_button);
+ if (switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(mImm, false)) {
+ switchImeButton.setVisibility(View.VISIBLE);
+ imeOrDeleteButtonVisible = true;
+ switchImeButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ mCallback.userActivity(0); // Leave the screen on a bit longer
+ mImm.showInputMethodPicker();
+ }
+ });
+ }
+
+ // If no icon is visible, reset the start margin on the password field so the text is
+ // still centered.
+ if (!imeOrDeleteButtonVisible) {
+ android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
+ if (params instanceof MarginLayoutParams) {
+ final MarginLayoutParams mlp = (MarginLayoutParams) params;
+ mlp.setMarginStart(0);
+ mPasswordEntry.setLayoutParams(params);
+ }
+ }
+ }
+
+ /**
+ * Method adapted from com.android.inputmethod.latin.Utils
+ *
+ * @param imm The input method manager
+ * @param shouldIncludeAuxiliarySubtypes
+ * @return true if we have multiple IMEs to choose from
+ */
+ private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
+ final boolean shouldIncludeAuxiliarySubtypes) {
+ final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
+
+ // Number of the filtered IMEs
+ int filteredImisCount = 0;
+
+ for (InputMethodInfo imi : enabledImis) {
+ // We can return true immediately after we find two or more filtered IMEs.
+ if (filteredImisCount > 1) return true;
+ final List<InputMethodSubtype> subtypes =
+ imm.getEnabledInputMethodSubtypeList(imi, true);
+ // IMEs that have no subtypes should be counted.
+ if (subtypes.isEmpty()) {
+ ++filteredImisCount;
+ continue;
+ }
+
+ int auxCount = 0;
+ for (InputMethodSubtype subtype : subtypes) {
+ if (subtype.isAuxiliary()) {
+ ++auxCount;
+ }
+ }
+ final int nonAuxCount = subtypes.size() - auxCount;
+
+ // IMEs that have one or more non-auxiliary subtypes should be counted.
+ // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+ // subtypes should be counted as well.
+ if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+ ++filteredImisCount;
+ continue;
+ }
+ }
+
+ return filteredImisCount > 1
+ // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
+ // input method subtype (The current IME should be LatinIME.)
+ || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
+ @Override
+ public int getWrongPasswordStringId() {
+ return R.string.kg_wrong_password;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
new file mode 100644
index 0000000..e7f1259
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+
+import java.io.IOException;
+import java.util.List;
+
+public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView {
+
+ private static final String TAG = "SecurityPatternView";
+ private static final boolean DEBUG = false;
+
+ // how long before we clear the wrong pattern
+ private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
+
+ // how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
+ private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
+
+ // how long we stay awake after the user hits the first dot.
+ private static final int UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS = 2000;
+
+ // how many cells the user has to cross before we poke the wakelock
+ private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
+
+ private int mFailedPatternAttemptsSinceLastTimeout = 0;
+ private int mTotalFailedPatternAttempts = 0;
+ private CountDownTimer mCountdownTimer = null;
+ private LockPatternUtils mLockPatternUtils;
+ private LockPatternView mLockPatternView;
+ private Button mForgotPatternButton;
+ private KeyguardSecurityCallback mCallback;
+ private boolean mEnableFallback;
+
+ /**
+ * Keeps track of the last time we poked the wake lock during dispatching of the touch event.
+ * Initialized to something guaranteed to make us poke the wakelock when the user starts
+ * drawing the pattern.
+ * @see #dispatchTouchEvent(android.view.MotionEvent)
+ */
+ private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
+
+ /**
+ * Useful for clearing out the wrong pattern after a delay
+ */
+ private Runnable mCancelPatternRunnable = new Runnable() {
+ public void run() {
+ mLockPatternView.clearPattern();
+ }
+ };
+ private Rect mTempRect = new Rect();
+ private SecurityMessageDisplay mSecurityMessageDisplay;
+ private View mEcaView;
+ private Drawable mBouncerFrame;
+
+ enum FooterMode {
+ Normal,
+ ForgotLockPattern,
+ VerifyUnlocked
+ }
+
+ public KeyguardPatternView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardPatternView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mLockPatternUtils = mLockPatternUtils == null
+ ? new LockPatternUtils(mContext) : mLockPatternUtils;
+
+ mLockPatternView = (LockPatternView) findViewById(R.id.lockPatternView);
+ mLockPatternView.setSaveEnabled(false);
+ mLockPatternView.setFocusable(false);
+ mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+
+ // stealth mode will be the same for the life of this screen
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled());
+
+ // vibrate mode will be the same for the life of this screen
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ mForgotPatternButton = (Button) findViewById(R.id.forgot_password_button);
+ // note: some configurations don't have an emergency call area
+ if (mForgotPatternButton != null) {
+ mForgotPatternButton.setText(R.string.kg_forgot_pattern_button_text);
+ mForgotPatternButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ mCallback.showBackupSecurity();
+ }
+ });
+ }
+
+ setFocusableInTouchMode(true);
+
+ maybeEnableFallback(mContext);
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
+ mEcaView = findViewById(R.id.keyguard_selector_fade_container);
+ View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame);
+ if (bouncerFrameView != null) {
+ mBouncerFrame = bouncerFrameView.getBackground();
+ }
+ }
+
+ private void updateFooter(FooterMode mode) {
+ if (mForgotPatternButton == null) return; // no ECA? no footer
+
+ switch (mode) {
+ case Normal:
+ if (DEBUG) Log.d(TAG, "mode normal");
+ mForgotPatternButton.setVisibility(View.GONE);
+ break;
+ case ForgotLockPattern:
+ if (DEBUG) Log.d(TAG, "mode ForgotLockPattern");
+ mForgotPatternButton.setVisibility(View.VISIBLE);
+ break;
+ case VerifyUnlocked:
+ if (DEBUG) Log.d(TAG, "mode VerifyUnlocked");
+ mForgotPatternButton.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ boolean result = super.onTouchEvent(ev);
+ // as long as the user is entering a pattern (i.e sending a touch event that was handled
+ // by this screen), keep poking the wake lock so that the screen will stay on.
+ final long elapsed = SystemClock.elapsedRealtime() - mLastPokeTime;
+ if (result && (elapsed > (UNLOCK_PATTERN_WAKE_INTERVAL_MS - 100))) {
+ mLastPokeTime = SystemClock.elapsedRealtime();
+ }
+ mTempRect.set(0, 0, 0, 0);
+ offsetRectIntoDescendantCoords(mLockPatternView, mTempRect);
+ ev.offsetLocation(mTempRect.left, mTempRect.top);
+ result = mLockPatternView.dispatchTouchEvent(ev) || result;
+ ev.offsetLocation(-mTempRect.left, -mTempRect.top);
+ return result;
+ }
+
+ public void reset() {
+ // reset lock pattern
+ mLockPatternView.enableInput();
+ mLockPatternView.setEnabled(true);
+ mLockPatternView.clearPattern();
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ } else {
+ displayDefaultSecurityMessage();
+ }
+
+ // the footer depends on how many total attempts the user has failed
+ if (mCallback.isVerifyUnlockOnly()) {
+ updateFooter(FooterMode.VerifyUnlocked);
+ } else if (mEnableFallback &&
+ (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+ updateFooter(FooterMode.ForgotLockPattern);
+ } else {
+ updateFooter(FooterMode.Normal);
+ }
+
+ }
+
+ private void displayDefaultSecurityMessage() {
+ if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) {
+ mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true);
+ } else {
+ mSecurityMessageDisplay.setMessage(R.string.kg_pattern_instructions, false);
+ }
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
+ /** TODO: hook this up */
+ public void cleanUp() {
+ if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
+ mLockPatternUtils = null;
+ mLockPatternView.setOnPatternListener(null);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (hasWindowFocus) {
+ // when timeout dialog closes we want to update our state
+ reset();
+ }
+ }
+
+ private class UnlockPatternListener implements LockPatternView.OnPatternListener {
+
+ public void onPatternStart() {
+ mLockPatternView.removeCallbacks(mCancelPatternRunnable);
+ }
+
+ public void onPatternCleared() {
+ }
+
+ public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
+ // To guard against accidental poking of the wakelock, look for
+ // the user actually trying to draw a pattern of some minimal length.
+ if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+ mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
+ } else {
+ // Give just a little extra time if they hit one of the first few dots
+ mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS);
+ }
+ }
+
+ public void onPatternDetected(List<LockPatternView.Cell> pattern) {
+ if (mLockPatternUtils.checkPattern(pattern)) {
+ mCallback.reportSuccessfulUnlockAttempt();
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
+ mTotalFailedPatternAttempts = 0;
+ mCallback.dismiss(true);
+ } else {
+ if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+ mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
+ }
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
+ if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+ mTotalFailedPatternAttempts++;
+ mFailedPatternAttemptsSinceLastTimeout++;
+ mCallback.reportFailedUnlockAttempt();
+ }
+ if (mFailedPatternAttemptsSinceLastTimeout
+ >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
+ handleAttemptLockout(deadline);
+ } else {
+ mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern, true);
+ mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
+ }
+ }
+ }
+ }
+
+ private void maybeEnableFallback(Context context) {
+ // Ask the account manager if we have an account that can be used as a
+ // fallback in case the user forgets his pattern.
+ AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context));
+ accountAnalyzer.start();
+ }
+
+ private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
+ private final AccountManager mAccountManager;
+ private final Account[] mAccounts;
+ private int mAccountIndex;
+
+ private AccountAnalyzer(AccountManager accountManager) {
+ mAccountManager = accountManager;
+ mAccounts = accountManager.getAccountsByTypeAsUser("com.google",
+ new UserHandle(mLockPatternUtils.getCurrentUser()));
+ }
+
+ private void next() {
+ // if we are ready to enable the fallback or if we depleted the list of accounts
+ // then finish and get out
+ if (mEnableFallback || mAccountIndex >= mAccounts.length) {
+ return;
+ }
+
+ // lookup the confirmCredentials intent for the current account
+ mAccountManager.confirmCredentialsAsUser(mAccounts[mAccountIndex], null, null, this,
+ null, new UserHandle(mLockPatternUtils.getCurrentUser()));
+ }
+
+ public void start() {
+ mEnableFallback = false;
+ mAccountIndex = 0;
+ next();
+ }
+
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ Bundle result = future.getResult();
+ if (result.getParcelable(AccountManager.KEY_INTENT) != null) {
+ mEnableFallback = true;
+ }
+ } catch (OperationCanceledException e) {
+ // just skip the account if we are unable to query it
+ } catch (IOException e) {
+ // just skip the account if we are unable to query it
+ } catch (AuthenticatorException e) {
+ // just skip the account if we are unable to query it
+ } finally {
+ mAccountIndex++;
+ next();
+ }
+ }
+ }
+
+ private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mLockPatternView.clearPattern();
+ mLockPatternView.setEnabled(false);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ if (mEnableFallback) {
+ updateFooter(FooterMode.ForgotLockPattern);
+ }
+
+ mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ final int secondsRemaining = (int) (millisUntilFinished / 1000);
+ mSecurityMessageDisplay.setMessage(
+ R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining);
+ }
+
+ @Override
+ public void onFinish() {
+ mLockPatternView.setEnabled(true);
+ displayDefaultSecurityMessage();
+ // TODO mUnlockIcon.setVisibility(View.VISIBLE);
+ mFailedPatternAttemptsSinceLastTimeout = 0;
+ if (mEnableFallback) {
+ updateFooter(FooterMode.ForgotLockPattern);
+ } else {
+ updateFooter(FooterMode.Normal);
+ }
+ }
+
+ }.start();
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ reset();
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void showBouncer(int duration) {
+ KeyguardSecurityViewHelper.
+ showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
+ }
+
+ @Override
+ public void hideBouncer(int duration) {
+ KeyguardSecurityViewHelper.
+ hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
new file mode 100644
index 0000000..4f139ad
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
+
+public interface KeyguardSecurityCallback {
+
+ /**
+ * Dismiss the given security screen.
+ * @param securityVerified true if the user correctly entered credentials for the given screen.
+ */
+ void dismiss(boolean securityVerified);
+
+ /**
+ * Manually report user activity to keep the device awake. If timeout is 0,
+ * uses user-defined timeout.
+ * @param timeout
+ */
+ void userActivity(long timeout);
+
+ /**
+ * Checks if keyguard is in "verify credentials" mode.
+ * @return true if user has been asked to verify security.
+ */
+ boolean isVerifyUnlockOnly();
+
+ /**
+ * Call when user correctly enters their credentials
+ */
+ void reportSuccessfulUnlockAttempt();
+
+ /**
+ * Call when the user incorrectly enters their credentials
+ */
+ void reportFailedUnlockAttempt();
+
+ /**
+ * Gets the number of attempts thus far as reported by {@link #reportFailedUnlockAttempt()}
+ * @return number of failed attempts
+ */
+ int getFailedAttempts();
+
+ /**
+ * Shows the backup security for the current method. If none available, this call is a no-op.
+ */
+ void showBackupSecurity();
+
+ /**
+ * Sets an action to perform after the user successfully enters their credentials.
+ * @param action
+ */
+ void setOnDismissAction(OnDismissAction action);
+
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
new file mode 100644
index 0000000..9d03c6a
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -0,0 +1,45 @@
+package com.android.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+public class KeyguardSecurityContainer extends FrameLayout {
+ public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardSecurityContainer(Context context) {
+ this(null, null, 0);
+ }
+
+ public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ KeyguardSecurityViewFlipper getFlipper() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof KeyguardSecurityViewFlipper) {
+ return (KeyguardSecurityViewFlipper) child;
+ }
+ }
+ return null;
+ }
+
+ public void showBouncer(int duration) {
+ KeyguardSecurityViewFlipper flipper = getFlipper();
+ if (flipper != null) {
+ flipper.showBouncer(duration);
+ }
+ }
+
+ public void hideBouncer(int duration) {
+ KeyguardSecurityViewFlipper flipper = getFlipper();
+ if (flipper != null) {
+ flipper.hideBouncer(duration);
+ }
+ }
+}
+
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
new file mode 100644
index 0000000..4129e33
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.widget.LockPatternUtils;
+
+public class KeyguardSecurityModel {
+ /**
+ * The different types of security available for {@link Mode#UnlockScreen}.
+ * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
+ */
+ enum SecurityMode {
+ Invalid, // NULL state
+ None, // No security enabled
+ Pattern, // Unlock by drawing a pattern.
+ Password, // Unlock by entering an alphanumeric password
+ PIN, // Strictly numeric password
+ Biometric, // Unlock with a biometric key (e.g. finger print or face unlock)
+ Account, // Unlock by entering an account's login and password.
+ SimPin, // Unlock by entering a sim pin.
+ SimPuk // Unlock by entering a sim puk
+ }
+
+ private Context mContext;
+ private LockPatternUtils mLockPatternUtils;
+
+ KeyguardSecurityModel(Context context) {
+ mContext = context;
+ mLockPatternUtils = new LockPatternUtils(context);
+ }
+
+ void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ /**
+ * Returns true if biometric unlock is installed and selected. If this returns false there is
+ * no need to even construct the biometric unlock.
+ */
+ boolean isBiometricUnlockEnabled() {
+ return mLockPatternUtils.usingBiometricWeak()
+ && mLockPatternUtils.isBiometricWeakInstalled();
+ }
+
+ /**
+ * Returns true if a condition is currently suppressing the biometric unlock. If this returns
+ * true there is no need to even construct the biometric unlock.
+ */
+ private boolean isBiometricUnlockSuppressed() {
+ KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final boolean backupIsTimedOut = monitor.getFailedUnlockAttempts() >=
+ LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
+ return monitor.getMaxBiometricUnlockAttemptsReached() || backupIsTimedOut
+ || !monitor.isAlternateUnlockEnabled()
+ || monitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE;
+ }
+
+ SecurityMode getSecurityMode() {
+ KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final IccCardConstants.State simState = updateMonitor.getSimState();
+ SecurityMode mode = SecurityMode.None;
+ if (simState == IccCardConstants.State.PIN_REQUIRED) {
+ mode = SecurityMode.SimPin;
+ } else if (simState == IccCardConstants.State.PUK_REQUIRED
+ && mLockPatternUtils.isPukUnlockScreenEnable()) {
+ mode = SecurityMode.SimPuk;
+ } else {
+ final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality();
+ switch (security) {
+ case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+ mode = mLockPatternUtils.isLockPasswordEnabled() ?
+ SecurityMode.PIN : SecurityMode.None;
+ break;
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+ mode = mLockPatternUtils.isLockPasswordEnabled() ?
+ SecurityMode.Password : SecurityMode.None;
+ break;
+
+ case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+ case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
+ if (mLockPatternUtils.isLockPatternEnabled()) {
+ mode = mLockPatternUtils.isPermanentlyLocked() ?
+ SecurityMode.Account : SecurityMode.Pattern;
+ }
+ break;
+
+ default:
+ throw new IllegalStateException("Unknown unlock mode:" + mode);
+ }
+ }
+ return mode;
+ }
+
+ /**
+ * Some unlock methods can have an alternate, such as biometric unlocks (e.g. face unlock).
+ * This function decides if an alternate unlock is available and returns it. Otherwise,
+ * returns @param mode.
+ *
+ * @param mode the mode we want the alternate for
+ * @return alternate or the given mode
+ */
+ SecurityMode getAlternateFor(SecurityMode mode) {
+ if (isBiometricUnlockEnabled() && !isBiometricUnlockSuppressed()
+ && (mode == SecurityMode.Password
+ || mode == SecurityMode.PIN
+ || mode == SecurityMode.Pattern)) {
+ return SecurityMode.Biometric;
+ }
+ return mode; // no alternate, return what was given
+ }
+
+ /**
+ * Some unlock methods can have a backup which gives the user another way to get into
+ * the device. This is currently only supported for Biometric and Pattern unlock.
+ *
+ * @return backup method or current security mode
+ */
+ SecurityMode getBackupSecurityMode(SecurityMode mode) {
+ switch(mode) {
+ case Biometric:
+ return getSecurityMode();
+ case Pattern:
+ return SecurityMode.Account;
+ }
+ return mode; // no backup, return current security mode
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
new file mode 100644
index 0000000..dfeacf3
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import com.android.internal.widget.LockPatternUtils;
+
+public interface KeyguardSecurityView {
+ static public final int SCREEN_ON = 1;
+ static public final int VIEW_REVEALED = 2;
+
+ /**
+ * Interface back to keyguard to tell it when security
+ * @param callback
+ */
+ void setKeyguardCallback(KeyguardSecurityCallback callback);
+
+ /**
+ * Set {@link LockPatternUtils} object. Useful for providing a mock interface.
+ * @param utils
+ */
+ void setLockPatternUtils(LockPatternUtils utils);
+
+ /**
+ * Reset the view and prepare to take input. This should do things like clearing the
+ * password or pattern and clear error messages.
+ */
+ void reset();
+
+ /**
+ * Emulate activity life cycle within the view. When called, the view should clean up
+ * and prepare to be removed.
+ */
+ void onPause();
+
+ /**
+ * Emulate activity life cycle within this view. When called, the view should prepare itself
+ * to be shown.
+ * @param reason the root cause of the event.
+ */
+ void onResume(int reason);
+
+ /**
+ * Inquire whether this view requires IME (keyboard) interaction.
+ *
+ * @return true if IME interaction is required.
+ */
+ boolean needsInput();
+
+ /**
+ * Get {@link KeyguardSecurityCallback} for the given object
+ * @return KeyguardSecurityCallback
+ */
+ KeyguardSecurityCallback getCallback();
+
+ /**
+ * Instruct the view to show usability hints, if any.
+ *
+ */
+ void showUsabilityHint();
+
+ /**
+ * Place the security view into bouncer mode.
+ * Animate transisiton if duration is non-zero.
+ * @param duration millisends for the transisiton animation.
+ */
+ void showBouncer(int duration);
+
+ /**
+ * Place the security view into non-bouncer mode.
+ * Animate transisiton if duration is non-zero.
+ * @param duration millisends for the transisiton animation.
+ */
+ void hideBouncer(int duration);
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
new file mode 100644
index 0000000..70a0e44
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ViewFlipper;
+
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so
+ * we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy.
+ *
+ */
+public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
+ private static final String TAG = "KeyguardSecurityViewFlipper";
+ private static final boolean DEBUG = false;
+
+ private Rect mTempRect = new Rect();
+
+ public KeyguardSecurityViewFlipper(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardSecurityViewFlipper(Context context, AttributeSet attr) {
+ super(context, attr);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ boolean result = super.onTouchEvent(ev);
+ mTempRect.set(0, 0, 0, 0);
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() == View.VISIBLE) {
+ offsetRectIntoDescendantCoords(child, mTempRect);
+ ev.offsetLocation(mTempRect.left, mTempRect.top);
+ result = child.dispatchTouchEvent(ev) || result;
+ ev.offsetLocation(-mTempRect.left, -mTempRect.top);
+ }
+ }
+ return result;
+ }
+
+ KeyguardSecurityView getSecurityView() {
+ View child = getChildAt(getDisplayedChild());
+ if (child instanceof KeyguardSecurityView) {
+ return (KeyguardSecurityView) child;
+ }
+ return null;
+ }
+
+ @Override
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.setKeyguardCallback(callback);
+ }
+ }
+
+ @Override
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.setLockPatternUtils(utils);
+ }
+ }
+
+ @Override
+ public void reset() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.reset();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.onPause();
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.onResume(reason);
+ }
+ }
+
+ @Override
+ public boolean needsInput() {
+ KeyguardSecurityView ksv = getSecurityView();
+ return (ksv != null) ? ksv.needsInput() : false;
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ KeyguardSecurityView ksv = getSecurityView();
+ return (ksv != null) ? ksv.getCallback() : null;
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.showUsabilityHint();
+ }
+ }
+
+ @Override
+ public void showBouncer(int duration) {
+ KeyguardSecurityView active = getSecurityView();
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof KeyguardSecurityView) {
+ KeyguardSecurityView ksv = (KeyguardSecurityView) child;
+ ksv.showBouncer(ksv == active ? duration : 0);
+ }
+ }
+ }
+
+ @Override
+ public void hideBouncer(int duration) {
+ KeyguardSecurityView active = getSecurityView();
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof KeyguardSecurityView) {
+ KeyguardSecurityView ksv = (KeyguardSecurityView) child;
+ ksv.hideBouncer(ksv == active ? duration : 0);
+ }
+ }
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) : new LayoutParams(p);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ final int widthMode = MeasureSpec.getMode(widthSpec);
+ final int heightMode = MeasureSpec.getMode(heightSpec);
+ if (DEBUG && widthMode != MeasureSpec.AT_MOST) {
+ Log.w(TAG, "onMeasure: widthSpec " + MeasureSpec.toString(widthSpec) +
+ " should be AT_MOST");
+ }
+ if (DEBUG && heightMode != MeasureSpec.AT_MOST) {
+ Log.w(TAG, "onMeasure: heightSpec " + MeasureSpec.toString(heightSpec) +
+ " should be AT_MOST");
+ }
+
+ final int widthSize = MeasureSpec.getSize(widthSpec);
+ final int heightSize = MeasureSpec.getSize(heightSpec);
+ int maxWidth = widthSize;
+ int maxHeight = heightSize;
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) {
+ maxWidth = lp.maxWidth;
+ }
+ if (lp.maxHeight > 0 && lp.maxHeight < maxHeight) {
+ maxHeight = lp.maxHeight;
+ }
+ }
+
+ final int wPadding = getPaddingLeft() + getPaddingRight();
+ final int hPadding = getPaddingTop() + getPaddingBottom();
+ maxWidth -= wPadding;
+ maxHeight -= hPadding;
+
+ int width = widthMode == MeasureSpec.EXACTLY ? widthSize : 0;
+ int height = heightMode == MeasureSpec.EXACTLY ? heightSize : 0;
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ final int childWidthSpec = makeChildMeasureSpec(maxWidth, lp.width);
+ final int childHeightSpec = makeChildMeasureSpec(maxHeight, lp.height);
+
+ child.measure(childWidthSpec, childHeightSpec);
+
+ width = Math.max(width, Math.min(child.getMeasuredWidth(), widthSize - wPadding));
+ height = Math.max(height, Math.min(child.getMeasuredHeight(), heightSize - hPadding));
+ }
+ setMeasuredDimension(width + wPadding, height + hPadding);
+ }
+
+ private int makeChildMeasureSpec(int maxSize, int childDimen) {
+ final int mode;
+ final int size;
+ switch (childDimen) {
+ case LayoutParams.WRAP_CONTENT:
+ mode = MeasureSpec.AT_MOST;
+ size = maxSize;
+ break;
+ case LayoutParams.MATCH_PARENT:
+ mode = MeasureSpec.EXACTLY;
+ size = maxSize;
+ break;
+ default:
+ mode = MeasureSpec.EXACTLY;
+ size = Math.min(maxSize, childDimen);
+ break;
+ }
+ return MeasureSpec.makeMeasureSpec(size, mode);
+ }
+
+ public static class LayoutParams extends FrameLayout.LayoutParams {
+ @ViewDebug.ExportedProperty(category = "layout")
+ public int maxWidth;
+
+ @ViewDebug.ExportedProperty(category = "layout")
+ public int maxHeight;
+
+ public LayoutParams(ViewGroup.LayoutParams other) {
+ super(other);
+ }
+
+ public LayoutParams(LayoutParams other) {
+ super(other);
+
+ maxWidth = other.maxWidth;
+ maxHeight = other.maxHeight;
+ }
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ final TypedArray a = c.obtainStyledAttributes(attrs,
+ R.styleable.KeyguardSecurityViewFlipper_Layout, 0, 0);
+ maxWidth = a.getDimensionPixelSize(
+ R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxWidth, 0);
+ maxHeight = a.getDimensionPixelSize(
+ R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxHeight, 0);
+ a.recycle();
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewHelper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewHelper.java
new file mode 100644
index 0000000..67a6f52
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewHelper.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+/**
+ * Some common functions that are useful for KeyguardSecurityViews.
+ */
+public class KeyguardSecurityViewHelper {
+
+ public static void showBouncer(SecurityMessageDisplay securityMessageDisplay,
+ final View ecaView, Drawable bouncerFrame, int duration) {
+ if (securityMessageDisplay != null) {
+ securityMessageDisplay.showBouncer(duration);
+ }
+ if (ecaView != null) {
+ if (duration > 0) {
+ Animator anim = ObjectAnimator.ofFloat(ecaView, "alpha", 0f);
+ anim.setDuration(duration);
+ anim.addListener(new AnimatorListenerAdapter() {
+ private boolean mCanceled;
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ // Fail safe and show the emergency button in onAnimationEnd()
+ mCanceled = true;
+ ecaView.setAlpha(1f);
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ecaView.setVisibility(mCanceled ? View.VISIBLE : View.INVISIBLE);
+ }
+ });
+ anim.start();
+ } else {
+ ecaView.setAlpha(0f);
+ ecaView.setVisibility(View.INVISIBLE);
+ }
+ }
+ if (bouncerFrame != null) {
+ if (duration > 0) {
+ Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 0, 255);
+ anim.setDuration(duration);
+ anim.start();
+ } else {
+ bouncerFrame.setAlpha(255);
+ }
+ }
+ }
+
+ public static void hideBouncer(SecurityMessageDisplay securityMessageDisplay,
+ View ecaView, Drawable bouncerFrame, int duration) {
+ if (securityMessageDisplay != null) {
+ securityMessageDisplay.hideBouncer(duration);
+ }
+ if (ecaView != null) {
+ ecaView.setVisibility(View.VISIBLE);
+ if (duration > 0) {
+ Animator anim = ObjectAnimator.ofFloat(ecaView, "alpha", 1f);
+ anim.setDuration(duration);
+ anim.start();
+ } else {
+ ecaView.setAlpha(1f);
+ }
+ }
+ if (bouncerFrame != null) {
+ if (duration > 0) {
+ Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 255, 0);
+ anim.setDuration(duration);
+ anim.start();
+ } else {
+ bouncerFrame.setAlpha(0);
+ }
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java
new file mode 100644
index 0000000..4d891be
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.animation.ObjectAnimator;
+import android.app.SearchManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.multiwaveview.GlowPadView;
+import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
+
+public class KeyguardSelectorView extends LinearLayout implements KeyguardSecurityView {
+ private static final boolean DEBUG = KeyguardHostView.DEBUG;
+ private static final String TAG = "SecuritySelectorView";
+ private static final String ASSIST_ICON_METADATA_NAME =
+ "com.android.systemui.action_assist_icon";
+
+ private KeyguardSecurityCallback mCallback;
+ private GlowPadView mGlowPadView;
+ private ObjectAnimator mAnim;
+ private View mFadeView;
+ private boolean mIsBouncing;
+ private boolean mCameraDisabled;
+ private boolean mSearchDisabled;
+ private LockPatternUtils mLockPatternUtils;
+ private SecurityMessageDisplay mSecurityMessageDisplay;
+ private Drawable mBouncerFrame;
+
+ OnTriggerListener mOnTriggerListener = new OnTriggerListener() {
+
+ public void onTrigger(View v, int target) {
+ final int resId = mGlowPadView.getResourceIdForTarget(target);
+ switch (resId) {
+ case R.drawable.ic_action_assist_generic:
+ Intent assistIntent =
+ ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
+ if (assistIntent != null) {
+ mActivityLauncher.launchActivity(assistIntent, false, true, null, null);
+ } else {
+ Log.w(TAG, "Failed to get intent for assist activity");
+ }
+ mCallback.userActivity(0);
+ break;
+
+ case R.drawable.ic_lockscreen_camera:
+ mActivityLauncher.launchCamera(null, null);
+ mCallback.userActivity(0);
+ break;
+
+ case R.drawable.ic_lockscreen_unlock_phantom:
+ case R.drawable.ic_lockscreen_unlock:
+ mCallback.userActivity(0);
+ mCallback.dismiss(false);
+ break;
+ }
+ }
+
+ public void onReleased(View v, int handle) {
+ if (!mIsBouncing) {
+ doTransition(mFadeView, 1.0f);
+ }
+ }
+
+ public void onGrabbed(View v, int handle) {
+ mCallback.userActivity(0);
+ doTransition(mFadeView, 0.0f);
+ }
+
+ public void onGrabbedStateChange(View v, int handle) {
+
+ }
+
+ public void onFinishFinalAnimation() {
+
+ }
+
+ };
+
+ KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+
+ @Override
+ public void onDevicePolicyManagerStateChanged() {
+ updateTargets();
+ }
+
+ @Override
+ public void onSimStateChanged(State simState) {
+ updateTargets();
+ }
+ };
+
+ private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
+
+ @Override
+ KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ LockPatternUtils getLockPatternUtils() {
+ return mLockPatternUtils;
+ }
+
+ @Override
+ Context getContext() {
+ return mContext;
+ }};
+
+ public KeyguardSelectorView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardSelectorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mLockPatternUtils = new LockPatternUtils(getContext());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
+ mGlowPadView.setOnTriggerListener(mOnTriggerListener);
+ updateTargets();
+
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
+ View bouncerFrameView = findViewById(R.id.keyguard_selector_view_frame);
+ mBouncerFrame = bouncerFrameView.getBackground();
+ }
+
+ public void setCarrierArea(View carrierArea) {
+ mFadeView = carrierArea;
+ }
+
+ public boolean isTargetPresent(int resId) {
+ return mGlowPadView.getTargetPosition(resId) != -1;
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ mGlowPadView.ping();
+ }
+
+ private void updateTargets() {
+ int currentUserHandle = mLockPatternUtils.getCurrentUser();
+ DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
+ int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUserHandle);
+ boolean secureCameraDisabled = mLockPatternUtils.isSecure()
+ && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
+ boolean cameraDisabledByAdmin = dpm.getCameraDisabled(null, currentUserHandle)
+ || secureCameraDisabled;
+ final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext());
+ boolean disabledBySimState = monitor.isSimLocked();
+ boolean cameraTargetPresent =
+ isTargetPresent(R.drawable.ic_lockscreen_camera);
+ boolean searchTargetPresent =
+ isTargetPresent(R.drawable.ic_action_assist_generic);
+
+ if (cameraDisabledByAdmin) {
+ Log.v(TAG, "Camera disabled by Device Policy");
+ } else if (disabledBySimState) {
+ Log.v(TAG, "Camera disabled by Sim State");
+ }
+ boolean currentUserSetup = 0 != Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 0 /*default */,
+ currentUserHandle);
+ boolean searchActionAvailable =
+ ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
+ mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent
+ || !currentUserSetup;
+ mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent
+ || !currentUserSetup;
+ updateResources();
+ }
+
+ public void updateResources() {
+ // Update the search icon with drawable from the search .apk
+ if (!mSearchDisabled) {
+ Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
+ if (intent != null) {
+ // XXX Hack. We need to substitute the icon here but haven't formalized
+ // the public API. The "_google" metadata will be going away, so
+ // DON'T USE IT!
+ ComponentName component = intent.getComponent();
+ boolean replaced = mGlowPadView.replaceTargetDrawablesIfPresent(component,
+ ASSIST_ICON_METADATA_NAME + "_google", R.drawable.ic_action_assist_generic);
+
+ if (!replaced && !mGlowPadView.replaceTargetDrawablesIfPresent(component,
+ ASSIST_ICON_METADATA_NAME, R.drawable.ic_action_assist_generic)) {
+ Slog.w(TAG, "Couldn't grab icon from package " + component);
+ }
+ }
+ }
+
+ mGlowPadView.setEnableTarget(R.drawable.ic_lockscreen_camera, !mCameraDisabled);
+ mGlowPadView.setEnableTarget(R.drawable.ic_action_assist_generic, !mSearchDisabled);
+ }
+
+ void doTransition(View view, float to) {
+ if (mAnim != null) {
+ mAnim.cancel();
+ }
+ mAnim = ObjectAnimator.ofFloat(view, "alpha", to);
+ mAnim.start();
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ @Override
+ public void reset() {
+ mGlowPadView.reset(false);
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ KeyguardUpdateMonitor.getInstance(getContext()).removeCallback(mInfoCallback);
+ }
+
+ @Override
+ public void onResume(int reason) {
+ KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mInfoCallback);
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void showBouncer(int duration) {
+ mIsBouncing = true;
+ KeyguardSecurityViewHelper.
+ showBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
+ }
+
+ @Override
+ public void hideBouncer(int duration) {
+ mIsBouncing = false;
+ KeyguardSecurityViewHelper.
+ hideBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
new file mode 100644
index 0000000..a70e5bd
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import android.app.Service;
+import android.content.Intent;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.policy.IKeyguardService;
+import com.android.internal.policy.IKeyguardExitCallback;
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.widget.LockPatternUtils;
+
+public class KeyguardService extends Service {
+ static final String TAG = "KeyguardService";
+ static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
+ private KeyguardViewMediator mKeyguardViewMediator;
+
+ @Override
+ public void onCreate() {
+ if (mKeyguardViewMediator == null) {
+ mKeyguardViewMediator = new KeyguardViewMediator(
+ KeyguardService.this, new LockPatternUtils(KeyguardService.this));
+ }
+ Log.v(TAG, "onCreate()");
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ // TODO
+ }
+
+ void checkPermission() {
+ if (getBaseContext().checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
+ Log.w(TAG, "Caller needs permission '" + PERMISSION + "' to call " + Debug.getCaller());
+ throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
+ + ", must have permission " + PERMISSION);
+ }
+ }
+
+ private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
+ public boolean isShowing() {
+ return mKeyguardViewMediator.isShowing();
+ }
+ public boolean isSecure() {
+ return mKeyguardViewMediator.isSecure();
+ }
+ public boolean isShowingAndNotHidden() {
+ return mKeyguardViewMediator.isShowingAndNotHidden();
+ }
+ public boolean isInputRestricted() {
+ return mKeyguardViewMediator.isInputRestricted();
+ }
+ public void verifyUnlock(IKeyguardExitCallback callback) {
+ mKeyguardViewMediator.verifyUnlock(callback);
+ }
+ public void keyguardDone(boolean authenticated, boolean wakeup) {
+ checkPermission();
+ mKeyguardViewMediator.keyguardDone(authenticated, wakeup);
+ }
+ public void setHidden(boolean isHidden) {
+ checkPermission();
+ mKeyguardViewMediator.setHidden(isHidden);
+ }
+ public void dismiss() {
+ mKeyguardViewMediator.dismiss();
+ }
+ public void onDreamingStarted() {
+ checkPermission();
+ mKeyguardViewMediator.onDreamingStarted();
+ }
+ public void onDreamingStopped() {
+ checkPermission();
+ mKeyguardViewMediator.onDreamingStopped();
+ }
+ public void onScreenTurnedOff(int reason) {
+ checkPermission();
+ mKeyguardViewMediator.onScreenTurnedOff(reason);
+ }
+ public void onScreenTurnedOn(IKeyguardShowCallback callback) {
+ checkPermission();
+ mKeyguardViewMediator.onScreenTurnedOn(callback);
+ }
+ public void setKeyguardEnabled(boolean enabled) {
+ checkPermission();
+ mKeyguardViewMediator.setKeyguardEnabled(enabled);
+ }
+ public boolean isDismissable() {
+ return mKeyguardViewMediator.isDismissable();
+ }
+ public void onSystemReady() {
+ checkPermission();
+ mKeyguardViewMediator.onSystemReady();
+ }
+ public void doKeyguardTimeout(Bundle options) {
+ checkPermission();
+ mKeyguardViewMediator.doKeyguardTimeout(options);
+ }
+ public void setCurrentUser(int userId) {
+ checkPermission();
+ mKeyguardViewMediator.setCurrentUser(userId);
+ }
+ public void showAssistant() {
+ checkPermission();
+ mKeyguardViewMediator.showAssistant();
+ }
+ };
+
+}
+
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
new file mode 100644
index 0000000..865a7c4
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import com.android.internal.telephony.ITelephony;
+
+import android.content.Context;
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.text.method.DigitsKeyListener;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView.OnEditorActionListener;
+
+/**
+ * Displays a PIN pad for unlocking.
+ */
+public class KeyguardSimPinView extends KeyguardAbsKeyInputView
+ implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+
+ private ProgressDialog mSimUnlockProgressDialog = null;
+ private volatile boolean mSimCheckInProgress;
+
+ public KeyguardSimPinView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardSimPinView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void resetState() {
+ mSecurityMessageDisplay.setMessage(R.string.kg_sim_pin_instructions, true);
+ mPasswordEntry.setEnabled(true);
+ }
+
+ @Override
+ protected int getPasswordTextViewId() {
+ return R.id.pinEntry;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ final View ok = findViewById(R.id.key_enter);
+ if (ok != null) {
+ ok.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doHapticKeyClick();
+ verifyPasswordAndUnlock();
+ }
+ });
+ }
+
+ // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
+ // not a separate view
+ View pinDelete = findViewById(R.id.delete_button);
+ if (pinDelete != null) {
+ pinDelete.setVisibility(View.VISIBLE);
+ pinDelete.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ CharSequence str = mPasswordEntry.getText();
+ if (str.length() > 0) {
+ mPasswordEntry.setText(str.subSequence(0, str.length()-1));
+ }
+ doHapticKeyClick();
+ }
+ });
+ pinDelete.setOnLongClickListener(new View.OnLongClickListener() {
+ public boolean onLongClick(View v) {
+ mPasswordEntry.setText("");
+ doHapticKeyClick();
+ return true;
+ }
+ });
+ }
+
+ mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
+ | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+
+ mPasswordEntry.requestFocus();
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
+ @Override
+ public void onPause() {
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPin extends Thread {
+ private final String mPin;
+
+ protected CheckSimPin(String pin) {
+ mPin = pin;
+ }
+
+ abstract void onSimCheckResponse(boolean success);
+
+ @Override
+ public void run() {
+ try {
+ final boolean result = ITelephony.Stub.asInterface(ServiceManager
+ .checkService("phone")).supplyPin(mPin);
+ post(new Runnable() {
+ public void run() {
+ onSimCheckResponse(result);
+ }
+ });
+ } catch (RemoteException e) {
+ post(new Runnable() {
+ public void run() {
+ onSimCheckResponse(false);
+ }
+ });
+ }
+ }
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mContext);
+ mSimUnlockProgressDialog.setMessage(
+ mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ if (!(mContext instanceof Activity)) {
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ String entry = mPasswordEntry.getText().toString();
+
+ if (entry.length() < 4) {
+ // otherwise, display a message to the user, and don't submit.
+ mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint, true);
+ mPasswordEntry.setText("");
+ mCallback.userActivity(0);
+ return;
+ }
+
+ getSimUnlockProgressDialog().show();
+
+ if (!mSimCheckInProgress) {
+ mSimCheckInProgress = true; // there should be only one
+ new CheckSimPin(mPasswordEntry.getText().toString()) {
+ void onSimCheckResponse(final boolean success) {
+ post(new Runnable() {
+ public void run() {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ if (success) {
+ // before closing the keyguard, report back that the sim is unlocked
+ // so it knows right away.
+ KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
+ mCallback.dismiss(true);
+ } else {
+ mSecurityMessageDisplay.setMessage
+ (R.string.kg_password_wrong_pin_code, true);
+ mPasswordEntry.setText("");
+ }
+ mCallback.userActivity(0);
+ mSimCheckInProgress = false;
+ }
+ });
+ }
+ }.start();
+ }
+ }
+}
+
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
new file mode 100644
index 0000000..7424fab
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import com.android.internal.telephony.ITelephony;
+
+import android.content.Context;
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.text.method.DigitsKeyListener;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView.OnEditorActionListener;
+
+/**
+ * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
+ */
+public class KeyguardSimPukView extends KeyguardAbsKeyInputView
+ implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+
+ private ProgressDialog mSimUnlockProgressDialog = null;
+ private volatile boolean mCheckInProgress;
+ private String mPukText;
+ private String mPinText;
+ private StateMachine mStateMachine = new StateMachine();
+
+ private class StateMachine {
+ final int ENTER_PUK = 0;
+ final int ENTER_PIN = 1;
+ final int CONFIRM_PIN = 2;
+ final int DONE = 3;
+ private int state = ENTER_PUK;
+
+ public void next() {
+ int msg = 0;
+ if (state == ENTER_PUK) {
+ if (checkPuk()) {
+ state = ENTER_PIN;
+ msg = R.string.kg_puk_enter_pin_hint;
+ } else {
+ msg = R.string.kg_invalid_sim_puk_hint;
+ }
+ } else if (state == ENTER_PIN) {
+ if (checkPin()) {
+ state = CONFIRM_PIN;
+ msg = R.string.kg_enter_confirm_pin_hint;
+ } else {
+ msg = R.string.kg_invalid_sim_pin_hint;
+ }
+ } else if (state == CONFIRM_PIN) {
+ if (confirmPin()) {
+ state = DONE;
+ msg = R.string.keyguard_sim_unlock_progress_dialog_message;
+ updateSim();
+ } else {
+ state = ENTER_PIN; // try again?
+ msg = R.string.kg_invalid_confirm_pin_hint;
+ }
+ }
+ mPasswordEntry.setText(null);
+ if (msg != 0) {
+ mSecurityMessageDisplay.setMessage(msg, true);
+ }
+ }
+
+ void reset() {
+ mPinText="";
+ mPukText="";
+ state = ENTER_PUK;
+ mSecurityMessageDisplay.setMessage(R.string.kg_puk_enter_puk_hint, true);
+ mPasswordEntry.requestFocus();
+ }
+ }
+
+ public KeyguardSimPukView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardSimPukView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void resetState() {
+ mStateMachine.reset();
+ mPasswordEntry.setEnabled(true);
+ }
+
+ @Override
+ protected int getPasswordTextViewId() {
+ return R.id.pinEntry;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ final View ok = findViewById(R.id.key_enter);
+ if (ok != null) {
+ ok.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doHapticKeyClick();
+ verifyPasswordAndUnlock();
+ }
+ });
+ }
+
+ // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
+ // not a separate view
+ View pinDelete = findViewById(R.id.delete_button);
+ if (pinDelete != null) {
+ pinDelete.setVisibility(View.VISIBLE);
+ pinDelete.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ CharSequence str = mPasswordEntry.getText();
+ if (str.length() > 0) {
+ mPasswordEntry.setText(str.subSequence(0, str.length()-1));
+ }
+ doHapticKeyClick();
+ }
+ });
+ pinDelete.setOnLongClickListener(new View.OnLongClickListener() {
+ public boolean onLongClick(View v) {
+ mPasswordEntry.setText("");
+ doHapticKeyClick();
+ return true;
+ }
+ });
+ }
+
+ mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
+ | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+
+ mPasswordEntry.requestFocus();
+
+ mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
+ @Override
+ public void onPause() {
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPuk extends Thread {
+
+ private final String mPin, mPuk;
+
+ protected CheckSimPuk(String puk, String pin) {
+ mPuk = puk;
+ mPin = pin;
+ }
+
+ abstract void onSimLockChangedResponse(boolean success);
+
+ @Override
+ public void run() {
+ try {
+ final boolean result = ITelephony.Stub.asInterface(ServiceManager
+ .checkService("phone")).supplyPuk(mPuk, mPin);
+
+ post(new Runnable() {
+ public void run() {
+ onSimLockChangedResponse(result);
+ }
+ });
+ } catch (RemoteException e) {
+ post(new Runnable() {
+ public void run() {
+ onSimLockChangedResponse(false);
+ }
+ });
+ }
+ }
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mContext);
+ mSimUnlockProgressDialog.setMessage(
+ mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ if (!(mContext instanceof Activity)) {
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+ private boolean checkPuk() {
+ // make sure the puk is at least 8 digits long.
+ if (mPasswordEntry.getText().length() >= 8) {
+ mPukText = mPasswordEntry.getText().toString();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean checkPin() {
+ // make sure the PIN is between 4 and 8 digits
+ int length = mPasswordEntry.getText().length();
+ if (length >= 4 && length <= 8) {
+ mPinText = mPasswordEntry.getText().toString();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean confirmPin() {
+ return mPinText.equals(mPasswordEntry.getText().toString());
+ }
+
+ private void updateSim() {
+ getSimUnlockProgressDialog().show();
+
+ if (!mCheckInProgress) {
+ mCheckInProgress = true;
+ new CheckSimPuk(mPukText, mPinText) {
+ void onSimLockChangedResponse(final boolean success) {
+ post(new Runnable() {
+ public void run() {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ if (success) {
+ mCallback.dismiss(true);
+ } else {
+ mStateMachine.reset();
+ mSecurityMessageDisplay.setMessage(R.string.kg_invalid_puk, true);
+ }
+ mCheckInProgress = false;
+ }
+ });
+ }
+ }.start();
+ }
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ mStateMachine.next();
+ }
+}
+
+
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
new file mode 100644
index 0000000..29f76f3
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.GridLayout;
+import android.widget.TextView;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import libcore.icu.ICU;
+
+public class KeyguardStatusView extends GridLayout {
+ private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
+ private static final String TAG = "KeyguardStatusView";
+
+ public static final int LOCK_ICON = 0; // R.drawable.ic_lock_idle_lock;
+ public static final int ALARM_ICON = R.drawable.ic_lock_idle_alarm;
+ public static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
+ public static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
+
+ private SimpleDateFormat mDateFormat;
+ private LockPatternUtils mLockPatternUtils;
+
+ private TextView mDateView;
+ private TextView mAlarmStatusView;
+ private ClockView mClockView;
+
+ private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+
+ @Override
+ public void onTimeChanged() {
+ refresh();
+ }
+
+ @Override
+ void onKeyguardVisibilityChanged(boolean showing) {
+ if (showing) {
+ if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
+ refresh();
+ }
+ };
+ };
+
+ public KeyguardStatusView(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardStatusView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardStatusView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ Resources res = getContext().getResources();
+ final Locale locale = Locale.getDefault();
+ final String datePattern = res.getString(R.string.system_ui_date_pattern);
+ final String bestFormat = ICU.getBestDateTimePattern(datePattern, locale.toString());
+ mDateFormat = new SimpleDateFormat(bestFormat, locale);
+ mDateView = (TextView) findViewById(R.id.date);
+ mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
+ mClockView = (ClockView) findViewById(R.id.clock_view);
+ mLockPatternUtils = new LockPatternUtils(getContext());
+
+ // Use custom font in mDateView
+ mDateView.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD);
+
+ // Required to get Marquee to work.
+ final View marqueeViews[] = { mDateView, mAlarmStatusView };
+ for (int i = 0; i < marqueeViews.length; i++) {
+ View v = marqueeViews[i];
+ if (v == null) {
+ throw new RuntimeException("Can't find widget at index " + i);
+ }
+ v.setSelected(true);
+ }
+ refresh();
+ }
+
+ protected void refresh() {
+ mClockView.updateTime();
+ refreshDate();
+ refreshAlarmStatus(); // might as well
+ }
+
+ void refreshAlarmStatus() {
+ // Update Alarm status
+ String nextAlarm = mLockPatternUtils.getNextAlarm();
+ if (!TextUtils.isEmpty(nextAlarm)) {
+ maybeSetUpperCaseText(mAlarmStatusView, nextAlarm);
+ mAlarmStatusView.setCompoundDrawablesWithIntrinsicBounds(ALARM_ICON, 0, 0, 0);
+ mAlarmStatusView.setVisibility(View.VISIBLE);
+ } else {
+ mAlarmStatusView.setVisibility(View.GONE);
+ }
+ }
+
+ void refreshDate() {
+ maybeSetUpperCaseText(mDateView, mDateFormat.format(new Date()));
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
+ }
+
+ public int getAppWidgetId() {
+ return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
+ }
+
+ private void maybeSetUpperCaseText(TextView textView, CharSequence text) {
+ if (KeyguardViewManager.USE_UPPER_CASE) {
+ textView.setText(text != null ? text.toString().toUpperCase() : null);
+ } else {
+ textView.setText(text);
+ }
+ }
+}
diff --git a/media/libdrm/MODULE_LICENSE_APACHE2 b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusViewManager.java
similarity index 100%
copy from media/libdrm/MODULE_LICENSE_APACHE2
copy to packages/Keyguard/src/com/android/keyguard/KeyguardStatusViewManager.java
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
new file mode 100644
index 0000000..3208aff
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.media.AudioManager;
+import android.media.IRemoteControlDisplay;
+import android.media.MediaMetadataRetriever;
+import android.media.RemoteControlClient;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.lang.ref.WeakReference;
+/**
+ * This is the widget responsible for showing music controls in keyguard.
+ */
+public class KeyguardTransportControlView extends FrameLayout implements OnClickListener {
+
+ private static final int MSG_UPDATE_STATE = 100;
+ private static final int MSG_SET_METADATA = 101;
+ private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
+ private static final int MSG_SET_ARTWORK = 103;
+ private static final int MSG_SET_GENERATION_ID = 104;
+ private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
+ protected static final boolean DEBUG = false;
+ protected static final String TAG = "TransportControlView";
+
+ private ImageView mAlbumArt;
+ private TextView mTrackTitle;
+ private ImageView mBtnPrev;
+ private ImageView mBtnPlay;
+ private ImageView mBtnNext;
+ private int mClientGeneration;
+ private Metadata mMetadata = new Metadata();
+ private boolean mAttached;
+ private PendingIntent mClientIntent;
+ private int mTransportControlFlags;
+ private int mCurrentPlayState;
+ private AudioManager mAudioManager;
+ private IRemoteControlDisplayWeak mIRCD;
+
+ /**
+ * The metadata which should be populated into the view once we've been attached
+ */
+ private Bundle mPopulateMetadataWhenAttached = null;
+
+ // This handler is required to ensure messages from IRCD are handled in sequence and on
+ // the UI thread.
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_STATE:
+ if (mClientGeneration == msg.arg1) updatePlayPauseState(msg.arg2);
+ break;
+
+ case MSG_SET_METADATA:
+ if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj);
+ break;
+
+ case MSG_SET_TRANSPORT_CONTROLS:
+ if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2);
+ break;
+
+ case MSG_SET_ARTWORK:
+ if (mClientGeneration == msg.arg1) {
+ if (mMetadata.bitmap != null) {
+ mMetadata.bitmap.recycle();
+ }
+ mMetadata.bitmap = (Bitmap) msg.obj;
+ mAlbumArt.setImageBitmap(mMetadata.bitmap);
+ }
+ break;
+
+ case MSG_SET_GENERATION_ID:
+ if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
+ mClientGeneration = msg.arg1;
+ mClientIntent = (PendingIntent) msg.obj;
+ break;
+
+ }
+ }
+ };
+
+ /**
+ * This class is required to have weak linkage to the current TransportControlView
+ * because the remote process can hold a strong reference to this binder object and
+ * we can't predict when it will be GC'd in the remote process. Without this code, it
+ * would allow a heavyweight object to be held on this side of the binder when there's
+ * no requirement to run a GC on the other side.
+ */
+ private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub {
+ private WeakReference<Handler> mLocalHandler;
+
+ IRemoteControlDisplayWeak(Handler handler) {
+ mLocalHandler = new WeakReference<Handler>(handler);
+ }
+
+ public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+ long currentPosMs, float speed) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
+ }
+ }
+
+ public void setMetadata(int generationId, Bundle metadata) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+ }
+ }
+
+ public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags)
+ .sendToTarget();
+ }
+ }
+
+ public void setArtwork(int generationId, Bitmap bitmap) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
+ }
+ }
+
+ public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+ handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
+ }
+ }
+
+ public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
+ boolean clearing) throws RemoteException {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_GENERATION_ID,
+ clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
+ }
+ }
+ };
+
+ public KeyguardTransportControlView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ if (DEBUG) Log.v(TAG, "Create TCV " + this);
+ mAudioManager = new AudioManager(mContext);
+ mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
+ mIRCD = new IRemoteControlDisplayWeak(mHandler);
+ }
+
+ private void updateTransportControls(int transportControlFlags) {
+ mTransportControlFlags = transportControlFlags;
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ mTrackTitle = (TextView) findViewById(R.id.title);
+ mTrackTitle.setSelected(true); // enable marquee
+ mAlbumArt = (ImageView) findViewById(R.id.albumart);
+ mBtnPrev = (ImageView) findViewById(R.id.btn_prev);
+ mBtnPlay = (ImageView) findViewById(R.id.btn_play);
+ mBtnNext = (ImageView) findViewById(R.id.btn_next);
+ final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext };
+ for (View view : buttons) {
+ view.setOnClickListener(this);
+ }
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (DEBUG) Log.v(TAG, "onAttachToWindow()");
+ if (mPopulateMetadataWhenAttached != null) {
+ updateMetadata(mPopulateMetadataWhenAttached);
+ mPopulateMetadataWhenAttached = null;
+ }
+ if (!mAttached) {
+ if (DEBUG) Log.v(TAG, "Registering TCV " + this);
+ mAudioManager.registerRemoteControlDisplay(mIRCD);
+ }
+ mAttached = true;
+ }
+
+ @Override
+ protected void onSizeChanged (int w, int h, int oldw, int oldh) {
+ if (mAttached) {
+ int dim = Math.min(512, Math.max(w, h));
+ if (DEBUG) Log.v(TAG, "TCV uses bitmap size=" + dim);
+ mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
+ }
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ if (DEBUG) Log.v(TAG, "onDetachFromWindow()");
+ super.onDetachedFromWindow();
+ if (mAttached) {
+ if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
+ mAudioManager.unregisterRemoteControlDisplay(mIRCD);
+ }
+ mAttached = false;
+ }
+
+ class Metadata {
+ private String artist;
+ private String trackTitle;
+ private String albumTitle;
+ private Bitmap bitmap;
+
+ public String toString() {
+ return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + albumTitle + "]";
+ }
+ }
+
+ private String getMdString(Bundle data, int id) {
+ return data.getString(Integer.toString(id));
+ }
+
+ private void updateMetadata(Bundle data) {
+ if (mAttached) {
+ mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
+ mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
+ mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
+ populateMetadata();
+ } else {
+ mPopulateMetadataWhenAttached = data;
+ }
+ }
+
+ /**
+ * Populates the given metadata into the view
+ */
+ private void populateMetadata() {
+ StringBuilder sb = new StringBuilder();
+ int trackTitleLength = 0;
+ if (!TextUtils.isEmpty(mMetadata.trackTitle)) {
+ sb.append(mMetadata.trackTitle);
+ trackTitleLength = mMetadata.trackTitle.length();
+ }
+ if (!TextUtils.isEmpty(mMetadata.artist)) {
+ if (sb.length() != 0) {
+ sb.append(" - ");
+ }
+ sb.append(mMetadata.artist);
+ }
+ if (!TextUtils.isEmpty(mMetadata.albumTitle)) {
+ if (sb.length() != 0) {
+ sb.append(" - ");
+ }
+ sb.append(mMetadata.albumTitle);
+ }
+ mTrackTitle.setText(sb.toString(), TextView.BufferType.SPANNABLE);
+ Spannable str = (Spannable) mTrackTitle.getText();
+ if (trackTitleLength != 0) {
+ str.setSpan(new ForegroundColorSpan(0xffffffff), 0, trackTitleLength,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ trackTitleLength++;
+ }
+ if (sb.length() > trackTitleLength) {
+ str.setSpan(new ForegroundColorSpan(0x7fffffff), trackTitleLength, sb.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ mAlbumArt.setImageBitmap(mMetadata.bitmap);
+ final int flags = mTransportControlFlags;
+ setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
+ setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
+ setVisibilityBasedOnFlag(mBtnPlay, flags,
+ RemoteControlClient.FLAG_KEY_MEDIA_PLAY
+ | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
+ | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
+ | RemoteControlClient.FLAG_KEY_MEDIA_STOP);
+
+ updatePlayPauseState(mCurrentPlayState);
+ }
+
+ private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
+ if ((flags & flag) != 0) {
+ view.setVisibility(View.VISIBLE);
+ } else {
+ view.setVisibility(View.GONE);
+ }
+ }
+
+ private void updatePlayPauseState(int state) {
+ if (DEBUG) Log.v(TAG,
+ "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state);
+ if (state == mCurrentPlayState) {
+ return;
+ }
+ final int imageResId;
+ final int imageDescId;
+ switch (state) {
+ case RemoteControlClient.PLAYSTATE_ERROR:
+ imageResId = R.drawable.stat_sys_warning;
+ // TODO use more specific image description string for warning, but here the "play"
+ // message is still valid because this button triggers a play command.
+ imageDescId = R.string.keyguard_transport_play_description;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ imageResId = R.drawable.ic_media_pause;
+ imageDescId = R.string.keyguard_transport_pause_description;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ imageResId = R.drawable.ic_media_stop;
+ imageDescId = R.string.keyguard_transport_stop_description;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ default:
+ imageResId = R.drawable.ic_media_play;
+ imageDescId = R.string.keyguard_transport_play_description;
+ break;
+ }
+ mBtnPlay.setImageResource(imageResId);
+ mBtnPlay.setContentDescription(getResources().getString(imageDescId));
+ mCurrentPlayState = state;
+ }
+
+ static class SavedState extends BaseSavedState {
+ boolean clientPresent;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ this.clientPresent = in.readInt() != 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(this.clientPresent ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ public void onClick(View v) {
+ int keyCode = -1;
+ if (v == mBtnPrev) {
+ keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+ } else if (v == mBtnNext) {
+ keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
+ } else if (v == mBtnPlay) {
+ keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
+
+ }
+ if (keyCode != -1) {
+ sendMediaButtonClick(keyCode);
+ }
+ }
+
+ private void sendMediaButtonClick(int keyCode) {
+ if (mClientIntent == null) {
+ // Shouldn't be possible because this view should be hidden in this case.
+ Log.e(TAG, "sendMediaButtonClick(): No client is currently registered");
+ return;
+ }
+ // use the registered PendingIntent that will be processed by the registered
+ // media button event receiver, which is the component of mClientIntent
+ KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
+ Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ try {
+ mClientIntent.send(getContext(), 0, intent);
+ } catch (CanceledException e) {
+ Log.e(TAG, "Error sending intent for media button down: "+e);
+ e.printStackTrace();
+ }
+
+ keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
+ intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ try {
+ mClientIntent.send(getContext(), 0, intent);
+ } catch (CanceledException e) {
+ Log.e(TAG, "Error sending intent for media button up: "+e);
+ e.printStackTrace();
+ }
+ }
+
+ public boolean providesClock() {
+ return false;
+ }
+
+ private boolean wasPlayingRecently(int state, long stateChangeTimeMs) {
+ switch (state) {
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+ case RemoteControlClient.PLAYSTATE_REWINDING:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ // actively playing or about to play
+ return true;
+ case RemoteControlClient.PLAYSTATE_NONE:
+ return false;
+ case RemoteControlClient.PLAYSTATE_STOPPED:
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ case RemoteControlClient.PLAYSTATE_ERROR:
+ // we have stopped playing, check how long ago
+ if (DEBUG) {
+ if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) {
+ Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently");
+ } else {
+ Log.v(TAG, "wasPlayingRecently: time > TIMEOUT");
+ }
+ }
+ return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS);
+ default:
+ Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()");
+ return false;
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
new file mode 100644
index 0000000..dcec654
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -0,0 +1,982 @@
+/*
+ * 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.
+ */
+
+package com.android.keyguard;
+
+import android.app.ActivityManagerNative;
+import android.app.IUserSwitchObserver;
+import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.graphics.Bitmap;
+
+import static android.os.BatteryManager.BATTERY_STATUS_FULL;
+import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
+import static android.os.BatteryManager.EXTRA_STATUS;
+import static android.os.BatteryManager.EXTRA_PLUGGED;
+import static android.os.BatteryManager.EXTRA_LEVEL;
+import static android.os.BatteryManager.EXTRA_HEALTH;
+import android.media.AudioManager;
+import android.media.IRemoteControlDisplay;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.TelephonyIntents;
+
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import com.google.android.collect.Lists;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Watches for updates that may be interesting to the keyguard, and provides
+ * the up to date information as well as a registration for callbacks that care
+ * to be updated.
+ *
+ * Note: under time crunch, this has been extended to include some stuff that
+ * doesn't really belong here. see {@link #handleBatteryUpdate} where it shutdowns
+ * the device, and {@link #getFailedUnlockAttempts()}, {@link #reportFailedAttempt()}
+ * and {@link #clearFailedUnlockAttempts()}. Maybe we should rename this 'KeyguardContext'...
+ */
+public class KeyguardUpdateMonitor {
+
+ private static final String TAG = "KeyguardUpdateMonitor";
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_SIM_STATES = DEBUG || false;
+ private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 3;
+ private static final int LOW_BATTERY_THRESHOLD = 20;
+
+ // Callback messages
+ private static final int MSG_TIME_UPDATE = 301;
+ private static final int MSG_BATTERY_UPDATE = 302;
+ private static final int MSG_CARRIER_INFO_UPDATE = 303;
+ private static final int MSG_SIM_STATE_CHANGE = 304;
+ private static final int MSG_RINGER_MODE_CHANGED = 305;
+ private static final int MSG_PHONE_STATE_CHANGED = 306;
+ private static final int MSG_CLOCK_VISIBILITY_CHANGED = 307;
+ private static final int MSG_DEVICE_PROVISIONED = 308;
+ private static final int MSG_DPM_STATE_CHANGED = 309;
+ private static final int MSG_USER_SWITCHING = 310;
+ private static final int MSG_USER_REMOVED = 311;
+ private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
+ protected static final int MSG_BOOT_COMPLETED = 313;
+ private static final int MSG_USER_SWITCH_COMPLETE = 314;
+ private static final int MSG_SET_CURRENT_CLIENT_ID = 315;
+ protected static final int MSG_SET_PLAYBACK_STATE = 316;
+ protected static final int MSG_USER_INFO_CHANGED = 317;
+
+
+ private static KeyguardUpdateMonitor sInstance;
+
+ private final Context mContext;
+
+ // Telephony state
+ private IccCardConstants.State mSimState = IccCardConstants.State.READY;
+ private CharSequence mTelephonyPlmn;
+ private CharSequence mTelephonySpn;
+ private int mRingMode;
+ private int mPhoneState;
+ private boolean mKeyguardIsVisible;
+ private boolean mBootCompleted;
+
+ // Device provisioning state
+ private boolean mDeviceProvisioned;
+
+ // Battery status
+ private BatteryStatus mBatteryStatus;
+
+ // Password attempts
+ private int mFailedAttempts = 0;
+ private int mFailedBiometricUnlockAttempts = 0;
+
+ private boolean mAlternateUnlockEnabled;
+
+ private boolean mClockVisible;
+
+ private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
+ mCallbacks = Lists.newArrayList();
+ private ContentObserver mDeviceProvisionedObserver;
+
+ private boolean mSwitchingUser;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TIME_UPDATE:
+ handleTimeUpdate();
+ break;
+ case MSG_BATTERY_UPDATE:
+ handleBatteryUpdate((BatteryStatus) msg.obj);
+ break;
+ case MSG_CARRIER_INFO_UPDATE:
+ handleCarrierInfoUpdate();
+ break;
+ case MSG_SIM_STATE_CHANGE:
+ handleSimStateChange((SimArgs) msg.obj);
+ break;
+ case MSG_RINGER_MODE_CHANGED:
+ handleRingerModeChange(msg.arg1);
+ break;
+ case MSG_PHONE_STATE_CHANGED:
+ handlePhoneStateChanged((String)msg.obj);
+ break;
+ case MSG_CLOCK_VISIBILITY_CHANGED:
+ handleClockVisibilityChanged();
+ break;
+ case MSG_DEVICE_PROVISIONED:
+ handleDeviceProvisioned();
+ break;
+ case MSG_DPM_STATE_CHANGED:
+ handleDevicePolicyManagerStateChanged();
+ break;
+ case MSG_USER_SWITCHING:
+ handleUserSwitching(msg.arg1, (IRemoteCallback)msg.obj);
+ break;
+ case MSG_USER_SWITCH_COMPLETE:
+ handleUserSwitchComplete(msg.arg1);
+ break;
+ case MSG_USER_REMOVED:
+ handleUserRemoved(msg.arg1);
+ break;
+ case MSG_KEYGUARD_VISIBILITY_CHANGED:
+ handleKeyguardVisibilityChanged(msg.arg1);
+ break;
+ case MSG_BOOT_COMPLETED:
+ handleBootCompleted();
+ break;
+ case MSG_SET_CURRENT_CLIENT_ID:
+ handleSetGenerationId(msg.arg1, msg.arg2 != 0, (PendingIntent) msg.obj);
+ break;
+ case MSG_SET_PLAYBACK_STATE:
+ handleSetPlaybackState(msg.arg1, msg.arg2, (Long) msg.obj);
+ break;
+ case MSG_USER_INFO_CHANGED:
+ handleUserInfoChanged(msg.arg1);
+ break;
+ }
+ }
+ };
+
+ private AudioManager mAudioManager;
+
+ static class DisplayClientState {
+ public int clientGeneration;
+ public boolean clearing;
+ public PendingIntent intent;
+ public int playbackState;
+ public long playbackEventTime;
+ }
+
+ private DisplayClientState mDisplayClientState = new DisplayClientState();
+
+ /**
+ * This currently implements the bare minimum required to enable showing and hiding
+ * KeyguardTransportControl. There's a lot of client state to maintain which is why
+ * KeyguardTransportControl maintains an independent connection while it's showing.
+ */
+ private final IRemoteControlDisplay.Stub mRemoteControlDisplay =
+ new IRemoteControlDisplay.Stub() {
+
+ public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+ long currentPosMs, float speed) {
+ Message msg = mHandler.obtainMessage(MSG_SET_PLAYBACK_STATE,
+ generationId, state, stateChangeTimeMs);
+ mHandler.sendMessage(msg);
+ }
+
+ public void setMetadata(int generationId, Bundle metadata) {
+
+ }
+
+ public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
+
+ }
+
+ public void setArtwork(int generationId, Bitmap bitmap) {
+
+ }
+
+ public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
+
+ }
+
+ public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
+ boolean clearing) throws RemoteException {
+ Message msg = mHandler.obtainMessage(MSG_SET_CURRENT_CLIENT_ID,
+ clientGeneration, (clearing ? 1 : 0), mediaIntent);
+ mHandler.sendMessage(msg);
+ }
+ };
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (DEBUG) Log.d(TAG, "received broadcast " + action);
+
+ if (Intent.ACTION_TIME_TICK.equals(action)
+ || Intent.ACTION_TIME_CHANGED.equals(action)
+ || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_TIME_UPDATE));
+ } else if (TelephonyIntents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
+ mTelephonyPlmn = getTelephonyPlmnFrom(intent);
+ mTelephonySpn = getTelephonySpnFrom(intent);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_CARRIER_INFO_UPDATE));
+ } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+ final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
+ final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
+ final int level = intent.getIntExtra(EXTRA_LEVEL, 0);
+ final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
+ final Message msg = mHandler.obtainMessage(
+ MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health));
+ mHandler.sendMessage(msg);
+ } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
+ if (DEBUG_SIM_STATES) {
+ Log.v(TAG, "action " + action + " state" +
+ intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE));
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MSG_SIM_STATE_CHANGE, SimArgs.fromIntent(intent)));
+ } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED,
+ intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
+ } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
+ String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
+ } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
+ .equals(action)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED));
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED,
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
+ } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_BOOT_COMPLETED));
+ }
+ }
+ };
+
+ private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
+
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_INFO_CHANGED,
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()), 0));
+ }
+ }
+ };
+
+ /**
+ * When we receive a
+ * {@link com.android.internal.telephony.TelephonyIntents#ACTION_SIM_STATE_CHANGED} broadcast,
+ * and then pass a result via our handler to {@link KeyguardUpdateMonitor#handleSimStateChange},
+ * we need a single object to pass to the handler. This class helps decode
+ * the intent and provide a {@link SimCard.State} result.
+ */
+ private static class SimArgs {
+ public final IccCardConstants.State simState;
+
+ SimArgs(IccCardConstants.State state) {
+ simState = state;
+ }
+
+ static SimArgs fromIntent(Intent intent) {
+ IccCardConstants.State state;
+ if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
+ throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
+ }
+ String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+ if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+ final String absentReason = intent
+ .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
+
+ if (IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED.equals(
+ absentReason)) {
+ state = IccCardConstants.State.PERM_DISABLED;
+ } else {
+ state = IccCardConstants.State.ABSENT;
+ }
+ } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+ state = IccCardConstants.State.READY;
+ } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+ final String lockedReason = intent
+ .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
+ if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+ state = IccCardConstants.State.PIN_REQUIRED;
+ } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+ state = IccCardConstants.State.PUK_REQUIRED;
+ } else {
+ state = IccCardConstants.State.UNKNOWN;
+ }
+ } else if (IccCardConstants.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) {
+ state = IccCardConstants.State.NETWORK_LOCKED;
+ } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(stateExtra)
+ || IccCardConstants.INTENT_VALUE_ICC_IMSI.equals(stateExtra)) {
+ // This is required because telephony doesn't return to "READY" after
+ // these state transitions. See bug 7197471.
+ state = IccCardConstants.State.READY;
+ } else {
+ state = IccCardConstants.State.UNKNOWN;
+ }
+ return new SimArgs(state);
+ }
+
+ public String toString() {
+ return simState.toString();
+ }
+ }
+
+ /* package */ static class BatteryStatus {
+ public final int status;
+ public final int level;
+ public final int plugged;
+ public final int health;
+ public BatteryStatus(int status, int level, int plugged, int health) {
+ this.status = status;
+ this.level = level;
+ this.plugged = plugged;
+ this.health = health;
+ }
+
+ /**
+ * Determine whether the device is plugged in (USB, power, or wireless).
+ * @return true if the device is plugged in.
+ */
+ boolean isPluggedIn() {
+ return plugged == BatteryManager.BATTERY_PLUGGED_AC
+ || plugged == BatteryManager.BATTERY_PLUGGED_USB
+ || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ }
+
+ /**
+ * Whether or not the device is charged. Note that some devices never return 100% for
+ * battery level, so this allows either battery level or status to determine if the
+ * battery is charged.
+ * @return true if the device is charged
+ */
+ public boolean isCharged() {
+ return status == BATTERY_STATUS_FULL || level >= 100;
+ }
+
+ /**
+ * Whether battery is low and needs to be charged.
+ * @return true if battery is low
+ */
+ public boolean isBatteryLow() {
+ return level < LOW_BATTERY_THRESHOLD;
+ }
+
+ }
+
+ public static KeyguardUpdateMonitor getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new KeyguardUpdateMonitor(context);
+ }
+ return sInstance;
+ }
+
+ protected void handleSetGenerationId(int clientGeneration, boolean clearing, PendingIntent p) {
+ mDisplayClientState.clientGeneration = clientGeneration;
+ mDisplayClientState.clearing = clearing;
+ mDisplayClientState.intent = p;
+ if (DEBUG)
+ Log.v(TAG, "handleSetGenerationId(g=" + clientGeneration + ", clear=" + clearing + ")");
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onMusicClientIdChanged(clientGeneration, clearing, p);
+ }
+ }
+ }
+
+ protected void handleSetPlaybackState(int generationId, int playbackState, long eventTime) {
+ if (DEBUG)
+ Log.v(TAG, "handleSetPlaybackState(gen=" + generationId
+ + ", state=" + playbackState + ", t=" + eventTime + ")");
+ mDisplayClientState.playbackState = playbackState;
+ mDisplayClientState.playbackEventTime = eventTime;
+ if (generationId == mDisplayClientState.clientGeneration) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onMusicPlaybackStateChanged(playbackState, eventTime);
+ }
+ }
+ } else {
+ Log.w(TAG, "Ignoring generation id " + generationId + " because it's not current");
+ }
+ }
+
+ private void handleUserInfoChanged(int userId) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onUserInfoChanged(userId);
+ }
+ }
+ }
+
+ private KeyguardUpdateMonitor(Context context) {
+ mContext = context;
+
+ mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
+ // Since device can't be un-provisioned, we only need to register a content observer
+ // to update mDeviceProvisioned when we are...
+ if (!mDeviceProvisioned) {
+ watchForDeviceProvisioning();
+ }
+
+ // Take a guess at initial SIM state, battery status and PLMN until we get an update
+ mSimState = IccCardConstants.State.NOT_READY;
+ mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0);
+ mTelephonyPlmn = getDefaultPlmn();
+
+ // Watch for interesting updates
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+ filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
+ filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ context.registerReceiver(mBroadcastReceiver, filter);
+
+ final IntentFilter bootCompleteFilter = new IntentFilter();
+ bootCompleteFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ bootCompleteFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
+ context.registerReceiver(mBroadcastReceiver, bootCompleteFilter);
+
+ final IntentFilter userInfoFilter = new IntentFilter(Intent.ACTION_USER_INFO_CHANGED);
+ context.registerReceiverAsUser(mBroadcastAllReceiver, UserHandle.ALL, userInfoFilter,
+ null, null);
+
+ try {
+ ActivityManagerNative.getDefault().registerUserSwitchObserver(
+ new IUserSwitchObserver.Stub() {
+ @Override
+ public void onUserSwitching(int newUserId, IRemoteCallback reply) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
+ newUserId, 0, reply));
+ mSwitchingUser = true;
+ }
+ @Override
+ public void onUserSwitchComplete(int newUserId) throws RemoteException {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE,
+ newUserId));
+ mSwitchingUser = false;
+ }
+ });
+ } catch (RemoteException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private boolean isDeviceProvisionedInSettingsDb() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ }
+
+ private void watchForDeviceProvisioning() {
+ mDeviceProvisionedObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
+ if (mDeviceProvisioned) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED));
+ }
+ if (DEBUG) Log.d(TAG, "DEVICE_PROVISIONED state = " + mDeviceProvisioned);
+ }
+ };
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mDeviceProvisionedObserver);
+
+ // prevent a race condition between where we check the flag and where we register the
+ // observer by grabbing the value once again...
+ boolean provisioned = isDeviceProvisionedInSettingsDb();
+ if (provisioned != mDeviceProvisioned) {
+ mDeviceProvisioned = provisioned;
+ if (mDeviceProvisioned) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED));
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_DPM_STATE_CHANGED}
+ */
+ protected void handleDevicePolicyManagerStateChanged() {
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onDevicePolicyManagerStateChanged();
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_USER_SWITCHING}
+ */
+ protected void handleUserSwitching(int userId, IRemoteCallback reply) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onUserSwitching(userId);
+ }
+ }
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_USER_SWITCH_COMPLETE}
+ */
+ protected void handleUserSwitchComplete(int userId) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onUserSwitchComplete(userId);
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_BOOT_COMPLETED}
+ */
+ protected void handleBootCompleted() {
+ mBootCompleted = true;
+ mAudioManager = new AudioManager(mContext);
+ mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBootCompleted();
+ }
+ }
+ }
+
+ /**
+ * We need to store this state in the KeyguardUpdateMonitor since this class will not be
+ * destroyed.
+ */
+ public boolean hasBootCompleted() {
+ return mBootCompleted;
+ }
+
+ /**
+ * Handle {@link #MSG_USER_REMOVED}
+ */
+ protected void handleUserRemoved(int userId) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onUserRemoved(userId);
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_DEVICE_PROVISIONED}
+ */
+ protected void handleDeviceProvisioned() {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onDeviceProvisioned();
+ }
+ }
+ if (mDeviceProvisionedObserver != null) {
+ // We don't need the observer anymore...
+ mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);
+ mDeviceProvisionedObserver = null;
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_PHONE_STATE_CHANGED}
+ */
+ protected void handlePhoneStateChanged(String newState) {
+ if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
+ if (TelephonyManager.EXTRA_STATE_IDLE.equals(newState)) {
+ mPhoneState = TelephonyManager.CALL_STATE_IDLE;
+ } else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(newState)) {
+ mPhoneState = TelephonyManager.CALL_STATE_OFFHOOK;
+ } else if (TelephonyManager.EXTRA_STATE_RINGING.equals(newState)) {
+ mPhoneState = TelephonyManager.CALL_STATE_RINGING;
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onPhoneStateChanged(mPhoneState);
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_RINGER_MODE_CHANGED}
+ */
+ protected void handleRingerModeChange(int mode) {
+ if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")");
+ mRingMode = mode;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onRingerModeChanged(mode);
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_TIME_UPDATE}
+ */
+ private void handleTimeUpdate() {
+ if (DEBUG) Log.d(TAG, "handleTimeUpdate");
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onTimeChanged();
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_BATTERY_UPDATE}
+ */
+ private void handleBatteryUpdate(BatteryStatus status) {
+ if (DEBUG) Log.d(TAG, "handleBatteryUpdate");
+ final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status);
+ mBatteryStatus = status;
+ if (batteryUpdateInteresting) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onRefreshBatteryInfo(status);
+ }
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_CARRIER_INFO_UPDATE}
+ */
+ private void handleCarrierInfoUpdate() {
+ if (DEBUG) Log.d(TAG, "handleCarrierInfoUpdate: plmn = " + mTelephonyPlmn
+ + ", spn = " + mTelephonySpn);
+
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_SIM_STATE_CHANGE}
+ */
+ private void handleSimStateChange(SimArgs simArgs) {
+ final IccCardConstants.State state = simArgs.simState;
+
+ if (DEBUG) {
+ Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " "
+ + "state resolved to " + state.toString());
+ }
+
+ if (state != IccCardConstants.State.UNKNOWN && state != mSimState) {
+ mSimState = state;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onSimStateChanged(state);
+ }
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_CLOCK_VISIBILITY_CHANGED}
+ */
+ private void handleClockVisibilityChanged() {
+ if (DEBUG) Log.d(TAG, "handleClockVisibilityChanged()");
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onClockVisibilityChanged();
+ }
+ }
+ }
+
+ /**
+ * Handle {@link #MSG_KEYGUARD_VISIBILITY_CHANGED}
+ */
+ private void handleKeyguardVisibilityChanged(int showing) {
+ if (DEBUG) Log.d(TAG, "handleKeyguardVisibilityChanged(" + showing + ")");
+ boolean isShowing = (showing == 1);
+ mKeyguardIsVisible = isShowing;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onKeyguardVisibilityChanged(isShowing);
+ }
+ }
+ }
+
+ public boolean isKeyguardVisible() {
+ return mKeyguardIsVisible;
+ }
+
+ public boolean isSwitchingUser() {
+ return mSwitchingUser;
+ }
+
+ private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
+ final boolean nowPluggedIn = current.isPluggedIn();
+ final boolean wasPluggedIn = old.isPluggedIn();
+ final boolean stateChangedWhilePluggedIn =
+ wasPluggedIn == true && nowPluggedIn == true
+ && (old.status != current.status);
+
+ // change in plug state is always interesting
+ if (wasPluggedIn != nowPluggedIn || stateChangedWhilePluggedIn) {
+ return true;
+ }
+
+ // change in battery level while plugged in
+ if (nowPluggedIn && old.level != current.level) {
+ return true;
+ }
+
+ // change where battery needs charging
+ if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param intent The intent with action {@link TelephonyIntents#SPN_STRINGS_UPDATED_ACTION}
+ * @return The string to use for the plmn, or null if it should not be shown.
+ */
+ private CharSequence getTelephonyPlmnFrom(Intent intent) {
+ if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
+ final String plmn = intent.getStringExtra(TelephonyIntents.EXTRA_PLMN);
+ return (plmn != null) ? plmn : getDefaultPlmn();
+ }
+ return null;
+ }
+
+ /**
+ * @return The default plmn (no service)
+ */
+ private CharSequence getDefaultPlmn() {
+ return mContext.getResources().getText(R.string.keyguard_carrier_default);
+ }
+
+ /**
+ * @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION}
+ * @return The string to use for the plmn, or null if it should not be shown.
+ */
+ private CharSequence getTelephonySpnFrom(Intent intent) {
+ if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
+ final String spn = intent.getStringExtra(TelephonyIntents.EXTRA_SPN);
+ if (spn != null) {
+ return spn;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Remove the given observer's callback.
+ *
+ * @param callback The callback to remove
+ */
+ public void removeCallback(KeyguardUpdateMonitorCallback callback) {
+ if (DEBUG) Log.v(TAG, "*** unregister callback for " + callback);
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ if (mCallbacks.get(i).get() == callback) {
+ mCallbacks.remove(i);
+ }
+ }
+ }
+
+ /**
+ * Register to receive notifications about general keyguard information
+ * (see {@link InfoCallback}.
+ * @param callback The callback to register
+ */
+ public void registerCallback(KeyguardUpdateMonitorCallback callback) {
+ if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
+ // Prevent adding duplicate callbacks
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ if (mCallbacks.get(i).get() == callback) {
+ if (DEBUG) Log.e(TAG, "Object tried to add another callback",
+ new Exception("Called by"));
+ return;
+ }
+ }
+ mCallbacks.add(new WeakReference<KeyguardUpdateMonitorCallback>(callback));
+ removeCallback(null); // remove unused references
+ sendUpdates(callback);
+ }
+
+ private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
+ // Notify listener of the current state
+ callback.onRefreshBatteryInfo(mBatteryStatus);
+ callback.onTimeChanged();
+ callback.onRingerModeChanged(mRingMode);
+ callback.onPhoneStateChanged(mPhoneState);
+ callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
+ callback.onClockVisibilityChanged();
+ callback.onSimStateChanged(mSimState);
+ callback.onMusicClientIdChanged(
+ mDisplayClientState.clientGeneration,
+ mDisplayClientState.clearing,
+ mDisplayClientState.intent);
+ callback.onMusicPlaybackStateChanged(mDisplayClientState.playbackState,
+ mDisplayClientState.playbackEventTime);
+ }
+
+ public void sendKeyguardVisibilityChanged(boolean showing) {
+ if (DEBUG) Log.d(TAG, "sendKeyguardVisibilityChanged(" + showing + ")");
+ Message message = mHandler.obtainMessage(MSG_KEYGUARD_VISIBILITY_CHANGED);
+ message.arg1 = showing ? 1 : 0;
+ message.sendToTarget();
+ }
+
+ public void reportClockVisible(boolean visible) {
+ mClockVisible = visible;
+ mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget();
+ }
+
+ public IccCardConstants.State getSimState() {
+ return mSimState;
+ }
+
+ /**
+ * Report that the user successfully entered the SIM PIN or PUK/SIM PIN so we
+ * have the information earlier than waiting for the intent
+ * broadcast from the telephony code.
+ *
+ * NOTE: Because handleSimStateChange() invokes callbacks immediately without going
+ * through mHandler, this *must* be called from the UI thread.
+ */
+ public void reportSimUnlocked() {
+ handleSimStateChange(new SimArgs(IccCardConstants.State.READY));
+ }
+
+ public CharSequence getTelephonyPlmn() {
+ return mTelephonyPlmn;
+ }
+
+ public CharSequence getTelephonySpn() {
+ return mTelephonySpn;
+ }
+
+ /**
+ * @return Whether the device is provisioned (whether they have gone through
+ * the setup wizard)
+ */
+ public boolean isDeviceProvisioned() {
+ return mDeviceProvisioned;
+ }
+
+ public int getFailedUnlockAttempts() {
+ return mFailedAttempts;
+ }
+
+ public void clearFailedUnlockAttempts() {
+ mFailedAttempts = 0;
+ mFailedBiometricUnlockAttempts = 0;
+ }
+
+ public void reportFailedUnlockAttempt() {
+ mFailedAttempts++;
+ }
+
+ public boolean isClockVisible() {
+ return mClockVisible;
+ }
+
+ public int getPhoneState() {
+ return mPhoneState;
+ }
+
+ public void reportFailedBiometricUnlockAttempt() {
+ mFailedBiometricUnlockAttempts++;
+ }
+
+ public boolean getMaxBiometricUnlockAttemptsReached() {
+ return mFailedBiometricUnlockAttempts >= FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP;
+ }
+
+ public boolean isAlternateUnlockEnabled() {
+ return mAlternateUnlockEnabled;
+ }
+
+ public void setAlternateUnlockEnabled(boolean enabled) {
+ mAlternateUnlockEnabled = enabled;
+ }
+
+ public boolean isSimLocked() {
+ return isSimLocked(mSimState);
+ }
+
+ public static boolean isSimLocked(IccCardConstants.State state) {
+ return state == IccCardConstants.State.PIN_REQUIRED
+ || state == IccCardConstants.State.PUK_REQUIRED
+ || state == IccCardConstants.State.PERM_DISABLED;
+ }
+
+ public boolean isSimPinSecure() {
+ return isSimPinSecure(mSimState);
+ }
+
+ public static boolean isSimPinSecure(IccCardConstants.State state) {
+ final IccCardConstants.State simState = state;
+ return (simState == IccCardConstants.State.PIN_REQUIRED
+ || simState == IccCardConstants.State.PUK_REQUIRED
+ || simState == IccCardConstants.State.PERM_DISABLED);
+ }
+
+ public DisplayClientState getCachedDisplayClientState() {
+ return mDisplayClientState;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
new file mode 100644
index 0000000..30b43f5
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.media.AudioManager;
+
+import com.android.internal.telephony.IccCardConstants;
+
+/**
+ * Callback for general information relevant to lock screen.
+ */
+class KeyguardUpdateMonitorCallback {
+ /**
+ * Called when the battery status changes, e.g. when plugged in or unplugged, charge
+ * level, etc. changes.
+ *
+ * @param status current battery status
+ */
+ void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { }
+
+ /**
+ * Called once per minute or when the time changes.
+ */
+ void onTimeChanged() { }
+
+ /**
+ * Called when the carrier PLMN or SPN changes.
+ *
+ * @param plmn The operator name of the registered network. May be null if it shouldn't
+ * be displayed.
+ * @param spn The service provider name. May be null if it shouldn't be displayed.
+ */
+ void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { }
+
+ /**
+ * Called when the ringer mode changes.
+ * @param state the current ringer state, as defined in
+ * {@link AudioManager#RINGER_MODE_CHANGED_ACTION}
+ */
+ void onRingerModeChanged(int state) { }
+
+ /**
+ * Called when the phone state changes. String will be one of:
+ * {@link TelephonyManager#EXTRA_STATE_IDLE}
+ * {@link TelephonyManager@EXTRA_STATE_RINGING}
+ * {@link TelephonyManager#EXTRA_STATE_OFFHOOK
+ */
+ void onPhoneStateChanged(int phoneState) { }
+
+ /**
+ * Called when the visibility of the keyguard changes.
+ * @param showing Indicates if the keyguard is now visible.
+ */
+ void onKeyguardVisibilityChanged(boolean showing) { }
+
+ /**
+ * Called when visibility of lockscreen clock changes, such as when
+ * obscured by a widget.
+ */
+ void onClockVisibilityChanged() { }
+
+ /**
+ * Called when the device becomes provisioned
+ */
+ void onDeviceProvisioned() { }
+
+ /**
+ * Called when the device policy changes.
+ * See {@link DevicePolicyManager#ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED}
+ */
+ void onDevicePolicyManagerStateChanged() { }
+
+ /**
+ * Called when the user change begins.
+ */
+ void onUserSwitching(int userId) { }
+
+ /**
+ * Called when the user change is complete.
+ */
+ void onUserSwitchComplete(int userId) { }
+
+ /**
+ * Called when the SIM state changes.
+ * @param simState
+ */
+ void onSimStateChanged(IccCardConstants.State simState) { }
+
+ /**
+ * Called when a user is removed.
+ */
+ void onUserRemoved(int userId) { }
+
+ /**
+ * Called when the user's info changed.
+ */
+ void onUserInfoChanged(int userId) { }
+
+ /**
+ * Called when boot completed.
+ *
+ * Note, this callback will only be received if boot complete occurs after registering with
+ * KeyguardUpdateMonitor.
+ */
+ void onBootCompleted() { }
+
+ /**
+ * Called when audio client attaches or detaches from AudioManager.
+ */
+ void onMusicClientIdChanged(int clientGeneration, boolean clearing, PendingIntent intent) { }
+
+ /**
+ * Called when the audio playback state changes.
+ * @param playbackState
+ * @param eventTime
+ */
+ public void onMusicPlaybackStateChanged(int playbackState, long eventTime) { }
+
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
new file mode 100644
index 0000000..714dfbd
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ */
+
+package com.android.keyguard;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.media.IAudioService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.view.KeyEvent;
+import android.widget.FrameLayout;
+
+/**
+ * Base class for keyguard view. {@link #reset} is where you should
+ * reset the state of your view. Use the {@link KeyguardViewCallback} via
+ * {@link #getCallback()} to send information back (such as poking the wake lock,
+ * or finishing the keyguard).
+ *
+ * Handles intercepting of media keys that still work when the keyguard is
+ * showing.
+ */
+public abstract class KeyguardViewBase extends FrameLayout {
+
+ private static final int BACKGROUND_COLOR = 0x70000000;
+ private AudioManager mAudioManager;
+ private TelephonyManager mTelephonyManager = null;
+ protected KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
+
+ // Whether the volume keys should be handled by keyguard. If true, then
+ // they will be handled here for specific media types such as music, otherwise
+ // the audio service will bring up the volume dialog.
+ private static final boolean KEYGUARD_MANAGES_VOLUME = true;
+
+ // This is a faster way to draw the background on devices without hardware acceleration
+ private static final Drawable mBackgroundDrawable = new Drawable() {
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+ };
+
+ public KeyguardViewBase(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardViewBase(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ resetBackground();
+ }
+
+ public void resetBackground() {
+ setBackground(mBackgroundDrawable);
+ }
+
+ /**
+ * Called when the screen turned off.
+ */
+ abstract public void onScreenTurnedOff();
+
+ /**
+ * Called when the screen turned on.
+ */
+ abstract public void onScreenTurnedOn();
+
+ /**
+ * Called when the view needs to be shown.
+ */
+ abstract public void show();
+
+ /**
+ * Verify that the user can get past the keyguard securely. This is called,
+ * for example, when the phone disables the keyguard but then wants to launch
+ * something else that requires secure access.
+ *
+ * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
+ */
+ abstract public void verifyUnlock();
+
+ /**
+ * Called before this view is being removed.
+ */
+ abstract public void cleanUp();
+
+ /**
+ * Gets the desired user activity timeout in milliseconds, or -1 if the
+ * default should be used.
+ */
+ abstract public long getUserActivityTimeout();
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (interceptMediaKey(event)) {
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ /**
+ * Allows the media keys to work when the keyguard is showing.
+ * The media keys should be of no interest to the actual keyguard view(s),
+ * so intercepting them here should not be of any harm.
+ * @param event The key event
+ * @return whether the event was consumed as a media key.
+ */
+ private boolean interceptMediaKey(KeyEvent event) {
+ final int keyCode = event.getKeyCode();
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ /* Suppress PLAY/PAUSE toggle when phone is ringing or
+ * in-call to avoid music playback */
+ if (mTelephonyManager == null) {
+ mTelephonyManager = (TelephonyManager) getContext().getSystemService(
+ Context.TELEPHONY_SERVICE);
+ }
+ if (mTelephonyManager != null &&
+ mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
+ return true; // suppress key event
+ }
+ case KeyEvent.KEYCODE_MUTE:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_MEDIA_RECORD:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+ handleMediaKeyEvent(event);
+ return true;
+ }
+
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_MUTE: {
+ if (KEYGUARD_MANAGES_VOLUME) {
+ synchronized (this) {
+ if (mAudioManager == null) {
+ mAudioManager = (AudioManager) getContext().getSystemService(
+ Context.AUDIO_SERVICE);
+ }
+ }
+ // Volume buttons should only function for music (local or remote).
+ // TODO: Actually handle MUTE.
+ mAudioManager.adjustLocalOrRemoteStreamVolume(
+ AudioManager.STREAM_MUSIC,
+ keyCode == KeyEvent.KEYCODE_VOLUME_UP
+ ? AudioManager.ADJUST_RAISE
+ : AudioManager.ADJUST_LOWER);
+ // Don't execute default volume behavior
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MUTE:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_MEDIA_RECORD:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+ handleMediaKeyEvent(event);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void handleMediaKeyEvent(KeyEvent keyEvent) {
+ IAudioService audioService = IAudioService.Stub.asInterface(
+ ServiceManager.checkService(Context.AUDIO_SERVICE));
+ if (audioService != null) {
+ try {
+ audioService.dispatchMediaKeyEvent(keyEvent);
+ } catch (RemoteException e) {
+ Log.e("KeyguardViewBase", "dispatchMediaKeyEvent threw exception " + e);
+ }
+ } else {
+ Slog.w("KeyguardViewBase", "Unable to find IAudioService for media key event");
+ }
+ }
+
+ @Override
+ public void dispatchSystemUiVisibilityChanged(int visibility) {
+ super.dispatchSystemUiVisibilityChanged(visibility);
+
+ if (!(mContext instanceof Activity)) {
+ setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
+ }
+ }
+
+ public void setViewMediatorCallback(
+ KeyguardViewMediator.ViewMediatorCallback viewMediatorCallback) {
+ mViewMediatorCallback = viewMediatorCallback;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
new file mode 100644
index 0000000..35bea26
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ */
+
+package com.android.keyguard;
+
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewManager;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+/**
+ * Manages creating, showing, hiding and resetting the keyguard. Calls back
+ * via {@link KeyguardViewMediator.ViewMediatorCallback} to poke
+ * the wake lock and report that the keyguard is done, which is in turn,
+ * reported to this class by the current {@link KeyguardViewBase}.
+ */
+public class KeyguardViewManager {
+ private final static boolean DEBUG = KeyguardViewMediator.DEBUG;
+ private static String TAG = "KeyguardViewManager";
+ public static boolean USE_UPPER_CASE = true;
+ public final static String IS_SWITCHING_USER = "is_switching_user";
+
+ // Timeout used for keypresses
+ static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
+
+ private final Context mContext;
+ private final ViewManager mViewManager;
+ private final KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
+
+ private WindowManager.LayoutParams mWindowLayoutParams;
+ private boolean mNeedsInput = false;
+
+ private FrameLayout mKeyguardHost;
+ private KeyguardHostView mKeyguardView;
+
+ private boolean mScreenOn = false;
+ private LockPatternUtils mLockPatternUtils;
+
+ public interface ShowListener {
+ void onShown(IBinder windowToken);
+ };
+
+ /**
+ * @param context Used to create views.
+ * @param viewManager Keyguard will be attached to this.
+ * @param callback Used to notify of changes.
+ * @param lockPatternUtils
+ */
+ public KeyguardViewManager(Context context, ViewManager viewManager,
+ KeyguardViewMediator.ViewMediatorCallback callback,
+ LockPatternUtils lockPatternUtils) {
+ mContext = context;
+ mViewManager = viewManager;
+ mViewMediatorCallback = callback;
+ mLockPatternUtils = lockPatternUtils;
+ }
+
+ /**
+ * Show the keyguard. Will handle creating and attaching to the view manager
+ * lazily.
+ */
+ public synchronized void show(Bundle options) {
+ if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
+
+ boolean enableScreenRotation = shouldEnableScreenRotation();
+
+ maybeCreateKeyguardLocked(enableScreenRotation, false, options);
+ maybeEnableScreenRotation(enableScreenRotation);
+
+ // Disable common aspects of the system/status/navigation bars that are not appropriate or
+ // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED
+ // activities. Other disabled bits are handled by the KeyguardViewMediator talking
+ // directly to the status bar service.
+ final int visFlags = View.STATUS_BAR_DISABLE_HOME;
+ if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");
+ mKeyguardHost.setSystemUiVisibility(visFlags);
+
+ mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+ mKeyguardHost.setVisibility(View.VISIBLE);
+ mKeyguardView.show();
+ mKeyguardView.requestFocus();
+ }
+
+ private boolean shouldEnableScreenRotation() {
+ Resources res = mContext.getResources();
+ return SystemProperties.getBoolean("lockscreen.rot_override",false)
+ || res.getBoolean(R.bool.config_enableLockScreenRotation);
+ }
+
+ class ViewManagerHost extends FrameLayout {
+ public ViewManagerHost(Context context) {
+ super(context);
+ setFitsSystemWindows(true);
+ }
+
+ @Override
+ protected boolean fitSystemWindows(Rect insets) {
+ Log.v("TAG", "bug 7643792: fitSystemWindows(" + insets.toShortString() + ")");
+ return super.fitSystemWindows(insets);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+ // only propagate configuration messages if we're currently showing
+ maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
+ } else {
+ if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
+ }
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mKeyguardView != null) {
+ // Always process back and menu keys, regardless of focus
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ int keyCode = event.getKeyCode();
+ if (keyCode == KeyEvent.KEYCODE_BACK && mKeyguardView.handleBackKey()) {
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_MENU && mKeyguardView.handleMenuKey()) {
+ return true;
+ }
+ }
+ // Always process media keys, regardless of focus
+ if (mKeyguardView.dispatchKeyEvent(event)) {
+ return true;
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+ }
+
+ SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>();
+
+ private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
+ Bundle options) {
+ if (mKeyguardHost != null) {
+ mKeyguardHost.saveHierarchyState(mStateContainer);
+ }
+
+ if (mKeyguardHost == null) {
+ if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
+
+ mKeyguardHost = new ViewManagerHost(mContext);
+
+ int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+ | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
+ if (!mNeedsInput) {
+ flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ }
+
+ final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
+ final int type = WindowManager.LayoutParams.TYPE_KEYGUARD;
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);
+ lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ lp.windowAnimations = R.style.Animation_LockScreen;
+ lp.screenOrientation = enableScreenRotation ?
+ ActivityInfo.SCREEN_ORIENTATION_USER : ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+
+ if (ActivityManager.isHighEndGfx()) {
+ lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ lp.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
+ }
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;
+ lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
+ lp.setTitle("Keyguard");
+ mWindowLayoutParams = lp;
+ mViewManager.addView(mKeyguardHost, lp);
+ }
+
+ if (force || mKeyguardView == null) {
+ inflateKeyguardView(options);
+ mKeyguardView.requestFocus();
+ }
+ updateUserActivityTimeoutInWindowLayoutParams();
+ mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+
+ mKeyguardHost.restoreHierarchyState(mStateContainer);
+ }
+
+ private void inflateKeyguardView(Bundle options) {
+ View v = mKeyguardHost.findViewById(R.id.keyguard_host_view);
+ if (v != null) {
+ mKeyguardHost.removeView(v);
+ }
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true);
+ mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view);
+ mKeyguardView.setLockPatternUtils(mLockPatternUtils);
+ mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);
+ mKeyguardView.initializeSwitchingUserState(options != null &&
+ options.getBoolean(IS_SWITCHING_USER));
+
+ // HACK
+ // The keyguard view will have set up window flags in onFinishInflate before we set
+ // the view mediator callback. Make sure it knows the correct IME state.
+ if (mViewMediatorCallback != null) {
+ KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(
+ R.id.keyguard_password_view);
+
+ if (kpv != null) {
+ mViewMediatorCallback.setNeedsInput(kpv.needsInput());
+ }
+ }
+
+ if (options != null) {
+ int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
+ mKeyguardView.goToWidget(widgetToShow);
+ }
+ }
+ }
+
+ public void updateUserActivityTimeout() {
+ updateUserActivityTimeoutInWindowLayoutParams();
+ mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+ }
+
+ private void updateUserActivityTimeoutInWindowLayoutParams() {
+ // Use the user activity timeout requested by the keyguard view, if any.
+ if (mKeyguardView != null) {
+ long timeout = mKeyguardView.getUserActivityTimeout();
+ if (timeout >= 0) {
+ mWindowLayoutParams.userActivityTimeout = timeout;
+ return;
+ }
+ }
+
+ // Otherwise, use the default timeout.
+ mWindowLayoutParams.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS;
+ }
+
+ private void maybeEnableScreenRotation(boolean enableScreenRotation) {
+ // TODO: move this outside
+ if (enableScreenRotation) {
+ if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!");
+ mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
+ } else {
+ if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
+ mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+ }
+ mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+ }
+
+ public void setNeedsInput(boolean needsInput) {
+ mNeedsInput = needsInput;
+ if (mWindowLayoutParams != null) {
+ if (needsInput) {
+ mWindowLayoutParams.flags &=
+ ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ } else {
+ mWindowLayoutParams.flags |=
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ }
+
+ try {
+ mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+ } catch (java.lang.IllegalArgumentException e) {
+ // TODO: Ensure this method isn't called on views that are changing...
+ Log.w(TAG,"Can't update input method on " + mKeyguardHost + " window not attached");
+ }
+ }
+ }
+
+ /**
+ * Reset the state of the view.
+ */
+ public synchronized void reset(Bundle options) {
+ if (DEBUG) Log.d(TAG, "reset()");
+ // User might have switched, check if we need to go back to keyguard
+ // TODO: It's preferable to stay and show the correct lockscreen or unlock if none
+ maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options);
+ }
+
+ public synchronized void onScreenTurnedOff() {
+ if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
+ mScreenOn = false;
+ if (mKeyguardView != null) {
+ mKeyguardView.onScreenTurnedOff();
+ }
+ }
+
+ public synchronized void onScreenTurnedOn(final IKeyguardShowCallback callback) {
+ if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
+ mScreenOn = true;
+ if (mKeyguardView != null) {
+ mKeyguardView.onScreenTurnedOn();
+
+ // Caller should wait for this window to be shown before turning
+ // on the screen.
+ if (callback != null) {
+ if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+ // Keyguard may be in the process of being shown, but not yet
+ // updated with the window manager... give it a chance to do so.
+ mKeyguardHost.post(new Runnable() {
+ @Override
+ public void run() {
+ IBinder token = null;
+ if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+ token = mKeyguardHost.getWindowToken();
+ }
+ try {
+ callback.onShown(token);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception calling onShown():", e);
+ }
+ }
+ });
+ } else {
+ try {
+ callback.onShown(null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception calling onShown():", e);
+ }
+ }
+ }
+ } else if (callback != null) {
+ try {
+ callback.onShown(null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception calling onShown():", e);
+ }
+ }
+ }
+
+ public synchronized void verifyUnlock() {
+ if (DEBUG) Log.d(TAG, "verifyUnlock()");
+ show(null);
+ mKeyguardView.verifyUnlock();
+ }
+
+ /**
+ * Hides the keyguard view
+ */
+ public synchronized void hide() {
+ if (DEBUG) Log.d(TAG, "hide()");
+
+ if (mKeyguardHost != null) {
+ mKeyguardHost.setVisibility(View.GONE);
+
+ // We really only want to preserve keyguard state for configuration changes. Hence
+ // we should clear state of widgets (e.g. Music) when we hide keyguard so it can
+ // start with a fresh state when we return.
+ mStateContainer.clear();
+
+ // Don't do this right away, so we can let the view continue to animate
+ // as it goes away.
+ if (mKeyguardView != null) {
+ final KeyguardViewBase lastView = mKeyguardView;
+ mKeyguardView = null;
+ mKeyguardHost.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (KeyguardViewManager.this) {
+ lastView.cleanUp();
+ mKeyguardHost.removeView(lastView);
+ }
+ }
+ }, 500);
+ }
+ }
+ }
+
+ /**
+ * Dismisses the keyguard by going to the next screen or making it gone.
+ */
+ public synchronized void dismiss() {
+ if (mScreenOn) {
+ mKeyguardView.dismiss();
+ }
+ }
+
+ /**
+ * @return Whether the keyguard is showing
+ */
+ public synchronized boolean isShowing() {
+ return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
+ }
+
+ public void showAssistant() {
+ if (mKeyguardView != null) {
+ mKeyguardView.showAssistant();
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
new file mode 100644
index 0000000..597fb3b
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -0,0 +1,1352 @@
+/*
+ * 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.
+ */
+
+package com.android.keyguard;
+
+import com.android.internal.policy.IKeyguardExitCallback;
+import com.android.internal.policy.IKeyguardShowCallback;
+import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.SearchManager;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.SoundPool;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.widget.LockPatternUtils;
+
+
+/**
+ * Mediates requests related to the keyguard. This includes queries about the
+ * state of the keyguard, power management events that effect whether the keyguard
+ * should be shown or reset, callbacks to the phone window manager to notify
+ * it of when the keyguard is showing, and events from the keyguard view itself
+ * stating that the keyguard was succesfully unlocked.
+ *
+ * Note that the keyguard view is shown when the screen is off (as appropriate)
+ * so that once the screen comes on, it will be ready immediately.
+ *
+ * Example queries about the keyguard:
+ * - is {movement, key} one that should wake the keygaurd?
+ * - is the keyguard showing?
+ * - are input events restricted due to the state of the keyguard?
+ *
+ * Callbacks to the phone window manager:
+ * - the keyguard is showing
+ *
+ * Example external events that translate to keyguard view changes:
+ * - screen turned off -> reset the keyguard, and show it so it will be ready
+ * next time the screen turns on
+ * - keyboard is slid open -> if the keyguard is not secure, hide it
+ *
+ * Events from the keyguard view:
+ * - user succesfully unlocked keyguard -> hide keyguard view, and no longer
+ * restrict input events.
+ *
+ * Note: in addition to normal power managment events that effect the state of
+ * whether the keyguard should be showing, external apps and services may request
+ * that the keyguard be disabled via {@link #setKeyguardEnabled(boolean)}. When
+ * false, this will override all other conditions for turning on the keyguard.
+ *
+ * Threading and synchronization:
+ * This class is created by the initialization routine of the {@link WindowManagerPolicy},
+ * and runs on its thread. The keyguard UI is created from that thread in the
+ * constructor of this class. The apis may be called from other threads, including the
+ * {@link com.android.server.input.InputManagerService}'s and {@link android.view.WindowManager}'s.
+ * Therefore, methods on this class are synchronized, and any action that is pointed
+ * directly to the keyguard UI is posted to a {@link Handler} to ensure it is taken on the UI
+ * thread of the keyguard.
+ */
+public class KeyguardViewMediator {
+ private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
+ final static boolean DEBUG = false;
+ private final static boolean DBG_WAKE = false;
+
+ private final static String TAG = "KeyguardViewMediator";
+
+ private static final String DELAYED_KEYGUARD_ACTION =
+ "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD";
+
+ // used for handler messages
+ private static final int SHOW = 2;
+ private static final int HIDE = 3;
+ private static final int RESET = 4;
+ private static final int VERIFY_UNLOCK = 5;
+ private static final int NOTIFY_SCREEN_OFF = 6;
+ private static final int NOTIFY_SCREEN_ON = 7;
+ private static final int KEYGUARD_DONE = 9;
+ private static final int KEYGUARD_DONE_DRAWING = 10;
+ private static final int KEYGUARD_DONE_AUTHENTICATING = 11;
+ private static final int SET_HIDDEN = 12;
+ private static final int KEYGUARD_TIMEOUT = 13;
+ private static final int SHOW_ASSISTANT = 14;
+
+ /**
+ * The default amount of time we stay awake (used for all key input)
+ */
+ protected static final int AWAKE_INTERVAL_DEFAULT_MS = 10000;
+
+ /**
+ * How long to wait after the screen turns off due to timeout before
+ * turning on the keyguard (i.e, the user has this much time to turn
+ * the screen back on without having to face the keyguard).
+ */
+ private static final int KEYGUARD_LOCK_AFTER_DELAY_DEFAULT = 5000;
+
+ /**
+ * How long we'll wait for the {@link ViewMediatorCallback#keyguardDoneDrawing()}
+ * callback before unblocking a call to {@link #setKeyguardEnabled(boolean)}
+ * that is reenabling the keyguard.
+ */
+ private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000;
+
+ /**
+ * Allow the user to expand the status bar when the keyguard is engaged
+ * (without a pattern or password).
+ */
+ private static final boolean ENABLE_INSECURE_STATUS_BAR_EXPAND = true;
+
+ /** The stream type that the lock sounds are tied to. */
+ private int mMasterStreamType;
+
+ private Context mContext;
+ private AlarmManager mAlarmManager;
+ private AudioManager mAudioManager;
+ private StatusBarManager mStatusBarManager;
+ private boolean mShowLockIcon;
+ private boolean mShowingLockIcon;
+ private boolean mSwitchingUser;
+
+ private boolean mSystemReady;
+
+ // Whether the next call to playSounds() should be skipped. Defaults to
+ // true because the first lock (on boot) should be silent.
+ private boolean mSuppressNextLockSound = true;
+
+
+ /** High level access to the power manager for WakeLocks */
+ private PowerManager mPM;
+
+ /** UserManager for querying number of users */
+ private UserManager mUserManager;
+
+ /** SearchManager for determining whether or not search assistant is available */
+ private SearchManager mSearchManager;
+
+ /**
+ * Used to keep the device awake while to ensure the keyguard finishes opening before
+ * we sleep.
+ */
+ private PowerManager.WakeLock mShowKeyguardWakeLock;
+
+ private KeyguardViewManager mKeyguardViewManager;
+
+ // these are protected by synchronized (this)
+
+ /**
+ * External apps (like the phone app) can tell us to disable the keygaurd.
+ */
+ private boolean mExternallyEnabled = true;
+
+ /**
+ * Remember if an external call to {@link #setKeyguardEnabled} with value
+ * false caused us to hide the keyguard, so that we need to reshow it once
+ * the keygaurd is reenabled with another call with value true.
+ */
+ private boolean mNeedToReshowWhenReenabled = false;
+
+ // cached value of whether we are showing (need to know this to quickly
+ // answer whether the input should be restricted)
+ private boolean mShowing = false;
+
+ // true if the keyguard is hidden by another window
+ private boolean mHidden = false;
+
+ /**
+ * Helps remember whether the screen has turned on since the last time
+ * it turned off due to timeout. see {@link #onScreenTurnedOff(int)}
+ */
+ private int mDelayedShowingSequence;
+
+ /**
+ * If the user has disabled the keyguard, then requests to exit, this is
+ * how we'll ultimately let them know whether it was successful. We use this
+ * var being non-null as an indicator that there is an in progress request.
+ */
+ private IKeyguardExitCallback mExitSecureCallback;
+
+ // the properties of the keyguard
+
+ private KeyguardUpdateMonitor mUpdateMonitor;
+
+ private boolean mScreenOn;
+
+ // last known state of the cellular connection
+ private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
+
+ /**
+ * we send this intent when the keyguard is dismissed.
+ */
+ private static final Intent USER_PRESENT_INTENT = new Intent(Intent.ACTION_USER_PRESENT)
+ .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ /**
+ * {@link #setKeyguardEnabled} waits on this condition when it reenables
+ * the keyguard.
+ */
+ private boolean mWaitingUntilKeyguardVisible = false;
+ private LockPatternUtils mLockPatternUtils;
+ private boolean mKeyguardDonePending = false;
+
+ private SoundPool mLockSounds;
+ private int mLockSoundId;
+ private int mUnlockSoundId;
+ private int mLockSoundStreamId;
+
+ /**
+ * The volume applied to the lock/unlock sounds.
+ */
+ private final float mLockSoundVolume;
+
+ /**
+ * Cache of avatar drawables, for use by KeyguardMultiUserAvatar.
+ */
+ private static MultiUserAvatarCache sMultiUserAvatarCache = new MultiUserAvatarCache();
+
+ /**
+ * The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
+ * various things.
+ */
+ public interface ViewMediatorCallback {
+ /**
+ * Reports user activity and requests that the screen stay on.
+ */
+ void userActivity();
+
+ /**
+ * Reports user activity and requests that the screen stay on for at least
+ * the specified amount of time.
+ * @param millis The amount of time in millis. This value is currently ignored.
+ */
+ void userActivity(long millis);
+
+ /**
+ * Report that the keyguard is done.
+ * @param authenticated Whether the user securely got past the keyguard.
+ * the only reason for this to be false is if the keyguard was instructed
+ * to appear temporarily to verify the user is supposed to get past the
+ * keyguard, and the user fails to do so.
+ */
+ void keyguardDone(boolean authenticated);
+
+ /**
+ * Report that the keyguard is done drawing.
+ */
+ void keyguardDoneDrawing();
+
+ /**
+ * Tell ViewMediator that the current view needs IME input
+ * @param needsInput
+ */
+ void setNeedsInput(boolean needsInput);
+
+ /**
+ * Tell view mediator that the keyguard view's desired user activity timeout
+ * has changed and needs to be reapplied to the window.
+ */
+ void onUserActivityTimeoutChanged();
+
+ /**
+ * Report that the keyguard is dismissable, pending the next keyguardDone call.
+ */
+ void keyguardDonePending();
+ }
+
+ KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
+
+ @Override
+ public void onUserSwitching(int userId) {
+ // Note that the mLockPatternUtils user has already been updated from setCurrentUser.
+ // We need to force a reset of the views, since lockNow (called by
+ // ActivityManagerService) will not reconstruct the keyguard if it is already showing.
+ synchronized (KeyguardViewMediator.this) {
+ mSwitchingUser = true;
+ resetStateLocked(null);
+ adjustStatusBarLocked();
+ // When we switch users we want to bring the new user to the biometric unlock even
+ // if the current user has gone to the backup.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
+ }
+ }
+
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ mSwitchingUser = false;
+ }
+
+ @Override
+ public void onUserRemoved(int userId) {
+ mLockPatternUtils.removeUser(userId);
+ sMultiUserAvatarCache.clear(userId);
+ }
+
+ @Override
+ public void onUserInfoChanged(int userId) {
+ sMultiUserAvatarCache.clear(userId);
+ }
+
+ @Override
+ void onPhoneStateChanged(int phoneState) {
+ synchronized (KeyguardViewMediator.this) {
+ if (TelephonyManager.CALL_STATE_IDLE == phoneState // call ending
+ && !mScreenOn // screen off
+ && mExternallyEnabled) { // not disabled by any app
+
+ // note: this is a way to gracefully reenable the keyguard when the call
+ // ends and the screen is off without always reenabling the keyguard
+ // each time the screen turns off while in call (and having an occasional ugly
+ // flicker while turning back on the screen and disabling the keyguard again).
+ if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
+ + "keyguard is showing");
+ doKeyguardLocked(null);
+ }
+ }
+ };
+
+ @Override
+ public void onClockVisibilityChanged() {
+ adjustStatusBarLocked();
+ }
+
+ @Override
+ public void onDeviceProvisioned() {
+ sendUserPresentBroadcast();
+ }
+
+ @Override
+ public void onSimStateChanged(IccCardConstants.State simState) {
+ if (DEBUG) Log.d(TAG, "onSimStateChanged: " + simState);
+
+ switch (simState) {
+ case NOT_READY:
+ case ABSENT:
+ // only force lock screen in case of missing sim if user hasn't
+ // gone through setup wizard
+ synchronized (this) {
+ if (!mUpdateMonitor.isDeviceProvisioned()) {
+ if (!isShowing()) {
+ if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
+ + " we need to show the keyguard since the "
+ + "device isn't provisioned yet.");
+ doKeyguardLocked(null);
+ } else {
+ resetStateLocked(null);
+ }
+ }
+ }
+ break;
+ case PIN_REQUIRED:
+ case PUK_REQUIRED:
+ synchronized (this) {
+ if (!isShowing()) {
+ if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
+ + "showing; need to show keyguard so user can enter sim pin");
+ doKeyguardLocked(null);
+ } else {
+ resetStateLocked(null);
+ }
+ }
+ break;
+ case PERM_DISABLED:
+ synchronized (this) {
+ if (!isShowing()) {
+ if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
+ + "keygaurd isn't showing.");
+ doKeyguardLocked(null);
+ } else {
+ if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+ + "show permanently disabled message in lockscreen.");
+ resetStateLocked(null);
+ }
+ }
+ break;
+ case READY:
+ synchronized (this) {
+ if (isShowing()) {
+ resetStateLocked(null);
+ }
+ }
+ break;
+ }
+ }
+
+ };
+
+ ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
+ public void userActivity() {
+ KeyguardViewMediator.this.userActivity();
+ }
+
+ public void userActivity(long holdMs) {
+ KeyguardViewMediator.this.userActivity(holdMs);
+ }
+
+ public void keyguardDone(boolean authenticated) {
+ KeyguardViewMediator.this.keyguardDone(authenticated, true);
+ }
+
+ public void keyguardDoneDrawing() {
+ mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
+ }
+
+ @Override
+ public void setNeedsInput(boolean needsInput) {
+ mKeyguardViewManager.setNeedsInput(needsInput);
+ }
+
+ @Override
+ public void onUserActivityTimeoutChanged() {
+ mKeyguardViewManager.updateUserActivityTimeout();
+ }
+
+ @Override
+ public void keyguardDonePending() {
+ mKeyguardDonePending = true;
+ }
+ };
+
+ private void userActivity() {
+ userActivity(AWAKE_INTERVAL_DEFAULT_MS);
+ }
+
+ public void userActivity(long holdMs) {
+ // We ignore the hold time. Eventually we should remove it.
+ // Instead, the keyguard window has an explicit user activity timeout set on it.
+ mPM.userActivity(SystemClock.uptimeMillis(), false);
+ }
+
+ /**
+ * Construct a KeyguardViewMediator
+ * @param context
+ * @param lockPatternUtils optional mock interface for LockPatternUtils
+ */
+ public KeyguardViewMediator(Context context, LockPatternUtils lockPatternUtils) {
+ mContext = context;
+ mPM = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
+ mShowKeyguardWakeLock.setReferenceCounted(false);
+
+ mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
+
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+ mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
+
+ mLockPatternUtils = lockPatternUtils != null
+ ? lockPatternUtils : new LockPatternUtils(mContext);
+ mLockPatternUtils.setCurrentUser(UserHandle.USER_OWNER);
+
+ WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+
+ mKeyguardViewManager = new KeyguardViewManager(context, wm, mViewMediatorCallback,
+ mLockPatternUtils);
+
+ final ContentResolver cr = mContext.getContentResolver();
+ mShowLockIcon = (Settings.System.getInt(cr, "show_status_bar_lock", 0) == 1);
+
+ mScreenOn = mPM.isScreenOn();
+
+ mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
+ String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND);
+ if (soundPath != null) {
+ mLockSoundId = mLockSounds.load(soundPath, 1);
+ }
+ if (soundPath == null || mLockSoundId == 0) {
+ Log.w(TAG, "failed to load lock sound from " + soundPath);
+ }
+ soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);
+ if (soundPath != null) {
+ mUnlockSoundId = mLockSounds.load(soundPath, 1);
+ }
+ if (soundPath == null || mUnlockSoundId == 0) {
+ Log.w(TAG, "failed to load unlock sound from " + soundPath);
+ }
+ int lockSoundDefaultAttenuation = context.getResources().getInteger(
+ com.android.internal.R.integer.config_lockSoundVolumeDb);
+ mLockSoundVolume = (float)Math.pow(10, (float)lockSoundDefaultAttenuation/20);
+ }
+
+ /**
+ * Let us know that the system is ready after startup.
+ */
+ public void onSystemReady() {
+ mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+ synchronized (this) {
+ if (DEBUG) Log.d(TAG, "onSystemReady");
+ mSystemReady = true;
+ mUpdateMonitor.registerCallback(mUpdateCallback);
+
+ // Suppress biometric unlock right after boot until things have settled if it is the
+ // selected security method, otherwise unsuppress it. It must be unsuppressed if it is
+ // not the selected security method for the following reason: if the user starts
+ // without a screen lock selected, the biometric unlock would be suppressed the first
+ // time they try to use it.
+ //
+ // Note that the biometric unlock will still not show if it is not the selected method.
+ // Calling setAlternateUnlockEnabled(true) simply says don't suppress it if it is the
+ // selected method.
+ if (mLockPatternUtils.usingBiometricWeak()
+ && mLockPatternUtils.isBiometricWeakInstalled()) {
+ if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");
+ mUpdateMonitor.setAlternateUnlockEnabled(false);
+ } else {
+ mUpdateMonitor.setAlternateUnlockEnabled(true);
+ }
+
+ doKeyguardLocked(null);
+ }
+ // Most services aren't available until the system reaches the ready state, so we
+ // send it here when the device first boots.
+ maybeSendUserPresentBroadcast();
+ }
+
+ /**
+ * Called to let us know the screen was turned off.
+ * @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER},
+ * {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
+ * {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
+ */
+ public void onScreenTurnedOff(int why) {
+ synchronized (this) {
+ mScreenOn = false;
+ if (DEBUG) Log.d(TAG, "onScreenTurnedOff(" + why + ")");
+
+ mKeyguardDonePending = false;
+
+ // Lock immediately based on setting if secure (user has a pin/pattern/password).
+ // This also "locks" the device when not secure to provide easy access to the
+ // camera while preventing unwanted input.
+ final boolean lockImmediately =
+ mLockPatternUtils.getPowerButtonInstantlyLocks() || !mLockPatternUtils.isSecure();
+
+ if (mExitSecureCallback != null) {
+ if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
+ try {
+ mExitSecureCallback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ mExitSecureCallback = null;
+ if (!mExternallyEnabled) {
+ hideLocked();
+ }
+ } else if (mShowing) {
+ notifyScreenOffLocked();
+ resetStateLocked(null);
+ } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT
+ || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) {
+ doKeyguardLaterLocked();
+ } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
+ // Do not enable the keyguard if the prox sensor forced the screen off.
+ } else {
+ doKeyguardLocked(null);
+ }
+ }
+ }
+
+ private void doKeyguardLaterLocked() {
+ // if the screen turned off because of timeout or the user hit the power button
+ // and we don't need to lock immediately, set an alarm
+ // to enable it a little bit later (i.e, give the user a chance
+ // to turn the screen back on within a certain window without
+ // having to unlock the screen)
+ final ContentResolver cr = mContext.getContentResolver();
+
+ // From DisplaySettings
+ long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT,
+ KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT);
+
+ // From SecuritySettings
+ final long lockAfterTimeout = Settings.Secure.getInt(cr,
+ Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+ KEYGUARD_LOCK_AFTER_DELAY_DEFAULT);
+
+ // From DevicePolicyAdmin
+ final long policyTimeout = mLockPatternUtils.getDevicePolicyManager()
+ .getMaximumTimeToLock(null, mLockPatternUtils.getCurrentUser());
+
+ long timeout;
+ if (policyTimeout > 0) {
+ // policy in effect. Make sure we don't go beyond policy limit.
+ displayTimeout = Math.max(displayTimeout, 0); // ignore negative values
+ timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout);
+ } else {
+ timeout = lockAfterTimeout;
+ }
+
+ if (timeout <= 0) {
+ // Lock now
+ mSuppressNextLockSound = true;
+ doKeyguardLocked(null);
+ } else {
+ // Lock in the future
+ long when = SystemClock.elapsedRealtime() + timeout;
+ Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
+ intent.putExtra("seq", mDelayedShowingSequence);
+ PendingIntent sender = PendingIntent.getBroadcast(mContext,
+ 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
+ if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
+ + mDelayedShowingSequence);
+ }
+ }
+
+ private void cancelDoKeyguardLaterLocked() {
+ mDelayedShowingSequence++;
+ }
+
+ /**
+ * Let's us know the screen was turned on.
+ */
+ public void onScreenTurnedOn(IKeyguardShowCallback callback) {
+ synchronized (this) {
+ mScreenOn = true;
+ cancelDoKeyguardLaterLocked();
+ if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
+ if (callback != null) {
+ notifyScreenOnLocked(callback);
+ }
+ }
+ maybeSendUserPresentBroadcast();
+ }
+
+ private void maybeSendUserPresentBroadcast() {
+ if (mSystemReady && mLockPatternUtils.isLockScreenDisabled()
+ && mUserManager.getUsers(true).size() == 1) {
+ // Lock screen is disabled because the user has set the preference to "None".
+ // In this case, send out ACTION_USER_PRESENT here instead of in
+ // handleKeyguardDone()
+ sendUserPresentBroadcast();
+ }
+ }
+
+ /**
+ * A dream started. We should lock after the usual screen-off lock timeout but only
+ * if there is a secure lock pattern.
+ */
+ public void onDreamingStarted() {
+ synchronized (this) {
+ if (mScreenOn && mLockPatternUtils.isSecure()) {
+ doKeyguardLaterLocked();
+ }
+ }
+ }
+
+ /**
+ * A dream stopped.
+ */
+ public void onDreamingStopped() {
+ synchronized (this) {
+ if (mScreenOn) {
+ cancelDoKeyguardLaterLocked();
+ }
+ }
+ }
+
+ /**
+ * Same semantics as {@link WindowManagerPolicy#enableKeyguard}; provide
+ * a way for external stuff to override normal keyguard behavior. For instance
+ * the phone app disables the keyguard when it receives incoming calls.
+ */
+ public void setKeyguardEnabled(boolean enabled) {
+ synchronized (this) {
+ if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")");
+
+ mExternallyEnabled = enabled;
+
+ if (!enabled && mShowing) {
+ if (mExitSecureCallback != null) {
+ if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring");
+ // we're in the process of handling a request to verify the user
+ // can get past the keyguard. ignore extraneous requests to disable / reenable
+ return;
+ }
+
+ // hiding keyguard that is showing, remember to reshow later
+ if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, "
+ + "disabling status bar expansion");
+ mNeedToReshowWhenReenabled = true;
+ hideLocked();
+ } else if (enabled && mNeedToReshowWhenReenabled) {
+ // reenabled after previously hidden, reshow
+ if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling "
+ + "status bar expansion");
+ mNeedToReshowWhenReenabled = false;
+
+ if (mExitSecureCallback != null) {
+ if (DEBUG) Log.d(TAG, "onKeyguardExitResult(false), resetting");
+ try {
+ mExitSecureCallback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ mExitSecureCallback = null;
+ resetStateLocked(null);
+ } else {
+ showLocked(null);
+
+ // block until we know the keygaurd is done drawing (and post a message
+ // to unblock us after a timeout so we don't risk blocking too long
+ // and causing an ANR).
+ mWaitingUntilKeyguardVisible = true;
+ mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS);
+ if (DEBUG) Log.d(TAG, "waiting until mWaitingUntilKeyguardVisible is false");
+ while (mWaitingUntilKeyguardVisible) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ if (DEBUG) Log.d(TAG, "done waiting for mWaitingUntilKeyguardVisible");
+ }
+ }
+ }
+ }
+
+ /**
+ * @see android.app.KeyguardManager#exitKeyguardSecurely
+ */
+ public void verifyUnlock(IKeyguardExitCallback callback) {
+ synchronized (this) {
+ if (DEBUG) Log.d(TAG, "verifyUnlock");
+ if (!mUpdateMonitor.isDeviceProvisioned()) {
+ // don't allow this api when the device isn't provisioned
+ if (DEBUG) Log.d(TAG, "ignoring because device isn't provisioned");
+ try {
+ callback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ } else if (mExternallyEnabled) {
+ // this only applies when the user has externally disabled the
+ // keyguard. this is unexpected and means the user is not
+ // using the api properly.
+ Log.w(TAG, "verifyUnlock called when not externally disabled");
+ try {
+ callback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ } else if (mExitSecureCallback != null) {
+ // already in progress with someone else
+ try {
+ callback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ } else {
+ mExitSecureCallback = callback;
+ verifyUnlockLocked();
+ }
+ }
+ }
+
+ /**
+ * Is the keyguard currently showing?
+ */
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ /**
+ * Is the keyguard currently showing and not being force hidden?
+ */
+ public boolean isShowingAndNotHidden() {
+ return mShowing && !mHidden;
+ }
+
+ /**
+ * Notify us when the keyguard is hidden by another window
+ */
+ public void setHidden(boolean isHidden) {
+ if (DEBUG) Log.d(TAG, "setHidden " + isHidden);
+ mUpdateMonitor.sendKeyguardVisibilityChanged(!isHidden);
+ mHandler.removeMessages(SET_HIDDEN);
+ Message msg = mHandler.obtainMessage(SET_HIDDEN, (isHidden ? 1 : 0), 0);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Handles SET_HIDDEN message sent by setHidden()
+ */
+ private void handleSetHidden(boolean isHidden) {
+ synchronized (KeyguardViewMediator.this) {
+ if (mHidden != isHidden) {
+ mHidden = isHidden;
+ updateActivityLockScreenState();
+ adjustStatusBarLocked();
+ }
+ }
+ }
+
+ /**
+ * Used by PhoneWindowManager to enable the keyguard due to a user activity timeout.
+ * This must be safe to call from any thread and with any window manager locks held.
+ */
+ public void doKeyguardTimeout(Bundle options) {
+ mHandler.removeMessages(KEYGUARD_TIMEOUT);
+ Message msg = mHandler.obtainMessage(KEYGUARD_TIMEOUT, options);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Given the state of the keyguard, is the input restricted?
+ * Input is restricted when the keyguard is showing, or when the keyguard
+ * was suppressed by an app that disabled the keyguard or we haven't been provisioned yet.
+ */
+ public boolean isInputRestricted() {
+ return mShowing || mNeedToReshowWhenReenabled || !mUpdateMonitor.isDeviceProvisioned();
+ }
+
+ /**
+ * Enable the keyguard if the settings are appropriate.
+ */
+ private void doKeyguardLocked(Bundle options) {
+ // if another app is disabling us, don't show
+ if (!mExternallyEnabled) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
+
+ // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
+ // for an occasional ugly flicker in this situation:
+ // 1) receive a call with the screen on (no keyguard) or make a call
+ // 2) screen times out
+ // 3) user hits key to turn screen back on
+ // instead, we reenable the keyguard when we know the screen is off and the call
+ // ends (see the broadcast receiver below)
+ // TODO: clean this up when we have better support at the window manager level
+ // for apps that wish to be on top of the keyguard
+ return;
+ }
+
+ // if the keyguard is already showing, don't bother
+ if (mKeyguardViewManager.isShowing()) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+ return;
+ }
+
+ // if the setup wizard hasn't run yet, don't show
+ final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
+ false);
+ final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
+ final IccCardConstants.State state = mUpdateMonitor.getSimState();
+ final boolean lockedOrMissing = state.isPinLocked()
+ || ((state == IccCardConstants.State.ABSENT
+ || state == IccCardConstants.State.PERM_DISABLED)
+ && requireSim);
+
+ if (!lockedOrMissing && !provisioned) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+ + " and the sim is not locked or missing");
+ return;
+ }
+
+ if (mUserManager.getUsers(true).size() < 2
+ && mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
+ showLocked(options);
+ }
+
+ /**
+ * Dismiss the keyguard through the security layers.
+ */
+ public void dismiss() {
+ if (mShowing && !mHidden) {
+ mKeyguardViewManager.dismiss();
+ }
+ }
+
+ /**
+ * Send message to keyguard telling it to reset its state.
+ * @param options options about how to show the keyguard
+ * @see #handleReset()
+ */
+ private void resetStateLocked(Bundle options) {
+ if (DEBUG) Log.e(TAG, "resetStateLocked");
+ Message msg = mHandler.obtainMessage(RESET, options);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Send message to keyguard telling it to verify unlock
+ * @see #handleVerifyUnlock()
+ */
+ private void verifyUnlockLocked() {
+ if (DEBUG) Log.d(TAG, "verifyUnlockLocked");
+ mHandler.sendEmptyMessage(VERIFY_UNLOCK);
+ }
+
+
+ /**
+ * Send a message to keyguard telling it the screen just turned on.
+ * @see #onScreenTurnedOff(int)
+ * @see #handleNotifyScreenOff
+ */
+ private void notifyScreenOffLocked() {
+ if (DEBUG) Log.d(TAG, "notifyScreenOffLocked");
+ mHandler.sendEmptyMessage(NOTIFY_SCREEN_OFF);
+ }
+
+ /**
+ * Send a message to keyguard telling it the screen just turned on.
+ * @see #onScreenTurnedOn()
+ * @see #handleNotifyScreenOn
+ */
+ private void notifyScreenOnLocked(IKeyguardShowCallback result) {
+ if (DEBUG) Log.d(TAG, "notifyScreenOnLocked");
+ Message msg = mHandler.obtainMessage(NOTIFY_SCREEN_ON, result);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Send message to keyguard telling it to show itself
+ * @see #handleShow()
+ */
+ private void showLocked(Bundle options) {
+ if (DEBUG) Log.d(TAG, "showLocked");
+ // ensure we stay awake until we are finished displaying the keyguard
+ mShowKeyguardWakeLock.acquire();
+ Message msg = mHandler.obtainMessage(SHOW, options);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Send message to keyguard telling it to hide itself
+ * @see #handleHide()
+ */
+ private void hideLocked() {
+ if (DEBUG) Log.d(TAG, "hideLocked");
+ Message msg = mHandler.obtainMessage(HIDE);
+ mHandler.sendMessage(msg);
+ }
+
+ public boolean isSecure() {
+ return mLockPatternUtils.isSecure()
+ || KeyguardUpdateMonitor.getInstance(mContext).isSimPinSecure();
+ }
+
+ /**
+ * Update the newUserId. Call while holding WindowManagerService lock.
+ * NOTE: Should only be called by KeyguardViewMediator in response to the user id changing.
+ *
+ * @param newUserId The id of the incoming user.
+ */
+ public void setCurrentUser(int newUserId) {
+ mLockPatternUtils.setCurrentUser(newUserId);
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DELAYED_KEYGUARD_ACTION.equals(intent.getAction())) {
+ final int sequence = intent.getIntExtra("seq", 0);
+ if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
+ + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
+ synchronized (KeyguardViewMediator.this) {
+ if (mDelayedShowingSequence == sequence) {
+ // Don't play lockscreen SFX if the screen went off due to timeout.
+ mSuppressNextLockSound = true;
+ doKeyguardLocked(null);
+ }
+ }
+ }
+ }
+ };
+
+ public void keyguardDone(boolean authenticated, boolean wakeup) {
+ mKeyguardDonePending = false;
+ synchronized (this) {
+ EventLog.writeEvent(70000, 2);
+ if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")");
+ Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0,
+ wakeup ? 1 : 0);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /**
+ * This handler will be associated with the policy thread, which will also
+ * be the UI thread of the keyguard. Since the apis of the policy, and therefore
+ * this class, can be called by other threads, any action that directly
+ * interacts with the keyguard ui should be posted to this handler, rather
+ * than called directly.
+ */
+ private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SHOW:
+ handleShow((Bundle) msg.obj);
+ return ;
+ case HIDE:
+ handleHide();
+ return ;
+ case RESET:
+ handleReset((Bundle) msg.obj);
+ return ;
+ case VERIFY_UNLOCK:
+ handleVerifyUnlock();
+ return;
+ case NOTIFY_SCREEN_OFF:
+ handleNotifyScreenOff();
+ return;
+ case NOTIFY_SCREEN_ON:
+ handleNotifyScreenOn((IKeyguardShowCallback) msg.obj);
+ return;
+ case KEYGUARD_DONE:
+ handleKeyguardDone(msg.arg1 != 0, msg.arg2 != 0);
+ return;
+ case KEYGUARD_DONE_DRAWING:
+ handleKeyguardDoneDrawing();
+ return;
+ case KEYGUARD_DONE_AUTHENTICATING:
+ keyguardDone(true, true);
+ return;
+ case SET_HIDDEN:
+ handleSetHidden(msg.arg1 != 0);
+ break;
+ case KEYGUARD_TIMEOUT:
+ synchronized (KeyguardViewMediator.this) {
+ doKeyguardLocked((Bundle) msg.obj);
+ }
+ break;
+ case SHOW_ASSISTANT:
+ handleShowAssistant();
+ break;
+ }
+ }
+ };
+
+ /**
+ * @see #keyguardDone
+ * @see #KEYGUARD_DONE
+ */
+ private void handleKeyguardDone(boolean authenticated, boolean wakeup) {
+ if (DEBUG) Log.d(TAG, "handleKeyguardDone");
+
+ if (authenticated) {
+ mUpdateMonitor.clearFailedUnlockAttempts();
+ }
+
+ if (mExitSecureCallback != null) {
+ try {
+ mExitSecureCallback.onKeyguardExitResult(authenticated);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(" + authenticated + ")", e);
+ }
+
+ mExitSecureCallback = null;
+
+ if (authenticated) {
+ // after succesfully exiting securely, no need to reshow
+ // the keyguard when they've released the lock
+ mExternallyEnabled = true;
+ mNeedToReshowWhenReenabled = false;
+ }
+ }
+
+ handleHide();
+ sendUserPresentBroadcast();
+ }
+
+ private void sendUserPresentBroadcast() {
+ final UserHandle currentUser = new UserHandle(mLockPatternUtils.getCurrentUser());
+ mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, currentUser);
+ }
+
+ /**
+ * @see #keyguardDoneDrawing
+ * @see #KEYGUARD_DONE_DRAWING
+ */
+ private void handleKeyguardDoneDrawing() {
+ synchronized(this) {
+ if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing");
+ if (mWaitingUntilKeyguardVisible) {
+ if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing: notifying mWaitingUntilKeyguardVisible");
+ mWaitingUntilKeyguardVisible = false;
+ notifyAll();
+
+ // there will usually be two of these sent, one as a timeout, and one
+ // as a result of the callback, so remove any remaining messages from
+ // the queue
+ mHandler.removeMessages(KEYGUARD_DONE_DRAWING);
+ }
+ }
+ }
+
+ private void playSounds(boolean locked) {
+ // User feedback for keyguard.
+
+ if (mSuppressNextLockSound) {
+ mSuppressNextLockSound = false;
+ return;
+ }
+
+ final ContentResolver cr = mContext.getContentResolver();
+ if (Settings.System.getInt(cr, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) == 1) {
+ final int whichSound = locked
+ ? mLockSoundId
+ : mUnlockSoundId;
+ mLockSounds.stop(mLockSoundStreamId);
+ // Init mAudioManager
+ if (mAudioManager == null) {
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ if (mAudioManager == null) return;
+ mMasterStreamType = mAudioManager.getMasterStreamType();
+ }
+ // If the stream is muted, don't play the sound
+ if (mAudioManager.isStreamMute(mMasterStreamType)) return;
+
+ mLockSoundStreamId = mLockSounds.play(whichSound,
+ mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
+ }
+ }
+
+ private void updateActivityLockScreenState() {
+ try {
+ ActivityManagerNative.getDefault().setLockScreenShown(
+ mShowing && !mHidden);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #showLocked}.
+ * @see #SHOW
+ */
+ private void handleShow(Bundle options) {
+ synchronized (KeyguardViewMediator.this) {
+ if (!mSystemReady) {
+ if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready.");
+ return;
+ } else {
+ if (DEBUG) Log.d(TAG, "handleShow");
+ }
+
+ mKeyguardViewManager.show(options);
+ mShowing = true;
+ mKeyguardDonePending = false;
+ updateActivityLockScreenState();
+ adjustStatusBarLocked();
+ userActivity();
+ try {
+ ActivityManagerNative.getDefault().closeSystemDialogs("lock");
+ } catch (RemoteException e) {
+ }
+
+ // Do this at the end to not slow down display of the keyguard.
+ playSounds(true);
+
+ mShowKeyguardWakeLock.release();
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #hideLocked()}
+ * @see #HIDE
+ */
+ private void handleHide() {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleHide");
+
+ // only play "unlock" noises if not on a call (since the incall UI
+ // disables the keyguard)
+ if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
+ playSounds(false);
+ }
+
+ mKeyguardViewManager.hide();
+ mShowing = false;
+ mKeyguardDonePending = false;
+ updateActivityLockScreenState();
+ adjustStatusBarLocked();
+ }
+ }
+
+ private void adjustStatusBarLocked() {
+ if (mStatusBarManager == null) {
+ mStatusBarManager = (StatusBarManager)
+ mContext.getSystemService(Context.STATUS_BAR_SERVICE);
+ }
+ if (mStatusBarManager == null) {
+ Log.w(TAG, "Could not get status bar manager");
+ } else {
+ if (mShowLockIcon) {
+ // Give feedback to user when secure keyguard is active and engaged
+ if (mShowing && isSecure()) {
+ if (!mShowingLockIcon) {
+ String contentDescription = mContext.getString(
+ com.android.internal.R.string.status_bar_device_locked);
+ mStatusBarManager.setIcon("secure",
+ com.android.internal.R.drawable.stat_sys_secure, 0,
+ contentDescription);
+ mShowingLockIcon = true;
+ }
+ } else {
+ if (mShowingLockIcon) {
+ mStatusBarManager.removeIcon("secure");
+ mShowingLockIcon = false;
+ }
+ }
+ }
+
+ // Disable aspects of the system/status/navigation bars that must not be re-enabled by
+ // windows that appear on top, ever
+ int flags = StatusBarManager.DISABLE_NONE;
+ if (mShowing) {
+ // Permanently disable components not available when keyguard is enabled
+ // (like recents). Temporary enable/disable (e.g. the "back" button) are
+ // done in KeyguardHostView.
+ flags |= StatusBarManager.DISABLE_RECENT;
+ if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
+ // showing secure lockscreen; disable expanding.
+ flags |= StatusBarManager.DISABLE_EXPAND;
+ }
+ if (isSecure()) {
+ // showing secure lockscreen; disable ticker.
+ flags |= StatusBarManager.DISABLE_NOTIFICATION_TICKER;
+ }
+ if (!isAssistantAvailable()) {
+ flags |= StatusBarManager.DISABLE_SEARCH;
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mHidden=" + mHidden
+ + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
+ }
+
+ if (!(mContext instanceof Activity)) {
+ mStatusBarManager.disable(flags);
+ }
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #resetStateLocked(Bundle)}
+ * @see #RESET
+ */
+ private void handleReset(Bundle options) {
+ if (options == null) {
+ options = new Bundle();
+ }
+ options.putBoolean(KeyguardViewManager.IS_SWITCHING_USER, mSwitchingUser);
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleReset");
+ mKeyguardViewManager.reset(options);
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #verifyUnlock}
+ * @see #VERIFY_UNLOCK
+ */
+ private void handleVerifyUnlock() {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
+ mKeyguardViewManager.verifyUnlock();
+ mShowing = true;
+ updateActivityLockScreenState();
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #notifyScreenOffLocked()}
+ * @see #NOTIFY_SCREEN_OFF
+ */
+ private void handleNotifyScreenOff() {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleNotifyScreenOff");
+ mKeyguardViewManager.onScreenTurnedOff();
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #notifyScreenOnLocked()}
+ * @see #NOTIFY_SCREEN_ON
+ */
+ private void handleNotifyScreenOn(IKeyguardShowCallback callback) {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleNotifyScreenOn");
+ mKeyguardViewManager.onScreenTurnedOn(callback);
+ }
+ }
+
+ public boolean isDismissable() {
+ return mKeyguardDonePending || !isSecure();
+ }
+
+ public void showAssistant() {
+ Message msg = mHandler.obtainMessage(SHOW_ASSISTANT);
+ mHandler.sendMessage(msg);
+ }
+
+ public void handleShowAssistant() {
+ mKeyguardViewManager.showAssistant();
+ }
+
+ private boolean isAssistantAvailable() {
+ return mSearchManager != null
+ && mSearchManager.getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
+ }
+
+ public static MultiUserAvatarCache getAvatarCache() {
+ return sMultiUserAvatarCache;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
new file mode 100644
index 0000000..e85f6df
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+
+public class KeyguardViewStateManager implements
+ SlidingChallengeLayout.OnChallengeScrolledListener,
+ ChallengeLayout.OnBouncerStateChangedListener {
+
+ private KeyguardWidgetPager mKeyguardWidgetPager;
+ private ChallengeLayout mChallengeLayout;
+ private KeyguardHostView mKeyguardHostView;
+ private int[] mTmpPoint = new int[2];
+ private int[] mTmpLoc = new int[2];
+
+ private KeyguardSecurityView mKeyguardSecurityContainer;
+ private static final int SCREEN_ON_HINT_DURATION = 1000;
+ private static final int SCREEN_ON_RING_HINT_DELAY = 300;
+ Handler mMainQueue = new Handler(Looper.myLooper());
+
+ int mLastScrollState = SlidingChallengeLayout.SCROLL_STATE_IDLE;
+
+ // Paged view state
+ private int mPageListeningToSlider = -1;
+ private int mCurrentPage = -1;
+ private int mPageIndexOnPageBeginMoving = -1;
+
+ int mChallengeTop = 0;
+
+ public KeyguardViewStateManager(KeyguardHostView hostView) {
+ mKeyguardHostView = hostView;
+ }
+
+ public void setPagedView(KeyguardWidgetPager pagedView) {
+ mKeyguardWidgetPager = pagedView;
+ updateEdgeSwiping();
+ }
+
+ public void setChallengeLayout(ChallengeLayout layout) {
+ mChallengeLayout = layout;
+ updateEdgeSwiping();
+ }
+
+ private void updateEdgeSwiping() {
+ if (mChallengeLayout != null && mKeyguardWidgetPager != null) {
+ if (mChallengeLayout.isChallengeOverlapping()) {
+ mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(true);
+ } else {
+ mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(false);
+ }
+ }
+ }
+
+ public boolean isChallengeShowing() {
+ if (mChallengeLayout != null) {
+ return mChallengeLayout.isChallengeShowing();
+ }
+ return false;
+ }
+
+ public boolean isChallengeOverlapping() {
+ if (mChallengeLayout != null) {
+ return mChallengeLayout.isChallengeOverlapping();
+ }
+ return false;
+ }
+
+ public void setSecurityViewContainer(KeyguardSecurityView container) {
+ mKeyguardSecurityContainer = container;
+ }
+
+ public void showBouncer(boolean show) {
+ mChallengeLayout.showBouncer();
+ }
+
+ public boolean isBouncing() {
+ return mChallengeLayout.isBouncing();
+ }
+
+ public void fadeOutSecurity(int duration) {
+ ((View) mKeyguardSecurityContainer).animate().alpha(0).setDuration(duration);
+ }
+
+ public void fadeInSecurity(int duration) {
+ ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration);
+ }
+
+ public void onPageBeginMoving() {
+ if (mChallengeLayout.isChallengeOverlapping() &&
+ mChallengeLayout instanceof SlidingChallengeLayout) {
+ SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
+ scl.fadeOutChallenge();
+ mPageIndexOnPageBeginMoving = mKeyguardWidgetPager.getCurrentPage();
+ }
+ // We use mAppWidgetToShow to show a particular widget after you add it--
+ // once the user swipes a page we clear that behavior
+ if (mKeyguardHostView != null) {
+ mKeyguardHostView.clearAppWidgetToShow();
+ mKeyguardHostView.setOnDismissAction(null);
+ }
+ if (mHideHintsRunnable != null) {
+ mMainQueue.removeCallbacks(mHideHintsRunnable);
+ mHideHintsRunnable = null;
+ }
+ }
+
+ public void onPageEndMoving() {
+ mPageIndexOnPageBeginMoving = -1;
+ }
+
+ public void onPageSwitching(View newPage, int newPageIndex) {
+ if (mKeyguardWidgetPager != null && mChallengeLayout instanceof SlidingChallengeLayout) {
+ boolean isCameraPage = newPage instanceof CameraWidgetFrame;
+ ((SlidingChallengeLayout) mChallengeLayout).setChallengeInteractive(!isCameraPage);
+ }
+
+ // If the page we're settling to is the same as we started on, and the action of
+ // moving the page hid the security, we restore it immediately.
+ if (mPageIndexOnPageBeginMoving == mKeyguardWidgetPager.getNextPage() &&
+ mChallengeLayout instanceof SlidingChallengeLayout) {
+ SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
+ scl.fadeInChallenge();
+ mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(-1);
+ }
+ mPageIndexOnPageBeginMoving = -1;
+ }
+
+ public void onPageSwitched(View newPage, int newPageIndex) {
+ // Reset the previous page size and ensure the current page is sized appropriately.
+ // We only modify the page state if it is not currently under control by the slider.
+ // This prevents conflicts.
+
+ // If the page hasn't switched, don't bother with any of this
+ if (mCurrentPage == newPageIndex) return;
+
+ if (mKeyguardWidgetPager != null && mChallengeLayout != null) {
+ KeyguardWidgetFrame prevPage = mKeyguardWidgetPager.getWidgetPageAt(mCurrentPage);
+ if (prevPage != null && mCurrentPage != mPageListeningToSlider && mCurrentPage
+ != mKeyguardWidgetPager.getWidgetToResetOnPageFadeOut()) {
+ prevPage.resetSize();
+ }
+
+ KeyguardWidgetFrame newCurPage = mKeyguardWidgetPager.getWidgetPageAt(newPageIndex);
+ boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
+ if (challengeOverlapping && !newCurPage.isSmall()
+ && mPageListeningToSlider != newPageIndex) {
+ newCurPage.shrinkWidget();
+ }
+ }
+
+ mCurrentPage = newPageIndex;
+ }
+
+ private int getChallengeTopRelativeToFrame(KeyguardWidgetFrame frame, int top) {
+ mTmpPoint[0] = 0;
+ mTmpPoint[1] = top;
+ mapPoint((View) mChallengeLayout, frame, mTmpPoint);
+ return mTmpPoint[1];
+ }
+
+ /**
+ * Simple method to map a point from one view's coordinates to another's. Note: this method
+ * doesn't account for transforms, so if the views will be transformed, this should not be used.
+ *
+ * @param fromView The view to which the point is relative
+ * @param toView The view into which the point should be mapped
+ * @param pt The point
+ */
+ private void mapPoint(View fromView, View toView, int pt[]) {
+ fromView.getLocationInWindow(mTmpLoc);
+
+ int x = mTmpLoc[0];
+ int y = mTmpLoc[1];
+
+ toView.getLocationInWindow(mTmpLoc);
+ int vX = mTmpLoc[0];
+ int vY = mTmpLoc[1];
+
+ pt[0] += x - vX;
+ pt[1] += y - vY;
+ }
+
+ private void userActivity() {
+ if (mKeyguardHostView != null) {
+ mKeyguardHostView.onUserActivityTimeoutChanged();
+ mKeyguardHostView.userActivity();
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(int scrollState) {
+ if (mKeyguardWidgetPager == null || mChallengeLayout == null) return;
+
+ boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
+
+ if (scrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
+ KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
+ if (frame == null) return;
+
+ if (!challengeOverlapping) {
+ if (!mKeyguardWidgetPager.isPageMoving()) {
+ frame.resetSize();
+ userActivity();
+ } else {
+ mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(mPageListeningToSlider);
+ }
+ }
+ if (frame.isSmall()) {
+ // This is to make sure that if the scroller animation gets cut off midway
+ // that the frame doesn't stay in a partial down position.
+ frame.setFrameHeight(frame.getSmallFrameHeight());
+ }
+ if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
+ frame.hideFrame(this);
+ }
+ updateEdgeSwiping();
+
+ if (mChallengeLayout.isChallengeShowing()) {
+ mKeyguardSecurityContainer.onResume(KeyguardSecurityView.VIEW_REVEALED);
+ } else {
+ mKeyguardSecurityContainer.onPause();
+ }
+ mPageListeningToSlider = -1;
+ } else if (mLastScrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
+ // Whether dragging or settling, if the last state was idle, we use this signal
+ // to update the current page who will receive events from the sliding challenge.
+ // We resize the frame as appropriate.
+ mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
+ KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
+ if (frame == null) return;
+
+ // Skip showing the frame and shrinking the widget if we are
+ if (!mChallengeLayout.isBouncing()) {
+ if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
+ frame.showFrame(this);
+ }
+
+ // As soon as the security begins sliding, the widget becomes small (if it wasn't
+ // small to begin with).
+ if (!frame.isSmall()) {
+ // We need to fetch the final page, in case the pages are in motion.
+ mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
+ frame.shrinkWidget(false);
+ }
+ } else {
+ if (!frame.isSmall()) {
+ // We need to fetch the final page, in case the pages are in motion.
+ mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
+ }
+ }
+
+ // View is on the move. Pause the security view until it completes.
+ mKeyguardSecurityContainer.onPause();
+ }
+ mLastScrollState = scrollState;
+ }
+
+ @Override
+ public void onScrollPositionChanged(float scrollPosition, int challengeTop) {
+ mChallengeTop = challengeTop;
+ KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
+ if (frame != null && mLastScrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
+ frame.adjustFrame(getChallengeTopRelativeToFrame(frame, mChallengeTop));
+ }
+ }
+
+ private Runnable mHideHintsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mKeyguardWidgetPager != null) {
+ mKeyguardWidgetPager.hideOutlinesAndSidePages();
+ }
+ }
+ };
+
+ public void showUsabilityHints() {
+ mMainQueue.postDelayed( new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardSecurityContainer.showUsabilityHint();
+ }
+ } , SCREEN_ON_RING_HINT_DELAY);
+ mKeyguardWidgetPager.showInitialPageHints();
+ if (mHideHintsRunnable != null) {
+ mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION);
+ }
+ }
+
+ // ChallengeLayout.OnBouncerStateChangedListener
+ @Override
+ public void onBouncerStateChanged(boolean bouncerActive) {
+ if (bouncerActive) {
+ mKeyguardWidgetPager.zoomOutToBouncer();
+ } else {
+ mKeyguardWidgetPager.zoomInFromBouncer();
+ if (mKeyguardHostView != null) {
+ mKeyguardHostView.setOnDismissAction(null);
+ }
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetCarousel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetCarousel.java
new file mode 100644
index 0000000..98b31b7
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetCarousel.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import java.util.ArrayList;
+
+public class KeyguardWidgetCarousel extends KeyguardWidgetPager {
+
+ private float mAdjacentPagesAngle;
+ private static float MAX_SCROLL_PROGRESS = 1.3f;
+ private static float CAMERA_DISTANCE = 10000;
+ protected AnimatorSet mChildrenTransformsAnimator;
+ float[] mTmpTransform = new float[3];
+
+ public KeyguardWidgetCarousel(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardWidgetCarousel(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardWidgetCarousel(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mAdjacentPagesAngle = context.getResources().getInteger(R.integer.kg_carousel_angle);
+ }
+
+ protected float getMaxScrollProgress() {
+ return MAX_SCROLL_PROGRESS;
+ }
+
+ public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
+ View child = getChildAt(index);
+ if (child == null) return 0f;
+
+ boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1;
+ float scrollProgress = getScrollProgress(screenCenter, child, index);
+
+ if (isOverScrollChild(index, scrollProgress)) {
+ return 1.0f;
+ } else if ((showSidePages && inVisibleRange) || index == getNextPage()) {
+ scrollProgress = getBoundedScrollProgress(screenCenter, child, index);
+ float alpha = 1.0f - 1.0f * Math.abs(scrollProgress / MAX_SCROLL_PROGRESS);
+ return alpha;
+ } else {
+ return 0f;
+ }
+ }
+
+ public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) {
+ boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1;
+ if (inVisibleRange) {
+ return super.getOutlineAlphaForPage(screenCenter, index, showSidePages);
+ } else {
+ return 0f;
+ }
+ }
+
+ private void updatePageAlphaValues(int screenCenter) {
+ if (mChildrenOutlineFadeAnimation != null) {
+ mChildrenOutlineFadeAnimation.cancel();
+ mChildrenOutlineFadeAnimation = null;
+ }
+ boolean showSidePages = mShowingInitialHints || isPageMoving();
+ if (!isReordering(false)) {
+ for (int i = 0; i < getChildCount(); i++) {
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+ if (child != null) {
+ float outlineAlpha = getOutlineAlphaForPage(screenCenter, i, showSidePages);
+ float contentAlpha = getAlphaForPage(screenCenter, i,showSidePages);
+ child.setBackgroundAlpha(outlineAlpha);
+ child.setContentAlpha(contentAlpha);
+ }
+ }
+ }
+ }
+
+ public void showInitialPageHints() {
+ mShowingInitialHints = true;
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ boolean inVisibleRange = i >= getNextPage() - 1 && i <= getNextPage() + 1;
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+ if (inVisibleRange) {
+ child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
+ child.setContentAlpha(1f);
+ } else {
+ child.setBackgroundAlpha(0f);
+ child.setContentAlpha(0f);
+ }
+ }
+ }
+
+ @Override
+ protected void screenScrolled(int screenCenter) {
+ mScreenCenter = screenCenter;
+ updatePageAlphaValues(screenCenter);
+ if (isReordering(false)) return;
+ for (int i = 0; i < getChildCount(); i++) {
+ KeyguardWidgetFrame v = getWidgetPageAt(i);
+ float scrollProgress = getScrollProgress(screenCenter, v, i);
+ float boundedProgress = getBoundedScrollProgress(screenCenter, v, i);
+ if (v == mDragView || v == null) continue;
+ v.setCameraDistance(CAMERA_DISTANCE);
+
+ if (isOverScrollChild(i, scrollProgress)) {
+ v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress);
+ v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0);
+ } else {
+ int width = v.getMeasuredWidth();
+ float pivotX = (width / 2f) + boundedProgress * (width / 2f);
+ float pivotY = v.getMeasuredHeight() / 2;
+ float rotationY = - mAdjacentPagesAngle * boundedProgress;
+ v.setPivotX(pivotX);
+ v.setPivotY(pivotY);
+ v.setRotationY(rotationY);
+ v.setOverScrollAmount(0f, false);
+ }
+ float alpha = v.getAlpha();
+ // If the view has 0 alpha, we set it to be invisible so as to prevent
+ // it from accepting touches
+ if (alpha == 0) {
+ v.setVisibility(INVISIBLE);
+ } else if (v.getVisibility() != VISIBLE) {
+ v.setVisibility(VISIBLE);
+ }
+ }
+ }
+
+ void animatePagesToNeutral() {
+ if (mChildrenTransformsAnimator != null) {
+ mChildrenTransformsAnimator.cancel();
+ mChildrenTransformsAnimator = null;
+ }
+
+ int count = getChildCount();
+ PropertyValuesHolder alpha;
+ PropertyValuesHolder outlineAlpha;
+ PropertyValuesHolder rotationY;
+ ArrayList<Animator> anims = new ArrayList<Animator>();
+
+ for (int i = 0; i < count; i++) {
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+ boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1);
+ if (!inVisibleRange) {
+ child.setRotationY(0f);
+ }
+ alpha = PropertyValuesHolder.ofFloat("contentAlpha", 1.0f);
+ outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha",
+ KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
+ rotationY = PropertyValuesHolder.ofFloat("rotationY", 0f);
+ ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha, rotationY);
+ child.setVisibility(VISIBLE);
+ if (!inVisibleRange) {
+ a.setInterpolator(mSlowFadeInterpolator);
+ }
+ anims.add(a);
+ }
+
+ int duration = REORDERING_ZOOM_IN_OUT_DURATION;
+ mChildrenTransformsAnimator = new AnimatorSet();
+ mChildrenTransformsAnimator.playTogether(anims);
+
+ mChildrenTransformsAnimator.setDuration(duration);
+ mChildrenTransformsAnimator.start();
+ }
+
+ private void getTransformForPage(int screenCenter, int index, float[] transform) {
+ View child = getChildAt(index);
+ float boundedProgress = getBoundedScrollProgress(screenCenter, child, index);
+ float rotationY = - mAdjacentPagesAngle * boundedProgress;
+ int width = child.getMeasuredWidth();
+ float pivotX = (width / 2f) + boundedProgress * (width / 2f);
+ float pivotY = child.getMeasuredHeight() / 2;
+
+ transform[0] = pivotX;
+ transform[1] = pivotY;
+ transform[2] = rotationY;
+ }
+
+ Interpolator mFastFadeInterpolator = new Interpolator() {
+ Interpolator mInternal = new DecelerateInterpolator(1.5f);
+ float mFactor = 2.5f;
+ @Override
+ public float getInterpolation(float input) {
+ return mInternal.getInterpolation(Math.min(mFactor * input, 1f));
+ }
+ };
+
+ Interpolator mSlowFadeInterpolator = new Interpolator() {
+ Interpolator mInternal = new AccelerateInterpolator(1.5f);
+ float mFactor = 1.3f;
+ @Override
+ public float getInterpolation(float input) {
+ input -= (1 - 1 / mFactor);
+ input = mFactor * Math.max(input, 0f);
+ return mInternal.getInterpolation(input);
+ }
+ };
+
+ void animatePagesToCarousel() {
+ if (mChildrenTransformsAnimator != null) {
+ mChildrenTransformsAnimator.cancel();
+ mChildrenTransformsAnimator = null;
+ }
+
+ int count = getChildCount();
+ PropertyValuesHolder alpha;
+ PropertyValuesHolder outlineAlpha;
+ PropertyValuesHolder rotationY;
+ PropertyValuesHolder pivotX;
+ PropertyValuesHolder pivotY;
+ ArrayList<Animator> anims = new ArrayList<Animator>();
+
+ for (int i = 0; i < count; i++) {
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+ float finalAlpha = getAlphaForPage(mScreenCenter, i, true);
+ float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i, true);
+ getTransformForPage(mScreenCenter, i, mTmpTransform);
+
+ boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1);
+
+ ObjectAnimator a;
+ alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalAlpha);
+ outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", finalOutlineAlpha);
+ pivotX = PropertyValuesHolder.ofFloat("pivotX", mTmpTransform[0]);
+ pivotY = PropertyValuesHolder.ofFloat("pivotY", mTmpTransform[1]);
+ rotationY = PropertyValuesHolder.ofFloat("rotationY", mTmpTransform[2]);
+
+ if (inVisibleRange) {
+ // for the central pages we animate into a rotated state
+ a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha,
+ pivotX, pivotY, rotationY);
+ } else {
+ a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha);
+ a.setInterpolator(mFastFadeInterpolator);
+ }
+ anims.add(a);
+ }
+
+ int duration = REORDERING_ZOOM_IN_OUT_DURATION;
+ mChildrenTransformsAnimator = new AnimatorSet();
+ mChildrenTransformsAnimator.playTogether(anims);
+
+ mChildrenTransformsAnimator.setDuration(duration);
+ mChildrenTransformsAnimator.start();
+ }
+
+ protected void reorderStarting() {
+ mViewStateManager.fadeOutSecurity(REORDERING_ZOOM_IN_OUT_DURATION);
+ animatePagesToNeutral();
+ }
+
+ protected boolean zoomIn(final Runnable onCompleteRunnable) {
+ animatePagesToCarousel();
+ return super.zoomIn(onCompleteRunnable);
+ }
+
+ @Override
+ protected void onEndReordering() {
+ super.onEndReordering();
+ mViewStateManager.fadeInSecurity(REORDERING_ZOOM_IN_OUT_DURATION);
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
new file mode 100644
index 0000000..81f6221
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+
+public class KeyguardWidgetFrame extends FrameLayout {
+ private final static PorterDuffXfermode sAddBlendMode =
+ new PorterDuffXfermode(PorterDuff.Mode.ADD);
+
+ static final float OUTLINE_ALPHA_MULTIPLIER = 0.6f;
+ static final int HOVER_OVER_DELETE_DROP_TARGET_OVERLAY_COLOR = 0x99FF0000;
+
+ // Temporarily disable this for the time being until we know why the gfx is messing up
+ static final boolean ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY = true;
+
+ private int mGradientColor;
+ private LinearGradient mForegroundGradient;
+ private LinearGradient mLeftToRightGradient;
+ private LinearGradient mRightToLeftGradient;
+ private Paint mGradientPaint = new Paint();
+ boolean mLeftToRight = true;
+
+ private float mOverScrollAmount = 0f;
+ private final Rect mForegroundRect = new Rect();
+ private int mForegroundAlpha = 0;
+ private CheckLongPressHelper mLongPressHelper;
+ private Animator mFrameFade;
+ private boolean mIsSmall = false;
+ private Handler mWorkerHandler;
+
+ private float mBackgroundAlpha;
+ private float mContentAlpha;
+ private float mBackgroundAlphaMultiplier = 1.0f;
+ private Drawable mBackgroundDrawable;
+ private Rect mBackgroundRect = new Rect();
+
+ // These variables are all needed in order to size things properly before we're actually
+ // measured.
+ private int mSmallWidgetHeight;
+ private int mSmallFrameHeight;
+ private boolean mWidgetLockedSmall = false;
+ private int mMaxChallengeTop = -1;
+ private int mFrameStrokeAdjustment;
+ private boolean mPerformAppWidgetSizeUpdateOnBootComplete;
+
+ // This will hold the width value before we've actually been measured
+ private int mFrameHeight;
+
+ private boolean mIsHoveringOverDeleteDropTarget;
+
+ // Multiple callers may try and adjust the alpha of the frame. When a caller shows
+ // the outlines, we give that caller control, and nobody else can fade them out.
+ // This prevents animation conflicts.
+ private Object mBgAlphaController;
+
+ public KeyguardWidgetFrame(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardWidgetFrame(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ mLongPressHelper = new CheckLongPressHelper(this);
+
+ Resources res = context.getResources();
+ // TODO: this padding should really correspond to the padding embedded in the background
+ // drawable (ie. outlines).
+ float density = res.getDisplayMetrics().density;
+ int padding = (int) (res.getDisplayMetrics().density * 8);
+ setPadding(padding, padding, padding, padding);
+
+ mFrameStrokeAdjustment = 2 + (int) (2 * density);
+
+ // This will be overriden on phones based on the current security mode, however on tablets
+ // we need to specify a height.
+ mSmallWidgetHeight =
+ res.getDimensionPixelSize(R.dimen.kg_small_widget_height);
+ mBackgroundDrawable = res.getDrawable(R.drawable.kg_widget_bg_padded);
+ mGradientColor = res.getColor(R.color.kg_widget_pager_gradient);
+ mGradientPaint.setXfermode(sAddBlendMode);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ cancelLongPress();
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
+
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
+ }
+
+ private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onBootCompleted() {
+ if (mPerformAppWidgetSizeUpdateOnBootComplete) {
+ performAppWidgetSizeCallbacksIfNecessary();
+ mPerformAppWidgetSizeUpdateOnBootComplete = false;
+ }
+ }
+ };
+
+ void setIsHoveringOverDeleteDropTarget(boolean isHovering) {
+ if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) {
+ if (mIsHoveringOverDeleteDropTarget != isHovering) {
+ mIsHoveringOverDeleteDropTarget = isHovering;
+ invalidate();
+ }
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // Watch for longpress events at this level to make sure
+ // users can always pick up this widget
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLongPressHelper.postCheckForLongPress(ev);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mLongPressHelper.onMove(ev);
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mLongPressHelper.cancelLongPress();
+ break;
+ }
+
+ // Otherwise continue letting touch events fall through to children
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // Watch for longpress events at this level to make sure
+ // users can always pick up this widget
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_MOVE:
+ mLongPressHelper.onMove(ev);
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mLongPressHelper.cancelLongPress();
+ break;
+ }
+
+ // We return true here to ensure that we will get cancel / up signal
+ // even if none of our children have requested touch.
+ return true;
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ cancelLongPress();
+ }
+
+ @Override
+ public void cancelLongPress() {
+ super.cancelLongPress();
+ mLongPressHelper.cancelLongPress();
+ }
+
+
+ private void drawGradientOverlay(Canvas c) {
+ mGradientPaint.setShader(mForegroundGradient);
+ mGradientPaint.setAlpha(mForegroundAlpha);
+ c.drawRect(mForegroundRect, mGradientPaint);
+ }
+
+ private void drawHoveringOverDeleteOverlay(Canvas c) {
+ if (mIsHoveringOverDeleteDropTarget) {
+ c.drawColor(HOVER_OVER_DELETE_DROP_TARGET_OVERLAY_COLOR);
+ }
+ }
+
+ protected void drawBg(Canvas canvas) {
+ if (mBackgroundAlpha > 0.0f) {
+ Drawable bg = mBackgroundDrawable;
+
+ bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
+ bg.setBounds(mBackgroundRect);
+ bg.draw(canvas);
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) {
+ canvas.save();
+ }
+ drawBg(canvas);
+ super.dispatchDraw(canvas);
+ drawGradientOverlay(canvas);
+ if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) {
+ drawHoveringOverDeleteOverlay(canvas);
+ canvas.restore();
+ }
+ }
+
+ /**
+ * Because this view has fading outlines, it is essential that we enable hardware
+ * layers on the content (child) so that updating the alpha of the outlines doesn't
+ * result in the content layer being recreated.
+ */
+ public void enableHardwareLayersForContent() {
+ View widget = getContent();
+ if (widget != null) {
+ widget.setLayerType(LAYER_TYPE_HARDWARE, null);
+ }
+ }
+
+ /**
+ * Because this view has fading outlines, it is essential that we enable hardware
+ * layers on the content (child) so that updating the alpha of the outlines doesn't
+ * result in the content layer being recreated.
+ */
+ public void disableHardwareLayersForContent() {
+ View widget = getContent();
+ if (widget != null) {
+ widget.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ }
+
+ public void enableHardwareLayers() {
+ setLayerType(LAYER_TYPE_HARDWARE, null);
+ }
+
+ public void disableHardwareLayers() {
+ setLayerType(LAYER_TYPE_NONE, null);
+ }
+
+ public View getContent() {
+ return getChildAt(0);
+ }
+
+ public int getContentAppWidgetId() {
+ View content = getContent();
+ if (content instanceof AppWidgetHostView) {
+ return ((AppWidgetHostView) content).getAppWidgetId();
+ } else if (content instanceof KeyguardStatusView) {
+ return ((KeyguardStatusView) content).getAppWidgetId();
+ } else {
+ return AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+ }
+
+ public float getBackgroundAlpha() {
+ return mBackgroundAlpha;
+ }
+
+ public void setBackgroundAlphaMultiplier(float multiplier) {
+ if (Float.compare(mBackgroundAlphaMultiplier, multiplier) != 0) {
+ mBackgroundAlphaMultiplier = multiplier;
+ invalidate();
+ }
+ }
+
+ public float getBackgroundAlphaMultiplier() {
+ return mBackgroundAlphaMultiplier;
+ }
+
+ public void setBackgroundAlpha(float alpha) {
+ if (Float.compare(mBackgroundAlpha, alpha) != 0) {
+ mBackgroundAlpha = alpha;
+ invalidate();
+ }
+ }
+
+ public float getContentAlpha() {
+ return mContentAlpha;
+ }
+
+ public void setContentAlpha(float alpha) {
+ mContentAlpha = alpha;
+ View content = getContent();
+ if (content != null) {
+ content.setAlpha(alpha);
+ }
+ }
+
+ /**
+ * Depending on whether the security is up, the widget size needs to change
+ *
+ * @param height The height of the widget, -1 for full height
+ */
+ private void setWidgetHeight(int height) {
+ boolean needLayout = false;
+ View widget = getContent();
+ if (widget != null) {
+ LayoutParams lp = (LayoutParams) widget.getLayoutParams();
+ if (lp.height != height) {
+ needLayout = true;
+ lp.height = height;
+ }
+ }
+ if (needLayout) {
+ requestLayout();
+ }
+ }
+
+ public void setMaxChallengeTop(int top) {
+ boolean dirty = mMaxChallengeTop != top;
+ mMaxChallengeTop = top;
+ mSmallWidgetHeight = top - getPaddingTop();
+ mSmallFrameHeight = top + getPaddingBottom();
+ if (dirty && mIsSmall) {
+ setWidgetHeight(mSmallWidgetHeight);
+ setFrameHeight(mSmallFrameHeight);
+ } else if (dirty && mWidgetLockedSmall) {
+ setWidgetHeight(mSmallWidgetHeight);
+ }
+ }
+
+ public boolean isSmall() {
+ return mIsSmall;
+ }
+
+ public void adjustFrame(int challengeTop) {
+ int frameHeight = challengeTop + getPaddingBottom();
+ setFrameHeight(frameHeight);
+ }
+
+ public void shrinkWidget(boolean alsoShrinkFrame) {
+ mIsSmall = true;
+ setWidgetHeight(mSmallWidgetHeight);
+
+ if (alsoShrinkFrame) {
+ setFrameHeight(mSmallFrameHeight);
+ }
+ }
+
+ public int getSmallFrameHeight() {
+ return mSmallFrameHeight;
+ }
+
+ public void shrinkWidget() {
+ shrinkWidget(true);
+ }
+
+ public void setWidgetLockedSmall(boolean locked) {
+ if (locked) {
+ setWidgetHeight(mSmallWidgetHeight);
+ }
+ mWidgetLockedSmall = locked;
+ }
+
+ public void resetSize() {
+ mIsSmall = false;
+ if (!mWidgetLockedSmall) {
+ setWidgetHeight(LayoutParams.MATCH_PARENT);
+ }
+ setFrameHeight(getMeasuredHeight());
+ }
+
+ public void setFrameHeight(int height) {
+ mFrameHeight = height;
+ mBackgroundRect.set(0, 0, getMeasuredWidth(), Math.min(mFrameHeight, getMeasuredHeight()));
+ mForegroundRect.set(mFrameStrokeAdjustment, mFrameStrokeAdjustment,getMeasuredWidth() -
+ mFrameStrokeAdjustment, Math.min(getMeasuredHeight(), mFrameHeight) -
+ mFrameStrokeAdjustment);
+ updateGradient();
+ invalidate();
+ }
+
+ public void hideFrame(Object caller) {
+ fadeFrame(caller, false, 0f, KeyguardWidgetPager.CHILDREN_OUTLINE_FADE_OUT_DURATION);
+ }
+
+ public void showFrame(Object caller) {
+ fadeFrame(caller, true, OUTLINE_ALPHA_MULTIPLIER,
+ KeyguardWidgetPager.CHILDREN_OUTLINE_FADE_IN_DURATION);
+ }
+
+ public void fadeFrame(Object caller, boolean takeControl, float alpha, int duration) {
+ if (takeControl) {
+ mBgAlphaController = caller;
+ }
+
+ if (mBgAlphaController != caller && mBgAlphaController != null) {
+ return;
+ }
+
+ if (mFrameFade != null) {
+ mFrameFade.cancel();
+ mFrameFade = null;
+ }
+ PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", alpha);
+ mFrameFade = ObjectAnimator.ofPropertyValuesHolder(this, bgAlpha);
+ mFrameFade.setDuration(duration);
+ mFrameFade.start();
+ }
+
+ private void updateGradient() {
+ float x0 = mLeftToRight ? 0 : mForegroundRect.width();
+ float x1 = mLeftToRight ? mForegroundRect.width(): 0;
+ mLeftToRightGradient = new LinearGradient(x0, 0f, x1, 0f,
+ mGradientColor, 0, Shader.TileMode.CLAMP);
+ mRightToLeftGradient = new LinearGradient(x1, 0f, x0, 0f,
+ mGradientColor, 0, Shader.TileMode.CLAMP);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ if (!mIsSmall) {
+ mFrameHeight = h;
+ }
+
+ // mFrameStrokeAdjustment is a cludge to prevent the overlay from drawing outside the
+ // rounded rect background.
+ mForegroundRect.set(mFrameStrokeAdjustment, mFrameStrokeAdjustment,
+ w - mFrameStrokeAdjustment, Math.min(h, mFrameHeight) - mFrameStrokeAdjustment);
+
+ mBackgroundRect.set(0, 0, getMeasuredWidth(), Math.min(h, mFrameHeight));
+ updateGradient();
+ invalidate();
+ }
+
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ performAppWidgetSizeCallbacksIfNecessary();
+ }
+
+ private void performAppWidgetSizeCallbacksIfNecessary() {
+ View content = getContent();
+ if (!(content instanceof AppWidgetHostView)) return;
+
+ if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
+ mPerformAppWidgetSizeUpdateOnBootComplete = true;
+ return;
+ }
+
+ // TODO: there's no reason to force the AppWidgetHostView to catch duplicate size calls.
+ // We can do that even more cheaply here. It's not an issue right now since we're in the
+ // system process and hence no binder calls.
+ AppWidgetHostView awhv = (AppWidgetHostView) content;
+ float density = getResources().getDisplayMetrics().density;
+
+ int width = (int) (content.getMeasuredWidth() / density);
+ int height = (int) (content.getMeasuredHeight() / density);
+ awhv.updateAppWidgetSize(null, width, height, width, height, true);
+ }
+
+ void setOverScrollAmount(float r, boolean left) {
+ if (Float.compare(mOverScrollAmount, r) != 0) {
+ mOverScrollAmount = r;
+ mForegroundGradient = left ? mLeftToRightGradient : mRightToLeftGradient;
+ mForegroundAlpha = (int) Math.round((0.5f * r * 255));
+
+ // We bump up the alpha of the outline to hide the fact that the overlay is drawing
+ // over the rounded part of the frame.
+ float bgAlpha = Math.min(OUTLINE_ALPHA_MULTIPLIER + r * (1 - OUTLINE_ALPHA_MULTIPLIER),
+ 1f);
+ setBackgroundAlpha(bgAlpha);
+ invalidate();
+ }
+ }
+
+ public void onActive(boolean isActive) {
+ // hook for subclasses
+ }
+
+ public boolean onUserInteraction(MotionEvent event) {
+ // hook for subclasses
+ return false;
+ }
+
+ public void onBouncerShowing(boolean showing) {
+ // hook for subclasses
+ }
+
+ public void setWorkerHandler(Handler workerHandler) {
+ mWorkerHandler = workerHandler;
+ }
+
+ public Handler getWorkerHandler() {
+ return mWorkerHandler;
+ }
+
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
new file mode 100644
index 0000000..c566457
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
@@ -0,0 +1,926 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.TextClock;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import java.util.ArrayList;
+import java.util.TimeZone;
+
+public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener,
+ OnLongClickListener, ChallengeLayout.OnBouncerStateChangedListener {
+
+ ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
+ private static float CAMERA_DISTANCE = 10000;
+ protected static float OVERSCROLL_MAX_ROTATION = 30;
+ private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
+
+ private static final int FLAG_HAS_LOCAL_HOUR = 0x1;
+ private static final int FLAG_HAS_LOCAL_MINUTE = 0x2;
+
+ protected KeyguardViewStateManager mViewStateManager;
+ private LockPatternUtils mLockPatternUtils;
+
+ // Related to the fading in / out background outlines
+ public static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
+ public static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
+ protected AnimatorSet mChildrenOutlineFadeAnimation;
+ protected int mScreenCenter;
+ private boolean mHasMeasure = false;
+ boolean showHintsAfterLayout = false;
+
+ private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
+ private static final String TAG = "KeyguardWidgetPager";
+ private boolean mCenterSmallWidgetsVertically;
+
+ private int mPage = 0;
+ private Callbacks mCallbacks;
+
+ private int mWidgetToResetAfterFadeOut;
+ protected boolean mShowingInitialHints = false;
+
+ // A temporary handle to the Add-Widget view
+ private View mAddWidgetView;
+ private int mLastWidthMeasureSpec;
+ private int mLastHeightMeasureSpec;
+
+ // Bouncer
+ private int mBouncerZoomInOutDuration = 250;
+ private float BOUNCER_SCALE_FACTOR = 0.67f;
+
+ // Background worker thread: used here for persistence, also made available to widget frames
+ private final HandlerThread mBackgroundWorkerThread;
+ private final Handler mBackgroundWorkerHandler;
+
+ public KeyguardWidgetPager(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardWidgetPager(Context context) {
+ this(null, null, 0);
+ }
+
+ public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+
+ setPageSwitchListener(this);
+
+ mBackgroundWorkerThread = new HandlerThread("KeyguardWidgetPager Worker");
+ mBackgroundWorkerThread.start();
+ mBackgroundWorkerHandler = new Handler(mBackgroundWorkerThread.getLooper());
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ // Clean up the worker thread
+ mBackgroundWorkerThread.quit();
+ }
+
+ public void setViewStateManager(KeyguardViewStateManager viewStateManager) {
+ mViewStateManager = viewStateManager;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils l) {
+ mLockPatternUtils = l;
+ }
+
+ @Override
+ public void onPageSwitching(View newPage, int newPageIndex) {
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageSwitching(newPage, newPageIndex);
+ }
+ }
+
+ @Override
+ public void onPageSwitched(View newPage, int newPageIndex) {
+ boolean showingClock = false;
+ if (newPage instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) newPage;
+ if (vg.getChildAt(0) instanceof KeyguardStatusView) {
+ showingClock = true;
+ }
+ }
+
+ if (newPage != null &&
+ findClockInHierarchy(newPage) == (FLAG_HAS_LOCAL_HOUR | FLAG_HAS_LOCAL_MINUTE)) {
+ showingClock = true;
+ }
+
+ // Disable the status bar clock if we're showing the default status widget
+ if (showingClock) {
+ setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
+ } else {
+ setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
+ }
+
+ // Extend the display timeout if the user switches pages
+ if (mPage != newPageIndex) {
+ int oldPageIndex = mPage;
+ mPage = newPageIndex;
+ userActivity();
+ KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex);
+ if (oldWidgetPage != null) {
+ oldWidgetPage.onActive(false);
+ }
+ KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex);
+ if (newWidgetPage != null) {
+ newWidgetPage.onActive(true);
+ newWidgetPage.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ newWidgetPage.requestAccessibilityFocus();
+ }
+ if (mParent != null && AccessibilityManager.getInstance(mContext).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_SCROLLED);
+ onInitializeAccessibilityEvent(event);
+ onPopulateAccessibilityEvent(event);
+ mParent.requestSendAccessibilityEvent(this, event);
+ }
+ }
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageSwitched(newPage, newPageIndex);
+ }
+ }
+
+ @Override
+ public void sendAccessibilityEvent(int eventType) {
+ if (eventType != AccessibilityEvent.TYPE_VIEW_SCROLLED || isPageMoving()) {
+ super.sendAccessibilityEvent(eventType);
+ }
+ }
+
+ private void updateWidgetFramesImportantForAccessibility() {
+ final int pageCount = getPageCount();
+ for (int i = 0; i < pageCount; i++) {
+ KeyguardWidgetFrame frame = getWidgetPageAt(i);
+ updateWidgetFrameImportantForAccessibility(frame);
+ }
+ }
+
+ private void updateWidgetFrameImportantForAccessibility(KeyguardWidgetFrame frame) {
+ if (frame.getContentAlpha() <= 0) {
+ frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ } else {
+ frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+ }
+
+ private void userActivity() {
+ if (mCallbacks != null) {
+ mCallbacks.onUserActivityTimeoutChanged();
+ mCallbacks.userActivity();
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return captureUserInteraction(ev) || super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return captureUserInteraction(ev) || super.onInterceptTouchEvent(ev);
+ }
+
+ private boolean captureUserInteraction(MotionEvent ev) {
+ KeyguardWidgetFrame currentWidgetPage = getWidgetPageAt(getCurrentPage());
+ return currentWidgetPage != null && currentWidgetPage.onUserInteraction(ev);
+ }
+
+ public void showPagingFeedback() {
+ // Nothing yet.
+ }
+
+ public long getUserActivityTimeout() {
+ View page = getPageAt(mPage);
+ if (page instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) page;
+ View view = vg.getChildAt(0);
+ if (!(view instanceof KeyguardStatusView)
+ && !(view instanceof KeyguardMultiUserSelectorView)) {
+ return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT;
+ }
+ }
+ return -1;
+ }
+
+ public void setCallbacks(Callbacks callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ public interface Callbacks {
+ public void userActivity();
+ public void onUserActivityTimeoutChanged();
+ public void onAddView(View v);
+ public void onRemoveView(View v, boolean deletePermanently);
+ public void onRemoveViewAnimationCompleted();
+ }
+
+ public void addWidget(View widget) {
+ addWidget(widget, -1);
+ }
+
+ public void onRemoveView(View v, final boolean deletePermanently) {
+ final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+ if (mCallbacks != null) {
+ mCallbacks.onRemoveView(v, deletePermanently);
+ }
+ mBackgroundWorkerHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mLockPatternUtils.removeAppWidget(appWidgetId);
+ }
+ });
+ }
+
+ @Override
+ public void onRemoveViewAnimationCompleted() {
+ if (mCallbacks != null) {
+ mCallbacks.onRemoveViewAnimationCompleted();
+ }
+ }
+
+ public void onAddView(View v, final int index) {
+ final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+ final int[] pagesRange = new int[mTempVisiblePagesRange.length];
+ getVisiblePages(pagesRange);
+ boundByReorderablePages(true, pagesRange);
+ if (mCallbacks != null) {
+ mCallbacks.onAddView(v);
+ }
+ // Subtract from the index to take into account pages before the reorderable
+ // pages (e.g. the "add widget" page)
+ mBackgroundWorkerHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mLockPatternUtils.addAppWidget(appWidgetId, index - pagesRange[0]);
+ }
+ });
+ }
+
+ /*
+ * We wrap widgets in a special frame which handles drawing the over scroll foreground.
+ */
+ public void addWidget(View widget, int pageIndex) {
+ KeyguardWidgetFrame frame;
+ // All views contained herein should be wrapped in a KeyguardWidgetFrame
+ if (!(widget instanceof KeyguardWidgetFrame)) {
+ frame = new KeyguardWidgetFrame(getContext());
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT);
+ lp.gravity = Gravity.TOP;
+
+ // The framework adds a default padding to AppWidgetHostView. We don't need this padding
+ // for the Keyguard, so we override it to be 0.
+ widget.setPadding(0, 0, 0, 0);
+ frame.addView(widget, lp);
+
+ // We set whether or not this widget supports vertical resizing.
+ if (widget instanceof AppWidgetHostView) {
+ AppWidgetHostView awhv = (AppWidgetHostView) widget;
+ AppWidgetProviderInfo info = awhv.getAppWidgetInfo();
+ if ((info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
+ frame.setWidgetLockedSmall(false);
+ } else {
+ // Lock the widget to be small.
+ frame.setWidgetLockedSmall(true);
+ if (mCenterSmallWidgetsVertically) {
+ lp.gravity = Gravity.CENTER;
+ }
+ }
+ }
+ } else {
+ frame = (KeyguardWidgetFrame) widget;
+ }
+
+ ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ frame.setOnLongClickListener(this);
+ frame.setWorkerHandler(mBackgroundWorkerHandler);
+
+ if (pageIndex == -1) {
+ addView(frame, pageLp);
+ } else {
+ addView(frame, pageIndex, pageLp);
+ }
+
+ // Update the frame content description.
+ View content = (widget == frame) ? frame.getContent() : widget;
+ if (content != null) {
+ String contentDescription = mContext.getString(
+ R.string.keyguard_accessibility_widget,
+ content.getContentDescription());
+ frame.setContentDescription(contentDescription);
+ }
+ updateWidgetFrameImportantForAccessibility(frame);
+ }
+
+ /**
+ * Use addWidget() instead.
+ * @deprecated
+ */
+ @Override
+ public void addView(View child, int index) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, index);
+ }
+
+ /**
+ * Use addWidget() instead.
+ * @deprecated
+ */
+ @Override
+ public void addView(View child, int width, int height) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, width, height);
+ }
+
+ /**
+ * Use addWidget() instead.
+ * @deprecated
+ */
+ @Override
+ public void addView(View child, LayoutParams params) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, params);
+ }
+
+ /**
+ * Use addWidget() instead.
+ * @deprecated
+ */
+ @Override
+ public void addView(View child, int index, LayoutParams params) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, index, params);
+ }
+
+ private void enforceKeyguardWidgetFrame(View child) {
+ if (!(child instanceof KeyguardWidgetFrame)) {
+ throw new IllegalArgumentException(
+ "KeyguardWidgetPager children must be KeyguardWidgetFrames");
+ }
+ }
+
+ public KeyguardWidgetFrame getWidgetPageAt(int index) {
+ // This is always a valid cast as we've guarded the ability to
+ return (KeyguardWidgetFrame) getChildAt(index);
+ }
+
+ protected void onUnhandledTap(MotionEvent ev) {
+ showPagingFeedback();
+ }
+
+ @Override
+ protected void onPageBeginMoving() {
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageBeginMoving();
+ }
+ if (!isReordering(false)) {
+ showOutlinesAndSidePages();
+ }
+ userActivity();
+ }
+
+ @Override
+ protected void onPageEndMoving() {
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageEndMoving();
+ }
+
+ // In the reordering case, the pages will be faded appropriately on completion
+ // of the zoom in animation.
+ if (!isReordering(false)) {
+ hideOutlinesAndSidePages();
+ }
+ }
+
+ protected void enablePageContentLayers() {
+ int children = getChildCount();
+ for (int i = 0; i < children; i++) {
+ getWidgetPageAt(i).enableHardwareLayersForContent();
+ }
+ }
+
+ protected void disablePageContentLayers() {
+ int children = getChildCount();
+ for (int i = 0; i < children; i++) {
+ getWidgetPageAt(i).disableHardwareLayersForContent();
+ }
+ }
+
+ /*
+ * This interpolator emulates the rate at which the perceived scale of an object changes
+ * as its distance from a camera increases. When this interpolator is applied to a scale
+ * animation on a view, it evokes the sense that the object is shrinking due to moving away
+ * from the camera.
+ */
+ static class ZInterpolator implements TimeInterpolator {
+ private float focalLength;
+
+ public ZInterpolator(float foc) {
+ focalLength = foc;
+ }
+
+ public float getInterpolation(float input) {
+ return (1.0f - focalLength / (focalLength + input)) /
+ (1.0f - focalLength / (focalLength + 1.0f));
+ }
+ }
+
+ @Override
+ protected void overScroll(float amount) {
+ acceleratedOverScroll(amount);
+ }
+
+ float backgroundAlphaInterpolator(float r) {
+ return Math.min(1f, r);
+ }
+
+ private void updatePageAlphaValues(int screenCenter) {
+ }
+
+ public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
+ if (showSidePages) {
+ return 1f;
+ } else {
+ return index == mCurrentPage ? 1.0f : 0f;
+ }
+ }
+
+ public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) {
+ if (showSidePages) {
+ return getAlphaForPage(screenCenter, index, showSidePages)
+ * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER;
+ } else {
+ return 0f;
+ }
+ }
+
+ protected boolean isOverScrollChild(int index, float scrollProgress) {
+ boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
+ return (isInOverscroll && (index == 0 && scrollProgress < 0 ||
+ index == getChildCount() - 1 && scrollProgress > 0));
+ }
+
+ @Override
+ protected void screenScrolled(int screenCenter) {
+ mScreenCenter = screenCenter;
+ updatePageAlphaValues(screenCenter);
+ for (int i = 0; i < getChildCount(); i++) {
+ KeyguardWidgetFrame v = getWidgetPageAt(i);
+ if (v == mDragView) continue;
+ if (v != null) {
+ float scrollProgress = getScrollProgress(screenCenter, v, i);
+
+ v.setCameraDistance(mDensity * CAMERA_DISTANCE);
+
+ if (isOverScrollChild(i, scrollProgress) && PERFORM_OVERSCROLL_ROTATION) {
+ float pivotX = v.getMeasuredWidth() / 2;
+ float pivotY = v.getMeasuredHeight() / 2;
+ v.setPivotX(pivotX);
+ v.setPivotY(pivotY);
+ v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress);
+ v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0);
+ } else {
+ v.setRotationY(0f);
+ v.setOverScrollAmount(0, false);
+ }
+
+ float alpha = v.getAlpha();
+ // If the view has 0 alpha, we set it to be invisible so as to prevent
+ // it from accepting touches
+ if (alpha == 0) {
+ v.setVisibility(INVISIBLE);
+ } else if (v.getVisibility() != VISIBLE) {
+ v.setVisibility(VISIBLE);
+ }
+ }
+ }
+ }
+
+ public boolean isWidgetPage(int pageIndex) {
+ if (pageIndex < 0 || pageIndex >= getChildCount()) {
+ return false;
+ }
+ View v = getChildAt(pageIndex);
+ if (v != null && v instanceof KeyguardWidgetFrame) {
+ KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v;
+ return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the bounded set of pages that are re-orderable. The range is fully inclusive.
+ */
+ @Override
+ void boundByReorderablePages(boolean isReordering, int[] range) {
+ if (isReordering) {
+ // Remove non-widget pages from the range
+ while (range[1] >= range[0] && !isWidgetPage(range[1])) {
+ range[1]--;
+ }
+ while (range[0] <= range[1] && !isWidgetPage(range[0])) {
+ range[0]++;
+ }
+ }
+ }
+
+ protected void reorderStarting() {
+ showOutlinesAndSidePages();
+ }
+
+ @Override
+ protected void onStartReordering() {
+ super.onStartReordering();
+ enablePageContentLayers();
+ reorderStarting();
+ }
+
+ @Override
+ protected void onEndReordering() {
+ super.onEndReordering();
+ hideOutlinesAndSidePages();
+ }
+
+ void showOutlinesAndSidePages() {
+ animateOutlinesAndSidePages(true);
+ }
+
+ void hideOutlinesAndSidePages() {
+ animateOutlinesAndSidePages(false);
+ }
+
+ void updateChildrenContentAlpha(float sidePageAlpha) {
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+ if (i != mCurrentPage) {
+ child.setBackgroundAlpha(sidePageAlpha);
+ child.setContentAlpha(0f);
+ } else {
+ child.setBackgroundAlpha(0f);
+ child.setContentAlpha(1f);
+ }
+ }
+ }
+
+ public void showInitialPageHints() {
+ mShowingInitialHints = true;
+ updateChildrenContentAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
+ }
+
+ @Override
+ void setCurrentPage(int currentPage) {
+ super.setCurrentPage(currentPage);
+ updateChildrenContentAlpha(0.0f);
+ updateWidgetFramesImportantForAccessibility();
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mHasMeasure = false;
+ }
+
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mLastWidthMeasureSpec = widthMeasureSpec;
+ mLastHeightMeasureSpec = heightMeasureSpec;
+
+ int maxChallengeTop = -1;
+ View parent = (View) getParent();
+ boolean challengeShowing = false;
+ // Widget pages need to know where the top of the sliding challenge is so that they
+ // now how big the widget should be when the challenge is up. We compute it here and
+ // then propagate it to each of our children.
+ if (parent.getParent() instanceof SlidingChallengeLayout) {
+ SlidingChallengeLayout scl = (SlidingChallengeLayout) parent.getParent();
+ int top = scl.getMaxChallengeTop();
+
+ // This is a bit evil, but we need to map a coordinate relative to the SCL into a
+ // coordinate relative to our children, hence we subtract the top padding.s
+ maxChallengeTop = top - getPaddingTop();
+ challengeShowing = scl.isChallengeShowing();
+
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ KeyguardWidgetFrame frame = getWidgetPageAt(i);
+ frame.setMaxChallengeTop(maxChallengeTop);
+ // On the very first measure pass, if the challenge is showing, we need to make sure
+ // that the widget on the current page is small.
+ if (challengeShowing && i == mCurrentPage && !mHasMeasure) {
+ frame.shrinkWidget();
+ }
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mHasMeasure = true;
+ }
+
+ void animateOutlinesAndSidePages(final boolean show) {
+ animateOutlinesAndSidePages(show, -1);
+ }
+
+ public void setWidgetToResetOnPageFadeOut(int widget) {
+ mWidgetToResetAfterFadeOut = widget;
+ }
+
+ public int getWidgetToResetOnPageFadeOut() {
+ return mWidgetToResetAfterFadeOut;
+ }
+
+ void animateOutlinesAndSidePages(final boolean show, int duration) {
+ if (mChildrenOutlineFadeAnimation != null) {
+ mChildrenOutlineFadeAnimation.cancel();
+ mChildrenOutlineFadeAnimation = null;
+ }
+ int count = getChildCount();
+ PropertyValuesHolder alpha;
+ ArrayList<Animator> anims = new ArrayList<Animator>();
+
+ if (duration == -1) {
+ duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION :
+ CHILDREN_OUTLINE_FADE_OUT_DURATION;
+ }
+
+ int curPage = getNextPage();
+ for (int i = 0; i < count; i++) {
+ float finalContentAlpha;
+ if (show) {
+ finalContentAlpha = getAlphaForPage(mScreenCenter, i, true);
+ } else if (!show && i == curPage) {
+ finalContentAlpha = 1f;
+ } else {
+ finalContentAlpha = 0f;
+ }
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+
+ alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalContentAlpha);
+ ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha);
+ anims.add(a);
+
+ float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i, true) : 0f;
+ child.fadeFrame(this, show, finalOutlineAlpha, duration);
+ }
+
+ mChildrenOutlineFadeAnimation = new AnimatorSet();
+ mChildrenOutlineFadeAnimation.playTogether(anims);
+
+ mChildrenOutlineFadeAnimation.setDuration(duration);
+ mChildrenOutlineFadeAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (show) {
+ enablePageContentLayers();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!show) {
+ disablePageContentLayers();
+ KeyguardWidgetFrame frame = getWidgetPageAt(mWidgetToResetAfterFadeOut);
+ if (frame != null && !(frame == getWidgetPageAt(mCurrentPage) &&
+ mViewStateManager.isChallengeOverlapping())) {
+ frame.resetSize();
+ }
+ mWidgetToResetAfterFadeOut = -1;
+ mShowingInitialHints = false;
+ }
+ updateWidgetFramesImportantForAccessibility();
+ }
+ });
+ mChildrenOutlineFadeAnimation.start();
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ // Disallow long pressing to reorder if the challenge is showing
+ boolean isChallengeOverlapping = mViewStateManager.isChallengeShowing() &&
+ mViewStateManager.isChallengeOverlapping();
+ if (!isChallengeOverlapping && startReordering()) {
+ return true;
+ }
+ return false;
+ }
+
+ public void removeWidget(View view) {
+ if (view instanceof KeyguardWidgetFrame) {
+ removeView(view);
+ } else {
+ // Assume view was wrapped by a KeyguardWidgetFrame in KeyguardWidgetPager#addWidget().
+ // This supports legacy hard-coded "widgets" like KeyguardTransportControlView.
+ int pos = getWidgetPageIndex(view);
+ if (pos != -1) {
+ KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(pos);
+ frame.removeView(view);
+ removeView(frame);
+ } else {
+ Slog.w(TAG, "removeWidget() can't find:" + view);
+ }
+ }
+ }
+
+ public int getWidgetPageIndex(View view) {
+ if (view instanceof KeyguardWidgetFrame) {
+ return indexOfChild(view);
+ } else {
+ // View was wrapped by a KeyguardWidgetFrame by KeyguardWidgetPager#addWidget()
+ return indexOfChild((KeyguardWidgetFrame)view.getParent());
+ }
+ }
+
+ @Override
+ protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {
+ KeyguardWidgetFrame child = getWidgetPageAt(viewIndex);
+ child.setIsHoveringOverDeleteDropTarget(isHovering);
+ }
+
+ // ChallengeLayout.OnBouncerStateChangedListener
+ @Override
+ public void onBouncerStateChanged(boolean bouncerActive) {
+ if (bouncerActive) {
+ zoomOutToBouncer();
+ } else {
+ zoomInFromBouncer();
+ }
+ }
+
+ void setBouncerAnimationDuration(int duration) {
+ mBouncerZoomInOutDuration = duration;
+ }
+
+ // Zoom in after the bouncer is dismissed
+ void zoomInFromBouncer() {
+ if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+ mZoomInOutAnim.cancel();
+ }
+ final View currentPage = getPageAt(getCurrentPage());
+ if (currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f) {
+ mZoomInOutAnim = new AnimatorSet();
+ mZoomInOutAnim.playTogether(
+ ObjectAnimator.ofFloat(currentPage, "scaleX", 1f),
+ ObjectAnimator.ofFloat(currentPage , "scaleY", 1f));
+ mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
+ mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
+ mZoomInOutAnim.start();
+ }
+ if (currentPage instanceof KeyguardWidgetFrame) {
+ ((KeyguardWidgetFrame)currentPage).onBouncerShowing(false);
+ }
+ }
+
+ // Zoom out after the bouncer is initiated
+ void zoomOutToBouncer() {
+ if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+ mZoomInOutAnim.cancel();
+ }
+ int curPage = getCurrentPage();
+ View currentPage = getPageAt(curPage);
+ if (shouldSetTopAlignedPivotForWidget(curPage)) {
+ currentPage.setPivotY(0);
+ // Note: we are working around the issue that setting the x-pivot to the same value as it
+ // was does not actually work.
+ currentPage.setPivotX(0);
+ currentPage.setPivotX(currentPage.getMeasuredWidth() / 2);
+ }
+ if (!(currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f)) {
+ mZoomInOutAnim = new AnimatorSet();
+ mZoomInOutAnim.playTogether(
+ ObjectAnimator.ofFloat(currentPage, "scaleX", BOUNCER_SCALE_FACTOR),
+ ObjectAnimator.ofFloat(currentPage, "scaleY", BOUNCER_SCALE_FACTOR));
+ mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
+ mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
+ mZoomInOutAnim.start();
+ }
+ if (currentPage instanceof KeyguardWidgetFrame) {
+ ((KeyguardWidgetFrame)currentPage).onBouncerShowing(true);
+ }
+ }
+
+ void setAddWidgetEnabled(boolean enabled) {
+ if (mAddWidgetView != null && enabled) {
+ addView(mAddWidgetView, 0);
+ // We need to force measure the PagedView so that the calls to update the scroll
+ // position below work
+ measure(mLastWidthMeasureSpec, mLastHeightMeasureSpec);
+ // Bump up the current page to account for the addition of the new page
+ setCurrentPage(mCurrentPage + 1);
+ mAddWidgetView = null;
+ } else if (mAddWidgetView == null && !enabled) {
+ View addWidget = findViewById(R.id.keyguard_add_widget);
+ if (addWidget != null) {
+ mAddWidgetView = addWidget;
+ removeView(addWidget);
+ }
+ }
+ }
+
+ boolean isAddPage(int pageIndex) {
+ View v = getChildAt(pageIndex);
+ return v != null && v.getId() == R.id.keyguard_add_widget;
+ }
+
+ boolean isCameraPage(int pageIndex) {
+ View v = getChildAt(pageIndex);
+ return v != null && v instanceof CameraWidgetFrame;
+ }
+
+ @Override
+ protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
+ return !isCameraPage(childIndex) && super.shouldSetTopAlignedPivotForWidget(childIndex);
+ }
+
+ /**
+ * Search given {@link View} hierarchy for {@link TextClock} instances that
+ * show various time components. Returns combination of
+ * {@link #FLAG_HAS_LOCAL_HOUR} and {@link #FLAG_HAS_LOCAL_MINUTE}.
+ */
+ private static int findClockInHierarchy(View view) {
+ if (view instanceof TextClock) {
+ return getClockFlags((TextClock) view);
+ } else if (view instanceof ViewGroup) {
+ int flags = 0;
+ final ViewGroup group = (ViewGroup) view;
+ final int size = group.getChildCount();
+ for (int i = 0; i < size; i++) {
+ flags |= findClockInHierarchy(group.getChildAt(i));
+ }
+ return flags;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Return combination of {@link #FLAG_HAS_LOCAL_HOUR} and
+ * {@link #FLAG_HAS_LOCAL_MINUTE} describing the time represented described
+ * by the given {@link TextClock}.
+ */
+ private static int getClockFlags(TextClock clock) {
+ int flags = 0;
+
+ final String timeZone = clock.getTimeZone();
+ if (timeZone != null && !TimeZone.getDefault().equals(TimeZone.getTimeZone(timeZone))) {
+ // Ignore clocks showing another timezone
+ return 0;
+ }
+
+ final CharSequence format = clock.getFormat();
+ final char hour = clock.is24HourModeEnabled() ? DateFormat.HOUR_OF_DAY
+ : DateFormat.HOUR;
+
+ if (DateFormat.hasDesignator(format, hour)) {
+ flags |= FLAG_HAS_LOCAL_HOUR;
+ }
+ if (DateFormat.hasDesignator(format, DateFormat.MINUTE)) {
+ flags |= FLAG_HAS_LOCAL_MINUTE;
+ }
+
+ return flags;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/LiftToActivateListener.java b/packages/Keyguard/src/com/android/keyguard/LiftToActivateListener.java
new file mode 100644
index 0000000..e59602b
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/LiftToActivateListener.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+
+/**
+ * Hover listener that implements lift-to-activate interaction for
+ * accessibility. May be added to multiple views.
+ */
+class LiftToActivateListener implements View.OnHoverListener {
+ /** Manager used to query accessibility enabled state. */
+ private final AccessibilityManager mAccessibilityManager;
+
+ private boolean mCachedClickableState;
+
+ public LiftToActivateListener(Context context) {
+ mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ }
+
+ @Override
+ public boolean onHover(View v, MotionEvent event) {
+ // When touch exploration is turned on, lifting a finger while
+ // inside the view bounds should perform a click action.
+ if (mAccessibilityManager.isEnabled()
+ && mAccessibilityManager.isTouchExplorationEnabled()) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ // Lift-to-type temporarily disables double-tap
+ // activation by setting the view as not clickable.
+ mCachedClickableState = v.isClickable();
+ v.setClickable(false);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ if ((x > v.getPaddingLeft()) && (y > v.getPaddingTop())
+ && (x < v.getWidth() - v.getPaddingRight())
+ && (y < v.getHeight() - v.getPaddingBottom())) {
+ v.performClick();
+ }
+ v.setClickable(mCachedClickableState);
+ break;
+ }
+ }
+
+ // Pass the event to View.onHoverEvent() to handle accessibility.
+ v.onHoverEvent(event);
+
+ // Consume the event so it doesn't fall through to other views.
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java
new file mode 100644
index 0000000..8fd39c0
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayout {
+ private static final String TAG = "MultiPaneChallengeLayout";
+
+ final int mOrientation;
+ private boolean mIsBouncing;
+
+ public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
+ public static final int VERTICAL = LinearLayout.VERTICAL;
+ public static final int ANIMATE_BOUNCE_DURATION = 350;
+
+ private KeyguardSecurityContainer mChallengeView;
+ private View mUserSwitcherView;
+ private View mScrimView;
+ private OnBouncerStateChangedListener mBouncerListener;
+
+ private final Rect mTempRect = new Rect();
+ private final Rect mZeroPadding = new Rect();
+
+ private final DisplayMetrics mDisplayMetrics;
+
+ private final OnClickListener mScrimClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ hideBouncer();
+ }
+ };
+
+ public MultiPaneChallengeLayout(Context context) {
+ this(context, null);
+ }
+
+ public MultiPaneChallengeLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public MultiPaneChallengeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.MultiPaneChallengeLayout, defStyleAttr, 0);
+ mOrientation = a.getInt(R.styleable.MultiPaneChallengeLayout_android_orientation,
+ HORIZONTAL);
+ a.recycle();
+
+ final Resources res = getResources();
+ mDisplayMetrics = res.getDisplayMetrics();
+
+ setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ }
+
+ @Override
+ public boolean isChallengeShowing() {
+ return true;
+ }
+
+ @Override
+ public boolean isChallengeOverlapping() {
+ return false;
+ }
+
+ @Override
+ public void showChallenge(boolean b) {
+ }
+
+ @Override
+ public int getBouncerAnimationDuration() {
+ return ANIMATE_BOUNCE_DURATION;
+ }
+
+ @Override
+ public void showBouncer() {
+ if (mIsBouncing) return;
+ mIsBouncing = true;
+ if (mScrimView != null) {
+ if (mChallengeView != null) {
+ mChallengeView.showBouncer(ANIMATE_BOUNCE_DURATION);
+ }
+
+ Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f);
+ anim.setDuration(ANIMATE_BOUNCE_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mScrimView.setVisibility(VISIBLE);
+ }
+ });
+ anim.start();
+ }
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(true);
+ }
+ }
+
+ @Override
+ public void hideBouncer() {
+ if (!mIsBouncing) return;
+ mIsBouncing = false;
+ if (mScrimView != null) {
+ if (mChallengeView != null) {
+ mChallengeView.hideBouncer(ANIMATE_BOUNCE_DURATION);
+ }
+
+ Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f);
+ anim.setDuration(ANIMATE_BOUNCE_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrimView.setVisibility(INVISIBLE);
+ }
+ });
+ anim.start();
+ }
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(false);
+ }
+ }
+
+ @Override
+ public boolean isBouncing() {
+ return mIsBouncing;
+ }
+
+ @Override
+ public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
+ mBouncerListener = listener;
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ if (mIsBouncing && child != mChallengeView) {
+ // Clear out of the bouncer if the user tries to move focus outside of
+ // the security challenge view.
+ hideBouncer();
+ }
+ super.requestChildFocus(child, focused);
+ }
+
+ void setScrimView(View scrim) {
+ if (mScrimView != null) {
+ mScrimView.setOnClickListener(null);
+ }
+ mScrimView = scrim;
+ mScrimView.setAlpha(mIsBouncing ? 1.0f : 0.0f);
+ mScrimView.setVisibility(mIsBouncing ? VISIBLE : INVISIBLE);
+ mScrimView.setFocusable(true);
+ mScrimView.setOnClickListener(mScrimClickListener);
+ }
+
+ private int getVirtualHeight(LayoutParams lp, int height, int heightUsed) {
+ int virtualHeight = height;
+ final View root = getRootView();
+ if (root != null) {
+ // This calculation is super dodgy and relies on several assumptions.
+ // Specifically that the root of the window will be padded in for insets
+ // and that the window is LAYOUT_IN_SCREEN.
+ virtualHeight = mDisplayMetrics.heightPixels - root.getPaddingTop();
+ }
+ if (lp.childType == LayoutParams.CHILD_TYPE_WIDGET ||
+ lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
+ // Always measure the widget pager/user switcher as if there were no IME insets
+ // on the window. We want to avoid resizing widgets when possible as it can
+ // be ugly/expensive. This lets us simply clip them instead.
+ return virtualHeight - heightUsed;
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_PAGE_DELETE_DROP_TARGET) {
+ return height;
+ }
+ return Math.min(virtualHeight - heightUsed, height);
+ }
+
+ @Override
+ protected void onMeasure(final int widthSpec, final int heightSpec) {
+ if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
+ MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
+ throw new IllegalArgumentException(
+ "MultiPaneChallengeLayout must be measured with an exact size");
+ }
+
+ final int width = MeasureSpec.getSize(widthSpec);
+ final int height = MeasureSpec.getSize(heightSpec);
+ setMeasuredDimension(width, height);
+
+ int widthUsed = 0;
+ int heightUsed = 0;
+
+ // First pass. Find the challenge view and measure the user switcher,
+ // which consumes space in the layout.
+ mChallengeView = null;
+ mUserSwitcherView = null;
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
+ if (mChallengeView != null) {
+ throw new IllegalStateException(
+ "There may only be one child of type challenge");
+ }
+ if (!(child instanceof KeyguardSecurityContainer)) {
+ throw new IllegalArgumentException(
+ "Challenge must be a KeyguardSecurityContainer");
+ }
+ mChallengeView = (KeyguardSecurityContainer) child;
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
+ if (mUserSwitcherView != null) {
+ throw new IllegalStateException(
+ "There may only be one child of type userSwitcher");
+ }
+ mUserSwitcherView = child;
+
+ if (child.getVisibility() == GONE) continue;
+
+ int adjustedWidthSpec = widthSpec;
+ int adjustedHeightSpec = heightSpec;
+ if (lp.maxWidth >= 0) {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(lp.maxWidth, width), MeasureSpec.EXACTLY);
+ }
+ if (lp.maxHeight >= 0) {
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(lp.maxHeight, height), MeasureSpec.EXACTLY);
+ }
+ // measureChildWithMargins will resolve layout direction for the LayoutParams
+ measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
+
+ // Only subtract out space from one dimension. Favor vertical.
+ // Offset by 1.5x to add some balance along the other edge.
+ if (Gravity.isVertical(lp.gravity)) {
+ heightUsed += child.getMeasuredHeight() * 1.5f;
+ } else if (Gravity.isHorizontal(lp.gravity)) {
+ widthUsed += child.getMeasuredWidth() * 1.5f;
+ }
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
+ setScrimView(child);
+ child.measure(widthSpec, heightSpec);
+ }
+ }
+
+ // Second pass. Measure everything that's left.
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER ||
+ lp.childType == LayoutParams.CHILD_TYPE_SCRIM ||
+ child.getVisibility() == GONE) {
+ // Don't need to measure GONE children, and the user switcher was already measured.
+ continue;
+ }
+
+ final int virtualHeight = getVirtualHeight(lp, height, heightUsed);
+
+ int adjustedWidthSpec;
+ int adjustedHeightSpec;
+ if (lp.centerWithinArea > 0) {
+ if (mOrientation == HORIZONTAL) {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ (int) ((width - widthUsed) * lp.centerWithinArea + 0.5f),
+ MeasureSpec.EXACTLY);
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ virtualHeight, MeasureSpec.EXACTLY);
+ } else {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ width - widthUsed, MeasureSpec.EXACTLY);
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ (int) (virtualHeight * lp.centerWithinArea + 0.5f),
+ MeasureSpec.EXACTLY);
+ }
+ } else {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ width - widthUsed, MeasureSpec.EXACTLY);
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ virtualHeight, MeasureSpec.EXACTLY);
+ }
+ if (lp.maxWidth >= 0) {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(lp.maxWidth, MeasureSpec.getSize(adjustedWidthSpec)),
+ MeasureSpec.EXACTLY);
+ }
+ if (lp.maxHeight >= 0) {
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(lp.maxHeight, MeasureSpec.getSize(adjustedHeightSpec)),
+ MeasureSpec.EXACTLY);
+ }
+
+ measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final Rect padding = mTempRect;
+ padding.left = getPaddingLeft();
+ padding.top = getPaddingTop();
+ padding.right = getPaddingRight();
+ padding.bottom = getPaddingBottom();
+ final int width = r - l;
+ final int height = b - t;
+
+ // Reserve extra space in layout for the user switcher by modifying
+ // local padding during this layout pass
+ if (mUserSwitcherView != null && mUserSwitcherView.getVisibility() != GONE) {
+ layoutWithGravity(width, height, mUserSwitcherView, padding, true);
+ }
+
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ // We did the user switcher above if we have one.
+ if (child == mUserSwitcherView || child.getVisibility() == GONE) continue;
+
+ if (child == mScrimView) {
+ child.layout(0, 0, width, height);
+ continue;
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_PAGE_DELETE_DROP_TARGET) {
+ layoutWithGravity(width, height, child, mZeroPadding, false);
+ continue;
+ }
+
+ layoutWithGravity(width, height, child, padding, false);
+ }
+ }
+
+ private void layoutWithGravity(int width, int height, View child, Rect padding,
+ boolean adjustPadding) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ final int heightUsed = padding.top + padding.bottom - getPaddingTop() - getPaddingBottom();
+ height = getVirtualHeight(lp, height, heightUsed);
+
+ final int gravity = Gravity.getAbsoluteGravity(lp.gravity, getLayoutDirection());
+
+ final boolean fixedLayoutSize = lp.centerWithinArea > 0;
+ final boolean fixedLayoutHorizontal = fixedLayoutSize && mOrientation == HORIZONTAL;
+ final boolean fixedLayoutVertical = fixedLayoutSize && mOrientation == VERTICAL;
+
+ final int adjustedWidth;
+ final int adjustedHeight;
+ if (fixedLayoutHorizontal) {
+ final int paddedWidth = width - padding.left - padding.right;
+ adjustedWidth = (int) (paddedWidth * lp.centerWithinArea + 0.5f);
+ adjustedHeight = height;
+ } else if (fixedLayoutVertical) {
+ final int paddedHeight = height - getPaddingTop() - getPaddingBottom();
+ adjustedWidth = width;
+ adjustedHeight = (int) (paddedHeight * lp.centerWithinArea + 0.5f);
+ } else {
+ adjustedWidth = width;
+ adjustedHeight = height;
+ }
+
+ final boolean isVertical = Gravity.isVertical(gravity);
+ final boolean isHorizontal = Gravity.isHorizontal(gravity);
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+
+ int left = padding.left;
+ int top = padding.top;
+ int right = left + childWidth;
+ int bottom = top + childHeight;
+ switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
+ case Gravity.TOP:
+ top = fixedLayoutVertical ?
+ padding.top + (adjustedHeight - childHeight) / 2 : padding.top;
+ bottom = top + childHeight;
+ if (adjustPadding && isVertical) {
+ padding.top = bottom;
+ padding.bottom += childHeight / 2;
+ }
+ break;
+ case Gravity.BOTTOM:
+ bottom = fixedLayoutVertical
+ ? padding.top + height - (adjustedHeight - childHeight) / 2
+ : padding.top + height;
+ top = bottom - childHeight;
+ if (adjustPadding && isVertical) {
+ padding.bottom = height - top;
+ padding.top += childHeight / 2;
+ }
+ break;
+ case Gravity.CENTER_VERTICAL:
+ top = padding.top + (height - childHeight) / 2;
+ bottom = top + childHeight;
+ break;
+ }
+ switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.LEFT:
+ left = fixedLayoutHorizontal ?
+ padding.left + (adjustedWidth - childWidth) / 2 : padding.left;
+ right = left + childWidth;
+ if (adjustPadding && isHorizontal && !isVertical) {
+ padding.left = right;
+ padding.right += childWidth / 2;
+ }
+ break;
+ case Gravity.RIGHT:
+ right = fixedLayoutHorizontal
+ ? width - padding.right - (adjustedWidth - childWidth) / 2
+ : width - padding.right;
+ left = right - childWidth;
+ if (adjustPadding && isHorizontal && !isVertical) {
+ padding.right = width - left;
+ padding.left += childWidth / 2;
+ }
+ break;
+ case Gravity.CENTER_HORIZONTAL:
+ final int paddedWidth = width - padding.left - padding.right;
+ left = (paddedWidth - childWidth) / 2;
+ right = left + childWidth;
+ break;
+ }
+ child.layout(left, top, right, bottom);
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs, this);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
+ p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
+ new LayoutParams(p);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams();
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ public static class LayoutParams extends MarginLayoutParams {
+
+ public float centerWithinArea = 0;
+
+ public int childType = 0;
+
+ public static final int CHILD_TYPE_NONE = 0;
+ public static final int CHILD_TYPE_WIDGET = 1;
+ public static final int CHILD_TYPE_CHALLENGE = 2;
+ public static final int CHILD_TYPE_USER_SWITCHER = 3;
+ public static final int CHILD_TYPE_SCRIM = 4;
+ public static final int CHILD_TYPE_PAGE_DELETE_DROP_TARGET = 7;
+
+ public int gravity = Gravity.NO_GRAVITY;
+
+ public int maxWidth = -1;
+ public int maxHeight = -1;
+
+ public LayoutParams() {
+ this(WRAP_CONTENT, WRAP_CONTENT);
+ }
+
+ LayoutParams(Context c, AttributeSet attrs, MultiPaneChallengeLayout parent) {
+ super(c, attrs);
+
+ final TypedArray a = c.obtainStyledAttributes(attrs,
+ R.styleable.MultiPaneChallengeLayout_Layout);
+
+ centerWithinArea = a.getFloat(
+ R.styleable.MultiPaneChallengeLayout_Layout_layout_centerWithinArea, 0);
+ childType = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_childType,
+ CHILD_TYPE_NONE);
+ gravity = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_gravity,
+ Gravity.NO_GRAVITY);
+ maxWidth = a.getDimensionPixelSize(
+ R.styleable.MultiPaneChallengeLayout_Layout_layout_maxWidth, -1);
+ maxHeight = a.getDimensionPixelSize(
+ R.styleable.MultiPaneChallengeLayout_Layout_layout_maxHeight, -1);
+
+ // Default gravity settings based on type and parent orientation
+ if (gravity == Gravity.NO_GRAVITY) {
+ if (parent.mOrientation == HORIZONTAL) {
+ switch (childType) {
+ case CHILD_TYPE_WIDGET:
+ gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
+ break;
+ case CHILD_TYPE_CHALLENGE:
+ gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
+ break;
+ case CHILD_TYPE_USER_SWITCHER:
+ gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ break;
+ }
+ } else {
+ switch (childType) {
+ case CHILD_TYPE_WIDGET:
+ gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ break;
+ case CHILD_TYPE_CHALLENGE:
+ gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ break;
+ case CHILD_TYPE_USER_SWITCHER:
+ gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ break;
+ }
+ }
+ }
+
+ a.recycle();
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(LayoutParams source) {
+ this((MarginLayoutParams) source);
+
+ centerWithinArea = source.centerWithinArea;
+ childType = source.childType;
+ gravity = source.gravity;
+ maxWidth = source.maxWidth;
+ maxHeight = source.maxHeight;
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/MultiUserAvatarCache.java b/packages/Keyguard/src/com/android/keyguard/MultiUserAvatarCache.java
new file mode 100644
index 0000000..9930e72
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/MultiUserAvatarCache.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.graphics.drawable.Drawable;
+
+import java.util.HashMap;
+
+public class MultiUserAvatarCache {
+
+ private final HashMap<Integer, Drawable> mCache;
+
+ public MultiUserAvatarCache() {
+ mCache = new HashMap<Integer, Drawable>();
+ }
+
+ public void clear(int userId) {
+ mCache.remove(userId);
+ }
+
+ public Drawable get(int userId) {
+ return mCache.get(userId);
+ }
+
+ public void put(int userId, Drawable image) {
+ mCache.put(userId, image);
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/NumPadKey.java b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
new file mode 100644
index 0000000..532670f
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.SpannableStringBuilder;
+import android.text.style.TextAppearanceSpan;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.internal.widget.LockPatternUtils;
+
+public class NumPadKey extends Button {
+ // list of "ABC", etc per digit, starting with '0'
+ static String sKlondike[];
+
+ int mDigit = -1;
+ int mTextViewResId;
+ TextView mTextView = null;
+ boolean mEnableHaptics;
+
+ private View.OnClickListener mListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View thisView) {
+ if (mTextView == null) {
+ if (mTextViewResId > 0) {
+ final View v = NumPadKey.this.getRootView().findViewById(mTextViewResId);
+ if (v != null && v instanceof TextView) {
+ mTextView = (TextView) v;
+ }
+ }
+ }
+ // check for time-based lockouts
+ if (mTextView != null && mTextView.isEnabled()) {
+ mTextView.append(String.valueOf(mDigit));
+ }
+ doHapticKeyClick();
+ }
+ };
+
+ public NumPadKey(Context context) {
+ this(context, null);
+ }
+
+ public NumPadKey(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NumPadKey(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumPadKey);
+ mDigit = a.getInt(R.styleable.NumPadKey_digit, mDigit);
+ setTextViewResId(a.getResourceId(R.styleable.NumPadKey_textView, 0));
+
+ setOnClickListener(mListener);
+ setOnHoverListener(new LiftToActivateListener(context));
+ setAccessibilityDelegate(new ObscureSpeechDelegate(context));
+
+ mEnableHaptics = new LockPatternUtils(context).isTactileFeedbackEnabled();
+
+ SpannableStringBuilder builder = new SpannableStringBuilder();
+ builder.append(String.valueOf(mDigit));
+ if (mDigit >= 0) {
+ if (sKlondike == null) {
+ sKlondike = context.getResources().getStringArray(
+ R.array.lockscreen_num_pad_klondike);
+ }
+ if (sKlondike != null && sKlondike.length > mDigit) {
+ final String extra = sKlondike[mDigit];
+ final int extraLen = extra.length();
+ if (extraLen > 0) {
+ builder.append(" ");
+ builder.append(extra);
+ builder.setSpan(
+ new TextAppearanceSpan(context, R.style.TextAppearance_NumPadKey_Klondike),
+ builder.length()-extraLen, builder.length(), 0);
+ }
+ }
+ }
+ setText(builder);
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ // Reset the "announced headset" flag when detached.
+ ObscureSpeechDelegate.sAnnouncedHeadset = false;
+ }
+
+ public void setTextView(TextView tv) {
+ mTextView = tv;
+ }
+
+ public void setTextViewResId(int resId) {
+ mTextView = null;
+ mTextViewResId = resId;
+ }
+
+ // Cause a VIRTUAL_KEY vibration
+ public void doHapticKeyClick() {
+ if (mEnableHaptics) {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/ObscureSpeechDelegate.java b/packages/Keyguard/src/com/android/keyguard/ObscureSpeechDelegate.java
new file mode 100644
index 0000000..573122a
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/ObscureSpeechDelegate.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.AudioManager;
+import android.provider.Settings;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.internal.R;
+
+/**
+ * Accessibility delegate that obscures speech for a view when the user has
+ * not turned on the "speak passwords" preference and is not listening
+ * through headphones.
+ */
+class ObscureSpeechDelegate extends AccessibilityDelegate {
+ /** Whether any client has announced the "headset" notification. */
+ static boolean sAnnouncedHeadset = false;
+
+ private final ContentResolver mContentResolver;
+ private final AudioManager mAudioManager;
+
+ public ObscureSpeechDelegate(Context context) {
+ mContentResolver = context.getContentResolver();
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ @Override
+ public void sendAccessibilityEvent(View host, int eventType) {
+ super.sendAccessibilityEvent(host, eventType);
+
+ // Play the "headset required" announcement the first time the user
+ // places accessibility focus on a key.
+ if ((eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED)
+ && !sAnnouncedHeadset && shouldObscureSpeech()) {
+ sAnnouncedHeadset = true;
+ host.announceForAccessibility(host.getContext().getString(
+ R.string.keyboard_headset_required_to_hear_password));
+ }
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(host, event);
+
+ if ((event.getEventType() != AccessibilityEvent.TYPE_ANNOUNCEMENT)
+ && shouldObscureSpeech()) {
+ event.getText().clear();
+ event.setContentDescription(host.getContext().getString(
+ R.string.keyboard_password_character_no_headset));
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+
+ if (shouldObscureSpeech()) {
+ final Context ctx = host.getContext();
+ info.setText(null);
+ info.setContentDescription(
+ ctx.getString(R.string.keyboard_password_character_no_headset));
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private boolean shouldObscureSpeech() {
+ // The user can optionally force speaking passwords.
+ if (Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0) {
+ return false;
+ }
+
+ // Always speak if the user is listening through headphones.
+ if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) {
+ return false;
+ }
+
+ // Don't speak since this key is used to type a password.
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/packages/Keyguard/src/com/android/keyguard/PagedView.java b/packages/Keyguard/src/com/android/keyguard/PagedView.java
new file mode 100644
index 0000000..881d14d
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/PagedView.java
@@ -0,0 +1,2567 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.Scroller;
+
+import java.util.ArrayList;
+
+/**
+ * An abstraction of the original Workspace which supports browsing through a
+ * sequential list of "pages"
+ */
+public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
+ private static final String TAG = "WidgetPagedView";
+ private static final boolean DEBUG = false;
+ protected static final int INVALID_PAGE = -1;
+
+ // the min drag distance for a fling to register, to prevent random page shifts
+ private static final int MIN_LENGTH_FOR_FLING = 25;
+
+ protected static final int PAGE_SNAP_ANIMATION_DURATION = 750;
+ protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
+ protected static final float NANOTIME_DIV = 1000000000.0f;
+
+ private static final float OVERSCROLL_ACCELERATE_FACTOR = 2;
+ private static final float OVERSCROLL_DAMP_FACTOR = 0.14f;
+
+ private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
+ // The page is moved more than halfway, automatically move to the next page on touch up.
+ private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
+
+ // The following constants need to be scaled based on density. The scaled versions will be
+ // assigned to the corresponding member variables below.
+ private static final int FLING_THRESHOLD_VELOCITY = 500;
+ private static final int MIN_SNAP_VELOCITY = 1500;
+ private static final int MIN_FLING_VELOCITY = 250;
+
+ // We are disabling touch interaction of the widget region for factory ROM.
+ private static final boolean DISABLE_TOUCH_INTERACTION = false;
+ private static final boolean DISABLE_TOUCH_SIDE_PAGES = true;
+ private static final boolean DISABLE_FLING_TO_DELETE = false;
+
+ static final int AUTOMATIC_PAGE_SPACING = -1;
+
+ protected int mFlingThresholdVelocity;
+ protected int mMinFlingVelocity;
+ protected int mMinSnapVelocity;
+
+ protected float mDensity;
+ protected float mSmoothingTime;
+ protected float mTouchX;
+
+ protected boolean mFirstLayout = true;
+
+ protected int mCurrentPage;
+ protected int mChildCountOnLastMeasure;
+
+ protected int mNextPage = INVALID_PAGE;
+ protected int mMaxScrollX;
+ protected Scroller mScroller;
+ private VelocityTracker mVelocityTracker;
+
+ private float mParentDownMotionX;
+ private float mParentDownMotionY;
+ private float mDownMotionX;
+ private float mDownMotionY;
+ private float mDownScrollX;
+ protected float mLastMotionX;
+ protected float mLastMotionXRemainder;
+ protected float mLastMotionY;
+ protected float mTotalMotionX;
+ private int mLastScreenCenter = -1;
+ private int[] mChildOffsets;
+ private int[] mChildRelativeOffsets;
+ private int[] mChildOffsetsWithLayoutScale;
+
+ protected final static int TOUCH_STATE_REST = 0;
+ protected final static int TOUCH_STATE_SCROLLING = 1;
+ protected final static int TOUCH_STATE_PREV_PAGE = 2;
+ protected final static int TOUCH_STATE_NEXT_PAGE = 3;
+ protected final static int TOUCH_STATE_REORDERING = 4;
+
+ protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
+
+ protected int mTouchState = TOUCH_STATE_REST;
+ protected boolean mForceScreenScrolled = false;
+
+ protected OnLongClickListener mLongClickListener;
+
+ protected int mTouchSlop;
+ private int mPagingTouchSlop;
+ private int mMaximumVelocity;
+ private int mMinimumWidth;
+ protected int mPageSpacing;
+ protected int mCellCountX = 0;
+ protected int mCellCountY = 0;
+ protected boolean mAllowOverScroll = true;
+ protected int mUnboundedScrollX;
+ protected int[] mTempVisiblePagesRange = new int[2];
+ protected boolean mForceDrawAllChildrenNextFrame;
+
+ // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise
+ // it is equal to the scaled overscroll position. We use a separate value so as to prevent
+ // the screens from continuing to translate beyond the normal bounds.
+ protected int mOverScrollX;
+
+ // parameter that adjusts the layout to be optimized for pages with that scale factor
+ protected float mLayoutScale = 1.0f;
+
+ protected static final int INVALID_POINTER = -1;
+
+ protected int mActivePointerId = INVALID_POINTER;
+
+ private PageSwitchListener mPageSwitchListener;
+
+ protected ArrayList<Boolean> mDirtyPageContent;
+
+ // If true, syncPages and syncPageItems will be called to refresh pages
+ protected boolean mContentIsRefreshable = true;
+
+ // If true, modify alpha of neighboring pages as user scrolls left/right
+ protected boolean mFadeInAdjacentScreens = false;
+
+ // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
+ // to switch to a new page
+ protected boolean mUsePagingTouchSlop = true;
+
+ // If true, the subclass should directly update scrollX itself in its computeScroll method
+ // (SmoothPagedView does this)
+ protected boolean mDeferScrollUpdate = false;
+
+ protected boolean mIsPageMoving = false;
+
+ // All syncs and layout passes are deferred until data is ready.
+ protected boolean mIsDataReady = true;
+
+ // Scrolling indicator
+ private ValueAnimator mScrollIndicatorAnimator;
+ private View mScrollIndicator;
+ private int mScrollIndicatorPaddingLeft;
+ private int mScrollIndicatorPaddingRight;
+ private boolean mShouldShowScrollIndicator = false;
+ private boolean mShouldShowScrollIndicatorImmediately = false;
+ protected static final int sScrollIndicatorFadeInDuration = 150;
+ protected static final int sScrollIndicatorFadeOutDuration = 650;
+ protected static final int sScrollIndicatorFlashDuration = 650;
+
+ // The viewport whether the pages are to be contained (the actual view may be larger than the
+ // viewport)
+ private Rect mViewport = new Rect();
+
+ // Reordering
+ // We use the min scale to determine how much to expand the actually PagedView measured
+ // dimensions such that when we are zoomed out, the view is not clipped
+ private int REORDERING_DROP_REPOSITION_DURATION = 200;
+ protected int REORDERING_REORDER_REPOSITION_DURATION = 300;
+ protected int REORDERING_ZOOM_IN_OUT_DURATION = 250;
+ private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 300;
+ private float REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE = 0.1f;
+ private long REORDERING_DELETE_DROP_TARGET_FADE_DURATION = 150;
+ private float mMinScale = 1f;
+ protected View mDragView;
+ protected AnimatorSet mZoomInOutAnim;
+ private Runnable mSidePageHoverRunnable;
+ private int mSidePageHoverIndex = -1;
+ // This variable's scope is only for the duration of startReordering() and endReordering()
+ private boolean mReorderingStarted = false;
+ // This variable's scope is for the duration of startReordering() and after the zoomIn()
+ // animation after endReordering()
+ private boolean mIsReordering;
+ // The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition
+ private int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
+ private int mPostReorderingPreZoomInRemainingAnimationCount;
+ private Runnable mPostReorderingPreZoomInRunnable;
+
+ // Edge swiping
+ private boolean mOnlyAllowEdgeSwipes = false;
+ private boolean mDownEventOnEdge = false;
+ private int mEdgeSwipeRegionSize = 0;
+
+ // Convenience/caching
+ private Matrix mTmpInvMatrix = new Matrix();
+ private float[] mTmpPoint = new float[2];
+ private Rect mTmpRect = new Rect();
+ private Rect mAltTmpRect = new Rect();
+
+ // Fling to delete
+ private int FLING_TO_DELETE_FADE_OUT_DURATION = 350;
+ private float FLING_TO_DELETE_FRICTION = 0.035f;
+ // The degrees specifies how much deviation from the up vector to still consider a fling "up"
+ private float FLING_TO_DELETE_MAX_FLING_DEGREES = 65f;
+ protected int mFlingToDeleteThresholdVelocity = -1400;
+ // Drag to delete
+ private boolean mDeferringForDelete = false;
+ private int DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250;
+ private int DRAG_TO_DELETE_FADE_OUT_DURATION = 350;
+
+ // Drop to delete
+ private View mDeleteDropTarget;
+
+ // Bouncer
+ private boolean mTopAlignPageWhenShrinkingForBouncer = false;
+
+ public interface PageSwitchListener {
+ void onPageSwitching(View newPage, int newPageIndex);
+ void onPageSwitched(View newPage, int newPageIndex);
+ }
+
+ public PagedView(Context context) {
+ this(context, null);
+ }
+
+ public PagedView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PagedView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.PagedView, defStyle, 0);
+ setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
+ mScrollIndicatorPaddingLeft =
+ a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0);
+ mScrollIndicatorPaddingRight =
+ a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
+ a.recycle();
+
+ Resources r = getResources();
+ mEdgeSwipeRegionSize = r.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
+ mTopAlignPageWhenShrinkingForBouncer =
+ r.getBoolean(R.bool.kg_top_align_page_shrink_on_bouncer_visible);
+
+ setHapticFeedbackEnabled(false);
+ init();
+ }
+
+ /**
+ * Initializes various states for this workspace.
+ */
+ protected void init() {
+ mDirtyPageContent = new ArrayList<Boolean>();
+ mDirtyPageContent.ensureCapacity(32);
+ mScroller = new Scroller(getContext(), new ScrollInterpolator());
+ mCurrentPage = 0;
+
+ final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mDensity = getResources().getDisplayMetrics().density;
+
+ // Scale the fling-to-delete threshold by the density
+ mFlingToDeleteThresholdVelocity =
+ (int) (mFlingToDeleteThresholdVelocity * mDensity);
+
+ mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
+ mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
+ mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
+ setOnHierarchyChangeListener(this);
+ }
+
+ void setDeleteDropTarget(View v) {
+ mDeleteDropTarget = v;
+ }
+
+ // Convenience methods to map points from self to parent and vice versa
+ float[] mapPointFromViewToParent(View v, float x, float y) {
+ mTmpPoint[0] = x;
+ mTmpPoint[1] = y;
+ v.getMatrix().mapPoints(mTmpPoint);
+ mTmpPoint[0] += v.getLeft();
+ mTmpPoint[1] += v.getTop();
+ return mTmpPoint;
+ }
+ float[] mapPointFromParentToView(View v, float x, float y) {
+ mTmpPoint[0] = x - v.getLeft();
+ mTmpPoint[1] = y - v.getTop();
+ v.getMatrix().invert(mTmpInvMatrix);
+ mTmpInvMatrix.mapPoints(mTmpPoint);
+ return mTmpPoint;
+ }
+
+ void updateDragViewTranslationDuringDrag() {
+ float x = mLastMotionX - mDownMotionX + getScrollX() - mDownScrollX;
+ float y = mLastMotionY - mDownMotionY;
+ mDragView.setTranslationX(x);
+ mDragView.setTranslationY(y);
+
+ if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): " + x + ", " + y);
+ }
+
+ public void setMinScale(float f) {
+ mMinScale = f;
+ requestLayout();
+ }
+
+ @Override
+ public void setScaleX(float scaleX) {
+ super.setScaleX(scaleX);
+ if (isReordering(true)) {
+ float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
+ mLastMotionX = p[0];
+ mLastMotionY = p[1];
+ updateDragViewTranslationDuringDrag();
+ }
+ }
+
+ // Convenience methods to get the actual width/height of the PagedView (since it is measured
+ // to be larger to account for the minimum possible scale)
+ int getViewportWidth() {
+ return mViewport.width();
+ }
+ int getViewportHeight() {
+ return mViewport.height();
+ }
+
+ // Convenience methods to get the offset ASSUMING that we are centering the pages in the
+ // PagedView both horizontally and vertically
+ int getViewportOffsetX() {
+ return (getMeasuredWidth() - getViewportWidth()) / 2;
+ }
+ int getViewportOffsetY() {
+ return (getMeasuredHeight() - getViewportHeight()) / 2;
+ }
+
+ public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
+ mPageSwitchListener = pageSwitchListener;
+ if (mPageSwitchListener != null) {
+ mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage);
+ }
+ }
+
+ /**
+ * Called by subclasses to mark that data is ready, and that we can begin loading and laying
+ * out pages.
+ */
+ protected void setDataIsReady() {
+ mIsDataReady = true;
+ }
+
+ protected boolean isDataReady() {
+ return mIsDataReady;
+ }
+
+ /**
+ * Returns the index of the currently displayed page.
+ *
+ * @return The index of the currently displayed page.
+ */
+ int getCurrentPage() {
+ return mCurrentPage;
+ }
+
+ int getNextPage() {
+ return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
+ }
+
+ int getPageCount() {
+ return getChildCount();
+ }
+
+ View getPageAt(int index) {
+ return getChildAt(index);
+ }
+
+ protected int indexToPage(int index) {
+ return index;
+ }
+
+ /**
+ * Updates the scroll of the current page immediately to its final scroll position. We use this
+ * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
+ * the previous tab page.
+ */
+ protected void updateCurrentPageScroll() {
+ int offset = getChildOffset(mCurrentPage);
+ int relOffset = getRelativeChildOffset(mCurrentPage);
+ int newX = offset - relOffset;
+ scrollTo(newX, 0);
+ mScroller.setFinalX(newX);
+ mScroller.forceFinished(true);
+ }
+
+ /**
+ * Sets the current page.
+ */
+ void setCurrentPage(int currentPage) {
+ notifyPageSwitching(currentPage);
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ }
+ // don't introduce any checks like mCurrentPage == currentPage here-- if we change the
+ // the default
+ if (getChildCount() == 0) {
+ return;
+ }
+
+ mForceScreenScrolled = true;
+ mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
+ updateCurrentPageScroll();
+ updateScrollingIndicator();
+ notifyPageSwitched();
+ invalidate();
+ }
+
+ public void setOnlyAllowEdgeSwipes(boolean enable) {
+ mOnlyAllowEdgeSwipes = enable;
+ }
+
+ protected void notifyPageSwitching(int whichPage) {
+ if (mPageSwitchListener != null) {
+ mPageSwitchListener.onPageSwitching(getPageAt(whichPage), whichPage);
+ }
+ }
+
+ protected void notifyPageSwitched() {
+ if (mPageSwitchListener != null) {
+ mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage);
+ }
+ }
+
+ protected void pageBeginMoving() {
+ if (!mIsPageMoving) {
+ mIsPageMoving = true;
+ onPageBeginMoving();
+ }
+ }
+
+ protected void pageEndMoving() {
+ if (mIsPageMoving) {
+ mIsPageMoving = false;
+ onPageEndMoving();
+ }
+ }
+
+ protected boolean isPageMoving() {
+ return mIsPageMoving;
+ }
+
+ // a method that subclasses can override to add behavior
+ protected void onPageBeginMoving() {
+ }
+
+ // a method that subclasses can override to add behavior
+ protected void onPageEndMoving() {
+ }
+
+ /**
+ * Registers the specified listener on each page contained in this workspace.
+ *
+ * @param l The listener used to respond to long clicks.
+ */
+ @Override
+ public void setOnLongClickListener(OnLongClickListener l) {
+ mLongClickListener = l;
+ final int count = getPageCount();
+ for (int i = 0; i < count; i++) {
+ getPageAt(i).setOnLongClickListener(l);
+ }
+ }
+
+ @Override
+ public void scrollBy(int x, int y) {
+ scrollTo(mUnboundedScrollX + x, getScrollY() + y);
+ }
+
+ @Override
+ public void scrollTo(int x, int y) {
+ mUnboundedScrollX = x;
+
+ if (x < 0) {
+ super.scrollTo(0, y);
+ if (mAllowOverScroll) {
+ overScroll(x);
+ }
+ } else if (x > mMaxScrollX) {
+ super.scrollTo(mMaxScrollX, y);
+ if (mAllowOverScroll) {
+ overScroll(x - mMaxScrollX);
+ }
+ } else {
+ mOverScrollX = x;
+ super.scrollTo(x, y);
+ }
+
+ mTouchX = x;
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+
+ // Update the last motion events when scrolling
+ if (isReordering(true)) {
+ float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
+ mLastMotionX = p[0];
+ mLastMotionY = p[1];
+ updateDragViewTranslationDuringDrag();
+ }
+ }
+
+ // we moved this functionality to a helper function so SmoothPagedView can reuse it
+ protected boolean computeScrollHelper() {
+ if (mScroller.computeScrollOffset()) {
+ // Don't bother scrolling if the page does not need to be moved
+ if (getScrollX() != mScroller.getCurrX()
+ || getScrollY() != mScroller.getCurrY()
+ || mOverScrollX != mScroller.getCurrX()) {
+ scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
+ }
+ invalidate();
+ return true;
+ } else if (mNextPage != INVALID_PAGE) {
+ mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
+ mNextPage = INVALID_PAGE;
+ notifyPageSwitched();
+
+ // We don't want to trigger a page end moving unless the page has settled
+ // and the user has stopped scrolling
+ if (mTouchState == TOUCH_STATE_REST) {
+ pageEndMoving();
+ }
+
+ onPostReorderingAnimationCompleted();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void computeScroll() {
+ computeScrollHelper();
+ }
+
+ protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
+ return mTopAlignPageWhenShrinkingForBouncer;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (!mIsDataReady || getChildCount() == 0) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+
+ // We measure the dimensions of the PagedView to be larger than the pages so that when we
+ // zoom out (and scale down), the view is still contained in the parent
+ View parent = (View) getParent();
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the
+ // viewport, we can be at most one and a half screens offset once we scale down
+ DisplayMetrics dm = getResources().getDisplayMetrics();
+ int maxSize = Math.max(dm.widthPixels, dm.heightPixels);
+ int parentWidthSize = (int) (1.5f * maxSize);
+ int parentHeightSize = maxSize;
+ int scaledWidthSize = (int) (parentWidthSize / mMinScale);
+ int scaledHeightSize = (int) (parentHeightSize / mMinScale);
+ mViewport.set(0, 0, widthSize, heightSize);
+
+ if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+
+ // Return early if we aren't given a proper dimension
+ if (widthSize <= 0 || heightSize <= 0) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+
+ /* Allow the height to be set as WRAP_CONTENT. This allows the particular case
+ * of the All apps view on XLarge displays to not take up more space then it needs. Width
+ * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect
+ * each page to have the same width.
+ */
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
+ final int horizontalPadding = getPaddingLeft() + getPaddingRight();
+
+ // The children are given the same width and height as the workspace
+ // unless they were set to WRAP_CONTENT
+ if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
+ if (DEBUG) Log.d(TAG, "PagedView.scaledSize: " + scaledWidthSize + ", " + scaledHeightSize);
+ if (DEBUG) Log.d(TAG, "PagedView.parentSize: " + parentWidthSize + ", " + parentHeightSize);
+ if (DEBUG) Log.d(TAG, "PagedView.horizontalPadding: " + horizontalPadding);
+ if (DEBUG) Log.d(TAG, "PagedView.verticalPadding: " + verticalPadding);
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ // disallowing padding in paged view (just pass 0)
+ final View child = getPageAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ int childWidthMode;
+ if (lp.width == LayoutParams.WRAP_CONTENT) {
+ childWidthMode = MeasureSpec.AT_MOST;
+ } else {
+ childWidthMode = MeasureSpec.EXACTLY;
+ }
+
+ int childHeightMode;
+ if (lp.height == LayoutParams.WRAP_CONTENT) {
+ childHeightMode = MeasureSpec.AT_MOST;
+ } else {
+ childHeightMode = MeasureSpec.EXACTLY;
+ }
+
+ final int childWidthMeasureSpec =
+ MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode);
+ final int childHeightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+ setMeasuredDimension(scaledWidthSize, scaledHeightSize);
+
+ // We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions.
+ // We also wait until we set the measured dimensions before flushing the cache as well, to
+ // ensure that the cache is filled with good values.
+ invalidateCachedOffsets();
+
+ if (mChildCountOnLastMeasure != getChildCount() && !mDeferringForDelete) {
+ setCurrentPage(mCurrentPage);
+ }
+ mChildCountOnLastMeasure = getChildCount();
+
+ if (childCount > 0) {
+ if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getViewportWidth() + ", "
+ + getChildWidth(0));
+
+ // Calculate the variable page spacing if necessary
+ if (mPageSpacing == AUTOMATIC_PAGE_SPACING) {
+ // The gap between pages in the PagedView should be equal to the gap from the page
+ // to the edge of the screen (so it is not visible in the current screen). To
+ // account for unequal padding on each side of the paged view, we take the maximum
+ // of the left/right gap and use that as the gap between each page.
+ int offset = getRelativeChildOffset(0);
+ int spacing = Math.max(offset, widthSize - offset -
+ getChildAt(0).getMeasuredWidth());
+ setPageSpacing(spacing);
+ }
+ }
+
+ updateScrollingIndicatorPosition();
+
+ if (childCount > 0) {
+ mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
+ } else {
+ mMaxScrollX = 0;
+ }
+ }
+
+ public void setPageSpacing(int pageSpacing) {
+ mPageSpacing = pageSpacing;
+ invalidateCachedOffsets();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mIsDataReady || getChildCount() == 0) {
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
+ final int childCount = getChildCount();
+
+ int offsetX = getViewportOffsetX();
+ int offsetY = getViewportOffsetY();
+
+ // Update the viewport offsets
+ mViewport.offset(offsetX, offsetY);
+
+ int childLeft = offsetX + getRelativeChildOffset(0);
+ for (int i = 0; i < childCount; i++) {
+ final View child = getPageAt(i);
+ int childTop = offsetY + getPaddingTop();
+ if (child.getVisibility() != View.GONE) {
+ final int childWidth = getScaledMeasuredWidth(child);
+ final int childHeight = child.getMeasuredHeight();
+
+ if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
+ child.layout(childLeft, childTop,
+ childLeft + child.getMeasuredWidth(), childTop + childHeight);
+ childLeft += childWidth + mPageSpacing;
+ }
+ }
+
+ if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
+ setHorizontalScrollBarEnabled(false);
+ updateCurrentPageScroll();
+ setHorizontalScrollBarEnabled(true);
+ mFirstLayout = false;
+ }
+ }
+
+ protected void screenScrolled(int screenCenter) {
+ }
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ // This ensures that when children are added, they get the correct transforms / alphas
+ // in accordance with any scroll effects.
+ mForceScreenScrolled = true;
+ invalidate();
+ invalidateCachedOffsets();
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ mForceScreenScrolled = true;
+ invalidate();
+ invalidateCachedOffsets();
+ }
+
+ protected void invalidateCachedOffsets() {
+ int count = getChildCount();
+ if (count == 0) {
+ mChildOffsets = null;
+ mChildRelativeOffsets = null;
+ mChildOffsetsWithLayoutScale = null;
+ return;
+ }
+
+ mChildOffsets = new int[count];
+ mChildRelativeOffsets = new int[count];
+ mChildOffsetsWithLayoutScale = new int[count];
+ for (int i = 0; i < count; i++) {
+ mChildOffsets[i] = -1;
+ mChildRelativeOffsets[i] = -1;
+ mChildOffsetsWithLayoutScale[i] = -1;
+ }
+ }
+
+ protected int getChildOffset(int index) {
+ if (index < 0 || index > getChildCount() - 1) return 0;
+
+ int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ?
+ mChildOffsets : mChildOffsetsWithLayoutScale;
+
+ if (childOffsets != null && childOffsets[index] != -1) {
+ return childOffsets[index];
+ } else {
+ if (getChildCount() == 0)
+ return 0;
+
+ int offset = getRelativeChildOffset(0);
+ for (int i = 0; i < index; ++i) {
+ offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing;
+ }
+ if (childOffsets != null) {
+ childOffsets[index] = offset;
+ }
+ return offset;
+ }
+ }
+
+ protected int getRelativeChildOffset(int index) {
+ if (index < 0 || index > getChildCount() - 1) return 0;
+
+ if (mChildRelativeOffsets != null && mChildRelativeOffsets[index] != -1) {
+ return mChildRelativeOffsets[index];
+ } else {
+ final int padding = getPaddingLeft() + getPaddingRight();
+ final int offset = getPaddingLeft() +
+ (getViewportWidth() - padding - getChildWidth(index)) / 2;
+ if (mChildRelativeOffsets != null) {
+ mChildRelativeOffsets[index] = offset;
+ }
+ return offset;
+ }
+ }
+
+ protected int getScaledMeasuredWidth(View child) {
+ // This functions are called enough times that it actually makes a difference in the
+ // profiler -- so just inline the max() here
+ final int measuredWidth = child.getMeasuredWidth();
+ final int minWidth = mMinimumWidth;
+ final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth;
+ return (int) (maxWidth * mLayoutScale + 0.5f);
+ }
+
+ void boundByReorderablePages(boolean isReordering, int[] range) {
+ // Do nothing
+ }
+
+ // TODO: Fix this
+ protected void getVisiblePages(int[] range) {
+ range[0] = 0;
+ range[1] = getPageCount() - 1;
+
+ /*
+ final int pageCount = getChildCount();
+
+ if (pageCount > 0) {
+ final int screenWidth = getViewportWidth();
+ int leftScreen = 0;
+ int rightScreen = 0;
+ int offsetX = getViewportOffsetX() + getScrollX();
+ View currPage = getPageAt(leftScreen);
+ while (leftScreen < pageCount - 1 &&
+ currPage.getX() + currPage.getWidth() -
+ currPage.getPaddingRight() < offsetX) {
+ leftScreen++;
+ currPage = getPageAt(leftScreen);
+ }
+ rightScreen = leftScreen;
+ currPage = getPageAt(rightScreen + 1);
+ while (rightScreen < pageCount - 1 &&
+ currPage.getX() - currPage.getPaddingLeft() < offsetX + screenWidth) {
+ rightScreen++;
+ currPage = getPageAt(rightScreen + 1);
+ }
+
+ // TEMP: this is a hacky way to ensure that animations to new pages are not clipped
+ // because we don't draw them while scrolling?
+ range[0] = Math.max(0, leftScreen - 1);
+ range[1] = Math.min(rightScreen + 1, getChildCount() - 1);
+ } else {
+ range[0] = -1;
+ range[1] = -1;
+ }
+ */
+ }
+
+ protected boolean shouldDrawChild(View child) {
+ return child.getAlpha() > 0;
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ int halfScreenSize = getViewportWidth() / 2;
+ // mOverScrollX is equal to getScrollX() when we're within the normal scroll range.
+ // Otherwise it is equal to the scaled overscroll position.
+ int screenCenter = mOverScrollX + halfScreenSize;
+
+ if (screenCenter != mLastScreenCenter || mForceScreenScrolled) {
+ // set mForceScreenScrolled before calling screenScrolled so that screenScrolled can
+ // set it for the next frame
+ mForceScreenScrolled = false;
+ screenScrolled(screenCenter);
+ mLastScreenCenter = screenCenter;
+ }
+
+ // Find out which screens are visible; as an optimization we only call draw on them
+ final int pageCount = getChildCount();
+ if (pageCount > 0) {
+ getVisiblePages(mTempVisiblePagesRange);
+ final int leftScreen = mTempVisiblePagesRange[0];
+ final int rightScreen = mTempVisiblePagesRange[1];
+ if (leftScreen != -1 && rightScreen != -1) {
+ final long drawingTime = getDrawingTime();
+ // Clip to the bounds
+ canvas.save();
+ canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(),
+ getScrollY() + getBottom() - getTop());
+
+ // Draw all the children, leaving the drag view for last
+ for (int i = pageCount - 1; i >= 0; i--) {
+ final View v = getPageAt(i);
+ if (v == mDragView) continue;
+ if (mForceDrawAllChildrenNextFrame ||
+ (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) {
+ drawChild(canvas, v, drawingTime);
+ }
+ }
+ // Draw the drag view on top (if there is one)
+ if (mDragView != null) {
+ drawChild(canvas, mDragView, drawingTime);
+ }
+
+ mForceDrawAllChildrenNextFrame = false;
+ canvas.restore();
+ }
+ }
+ }
+
+ @Override
+ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+ int page = indexToPage(indexOfChild(child));
+ if (page != mCurrentPage || !mScroller.isFinished()) {
+ snapToPage(page);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ int focusablePage;
+ if (mNextPage != INVALID_PAGE) {
+ focusablePage = mNextPage;
+ } else {
+ focusablePage = mCurrentPage;
+ }
+ View v = getPageAt(focusablePage);
+ if (v != null) {
+ return v.requestFocus(direction, previouslyFocusedRect);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ if (direction == View.FOCUS_LEFT) {
+ if (getCurrentPage() > 0) {
+ snapToPage(getCurrentPage() - 1);
+ return true;
+ }
+ } else if (direction == View.FOCUS_RIGHT) {
+ if (getCurrentPage() < getPageCount() - 1) {
+ snapToPage(getCurrentPage() + 1);
+ return true;
+ }
+ }
+ return super.dispatchUnhandledMove(focused, direction);
+ }
+
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
+ getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
+ }
+ if (direction == View.FOCUS_LEFT) {
+ if (mCurrentPage > 0) {
+ getPageAt(mCurrentPage - 1).addFocusables(views, direction, focusableMode);
+ }
+ } else if (direction == View.FOCUS_RIGHT){
+ if (mCurrentPage < getPageCount() - 1) {
+ getPageAt(mCurrentPage + 1).addFocusables(views, direction, focusableMode);
+ }
+ }
+ }
+
+ /**
+ * If one of our descendant views decides that it could be focused now, only
+ * pass that along if it's on the current page.
+ *
+ * This happens when live folders requery, and if they're off page, they
+ * end up calling requestFocus, which pulls it on page.
+ */
+ @Override
+ public void focusableViewAvailable(View focused) {
+ View current = getPageAt(mCurrentPage);
+ View v = focused;
+ while (true) {
+ if (v == current) {
+ super.focusableViewAvailable(focused);
+ return;
+ }
+ if (v == this) {
+ return;
+ }
+ ViewParent parent = v.getParent();
+ if (parent instanceof View) {
+ v = (View)v.getParent();
+ } else {
+ return;
+ }
+ }
+ }
+
+ /**
+ * Return true if a tap at (x, y) should trigger a flip to the previous page.
+ */
+ protected boolean hitsPreviousPage(float x, float y) {
+ return (x < getViewportOffsetX() + getRelativeChildOffset(mCurrentPage) - mPageSpacing);
+ }
+
+ /**
+ * Return true if a tap at (x, y) should trigger a flip to the next page.
+ */
+ protected boolean hitsNextPage(float x, float y) {
+ return (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+ }
+
+ /** Returns whether x and y originated within the buffered viewport */
+ private boolean isTouchPointInViewportWithBuffer(int x, int y) {
+ mTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top,
+ mViewport.right + mViewport.width() / 2, mViewport.bottom);
+ return mTmpRect.contains(x, y);
+ }
+
+ /** Returns whether x and y originated within the current page view bounds */
+ private boolean isTouchPointInCurrentPage(int x, int y) {
+ View v = getPageAt(getCurrentPage());
+ if (v != null) {
+ mTmpRect.set((v.getLeft() - getScrollX()), 0, (v.getRight() - getScrollX()),
+ v.getBottom());
+ return mTmpRect.contains(x, y);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (DISABLE_TOUCH_INTERACTION) {
+ return false;
+ }
+
+ /*
+ * This method JUST determines whether we want to intercept the motion.
+ * If we return true, onTouchEvent will be called and we do the actual
+ * scrolling there.
+ */
+ acquireVelocityTrackerAndAddMovement(ev);
+
+ // Skip touch handling if there are no pages to swipe
+ if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
+
+ /*
+ * Shortcut the most recurring case: the user is in the dragging
+ * state and he is moving his finger. We want to intercept this
+ * motion.
+ */
+ final int action = ev.getAction();
+ if ((action == MotionEvent.ACTION_MOVE) &&
+ (mTouchState == TOUCH_STATE_SCROLLING)) {
+ return true;
+ }
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_MOVE: {
+ /*
+ * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
+ * whether the user has moved far enough from his original down touch.
+ */
+ if (mActivePointerId != INVALID_POINTER) {
+ determineScrollingStart(ev);
+ break;
+ }
+ // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
+ // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
+ // i.e. fall through to the next case (don't break)
+ // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
+ // while it's small- this was causing a crash before we checked for INVALID_POINTER)
+ }
+
+ case MotionEvent.ACTION_DOWN: {
+ final float x = ev.getX();
+ final float y = ev.getY();
+ // Remember location of down touch
+ mDownMotionX = x;
+ mDownMotionY = y;
+ mDownScrollX = getScrollX();
+ mLastMotionX = x;
+ mLastMotionY = y;
+ float[] p = mapPointFromViewToParent(this, x, y);
+ mParentDownMotionX = p[0];
+ mParentDownMotionY = p[1];
+ mLastMotionXRemainder = 0;
+ mTotalMotionX = 0;
+ mActivePointerId = ev.getPointerId(0);
+
+ // Determine if the down event is within the threshold to be an edge swipe
+ int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
+ int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
+ if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
+ mDownEventOnEdge = true;
+ }
+
+ /*
+ * If being flinged and user touches the screen, initiate drag;
+ * otherwise don't. mScroller.isFinished should be false when
+ * being flinged.
+ */
+ final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
+ final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
+ if (finishedScrolling) {
+ mTouchState = TOUCH_STATE_REST;
+ mScroller.abortAnimation();
+ } else {
+ if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) {
+ mTouchState = TOUCH_STATE_SCROLLING;
+ } else {
+ mTouchState = TOUCH_STATE_REST;
+ }
+ }
+
+ // check if this can be the beginning of a tap on the side of the pages
+ // to scroll the current page
+ if (!DISABLE_TOUCH_SIDE_PAGES) {
+ if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
+ if (getChildCount() > 0) {
+ if (hitsPreviousPage(x, y)) {
+ mTouchState = TOUCH_STATE_PREV_PAGE;
+ } else if (hitsNextPage(x, y)) {
+ mTouchState = TOUCH_STATE_NEXT_PAGE;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ resetTouchState();
+ // Just intercept the touch event on up if we tap outside the strict viewport
+ if (!isTouchPointInCurrentPage((int) mLastMotionX, (int) mLastMotionY)) {
+ return true;
+ }
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ releaseVelocityTracker();
+ break;
+ }
+
+ /*
+ * The only time we want to intercept motion events is if we are in the
+ * drag mode.
+ */
+ return mTouchState != TOUCH_STATE_REST;
+ }
+
+ protected void determineScrollingStart(MotionEvent ev) {
+ determineScrollingStart(ev, 1.0f);
+ }
+
+ /*
+ * Determines if we should change the touch state to start scrolling after the
+ * user moves their touch point too far.
+ */
+ protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
+ // Disallow scrolling if we don't have a valid pointer index
+ final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == -1) return;
+
+ // Disallow scrolling if we started the gesture from outside the viewport
+ final float x = ev.getX(pointerIndex);
+ final float y = ev.getY(pointerIndex);
+ if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return;
+
+ // If we're only allowing edge swipes, we break out early if the down event wasn't
+ // at the edge.
+ if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return;
+
+ final int xDiff = (int) Math.abs(x - mLastMotionX);
+ final int yDiff = (int) Math.abs(y - mLastMotionY);
+
+ final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
+ boolean xPaged = xDiff > mPagingTouchSlop;
+ boolean xMoved = xDiff > touchSlop;
+ boolean yMoved = yDiff > touchSlop;
+
+ if (xMoved || xPaged || yMoved) {
+ if (mUsePagingTouchSlop ? xPaged : xMoved) {
+ // Scroll if the user moved far enough along the X axis
+ mTouchState = TOUCH_STATE_SCROLLING;
+ mTotalMotionX += Math.abs(mLastMotionX - x);
+ mLastMotionX = x;
+ mLastMotionXRemainder = 0;
+ mTouchX = getViewportOffsetX() + getScrollX();
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+ pageBeginMoving();
+ }
+ }
+ }
+
+ protected float getMaxScrollProgress() {
+ return 1.0f;
+ }
+
+ protected float getBoundedScrollProgress(int screenCenter, View v, int page) {
+ final int halfScreenSize = getViewportWidth() / 2;
+
+ screenCenter = Math.min(mScrollX + halfScreenSize, screenCenter);
+ screenCenter = Math.max(halfScreenSize, screenCenter);
+
+ return getScrollProgress(screenCenter, v, page);
+ }
+
+ protected float getScrollProgress(int screenCenter, View v, int page) {
+ final int halfScreenSize = getViewportWidth() / 2;
+
+ int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing;
+ int delta = screenCenter - (getChildOffset(page) -
+ getRelativeChildOffset(page) + halfScreenSize);
+
+ float scrollProgress = delta / (totalDistance * 1.0f);
+ scrollProgress = Math.min(scrollProgress, getMaxScrollProgress());
+ scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress());
+ return scrollProgress;
+ }
+
+ // This curve determines how the effect of scrolling over the limits of the page dimishes
+ // as the user pulls further and further from the bounds
+ private float overScrollInfluenceCurve(float f) {
+ f -= 1.0f;
+ return f * f * f + 1.0f;
+ }
+
+ protected void acceleratedOverScroll(float amount) {
+ int screenSize = getViewportWidth();
+
+ // We want to reach the max over scroll effect when the user has
+ // over scrolled half the size of the screen
+ float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);
+
+ if (f == 0) return;
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ int overScrollAmount = (int) Math.round(f * screenSize);
+ if (amount < 0) {
+ mOverScrollX = overScrollAmount;
+ super.scrollTo(0, getScrollY());
+ } else {
+ mOverScrollX = mMaxScrollX + overScrollAmount;
+ super.scrollTo(mMaxScrollX, getScrollY());
+ }
+ invalidate();
+ }
+
+ protected void dampedOverScroll(float amount) {
+ int screenSize = getViewportWidth();
+
+ float f = (amount / screenSize);
+
+ if (f == 0) return;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
+ if (amount < 0) {
+ mOverScrollX = overScrollAmount;
+ super.scrollTo(0, getScrollY());
+ } else {
+ mOverScrollX = mMaxScrollX + overScrollAmount;
+ super.scrollTo(mMaxScrollX, getScrollY());
+ }
+ invalidate();
+ }
+
+ protected void overScroll(float amount) {
+ dampedOverScroll(amount);
+ }
+
+ protected float maxOverScroll() {
+ // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
+ // exceed). Used to find out how much extra wallpaper we need for the over scroll effect
+ float f = 1.0f;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+ return OVERSCROLL_DAMP_FACTOR * f;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (DISABLE_TOUCH_INTERACTION) {
+ return false;
+ }
+
+ // Skip touch handling if there are no pages to swipe
+ if (getChildCount() <= 0) return super.onTouchEvent(ev);
+
+ acquireVelocityTrackerAndAddMovement(ev);
+
+ final int action = ev.getAction();
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ /*
+ * If being flinged and user touches, stop the fling. isFinished
+ * will be false if being flinged.
+ */
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ }
+
+ // Remember where the motion event started
+ mDownMotionX = mLastMotionX = ev.getX();
+ mDownMotionY = mLastMotionY = ev.getY();
+ mDownScrollX = getScrollX();
+ float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
+ mParentDownMotionX = p[0];
+ mParentDownMotionY = p[1];
+ mLastMotionXRemainder = 0;
+ mTotalMotionX = 0;
+ mActivePointerId = ev.getPointerId(0);
+
+ // Determine if the down event is within the threshold to be an edge swipe
+ int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
+ int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
+ if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
+ mDownEventOnEdge = true;
+ }
+
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ pageBeginMoving();
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ // Scroll to follow the motion event
+ final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+
+ if (pointerIndex == -1) return true;
+
+ final float x = ev.getX(pointerIndex);
+ final float deltaX = mLastMotionX + mLastMotionXRemainder - x;
+
+ mTotalMotionX += Math.abs(deltaX);
+
+ // Only scroll and update mLastMotionX if we have moved some discrete amount. We
+ // keep the remainder because we are actually testing if we've moved from the last
+ // scrolled position (which is discrete).
+ if (Math.abs(deltaX) >= 1.0f) {
+ mTouchX += deltaX;
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+ if (!mDeferScrollUpdate) {
+ scrollBy((int) deltaX, 0);
+ if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX);
+ } else {
+ invalidate();
+ }
+ mLastMotionX = x;
+ mLastMotionXRemainder = deltaX - (int) deltaX;
+ } else {
+ awakenScrollBars();
+ }
+ } else if (mTouchState == TOUCH_STATE_REORDERING) {
+ // Update the last motion position
+ mLastMotionX = ev.getX();
+ mLastMotionY = ev.getY();
+
+ // Update the parent down so that our zoom animations take this new movement into
+ // account
+ float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
+ mParentDownMotionX = pt[0];
+ mParentDownMotionY = pt[1];
+ updateDragViewTranslationDuringDrag();
+
+ // Find the closest page to the touch point
+ final int dragViewIndex = indexOfChild(mDragView);
+ int bufferSize = (int) (REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE *
+ getViewportWidth());
+ int leftBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.left, 0)[0]
+ + bufferSize);
+ int rightBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.right, 0)[0]
+ - bufferSize);
+
+ // Change the drag view if we are hovering over the drop target
+ boolean isHoveringOverDelete = isHoveringOverDeleteDropTarget(
+ (int) mParentDownMotionX, (int) mParentDownMotionY);
+ setPageHoveringOverDeleteDropTarget(dragViewIndex, isHoveringOverDelete);
+
+ if (DEBUG) Log.d(TAG, "leftBufferEdge: " + leftBufferEdge);
+ if (DEBUG) Log.d(TAG, "rightBufferEdge: " + rightBufferEdge);
+ if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX);
+ if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY);
+ if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX);
+ if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
+
+ float parentX = mParentDownMotionX;
+ int pageIndexToSnapTo = -1;
+ if (parentX < leftBufferEdge && dragViewIndex > 0) {
+ pageIndexToSnapTo = dragViewIndex - 1;
+ } else if (parentX > rightBufferEdge && dragViewIndex < getChildCount() - 1) {
+ pageIndexToSnapTo = dragViewIndex + 1;
+ }
+
+ final int pageUnderPointIndex = pageIndexToSnapTo;
+ if (pageUnderPointIndex > -1 && !isHoveringOverDelete) {
+ mTempVisiblePagesRange[0] = 0;
+ mTempVisiblePagesRange[1] = getPageCount() - 1;
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
+ pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
+ pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
+ mSidePageHoverIndex = pageUnderPointIndex;
+ mSidePageHoverRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Update the down scroll position to account for the fact that the
+ // current page is moved
+ mDownScrollX = getChildOffset(pageUnderPointIndex)
+ - getRelativeChildOffset(pageUnderPointIndex);
+
+ // Setup the scroll to the correct page before we swap the views
+ snapToPage(pageUnderPointIndex);
+
+ // For each of the pages between the paged view and the drag view,
+ // animate them from the previous position to the new position in
+ // the layout (as a result of the drag view moving in the layout)
+ int shiftDelta = (dragViewIndex < pageUnderPointIndex) ? -1 : 1;
+ int lowerIndex = (dragViewIndex < pageUnderPointIndex) ?
+ dragViewIndex + 1 : pageUnderPointIndex;
+ int upperIndex = (dragViewIndex > pageUnderPointIndex) ?
+ dragViewIndex - 1 : pageUnderPointIndex;
+ for (int i = lowerIndex; i <= upperIndex; ++i) {
+ View v = getChildAt(i);
+ // dragViewIndex < pageUnderPointIndex, so after we remove the
+ // drag view all subsequent views to pageUnderPointIndex will
+ // shift down.
+ int oldX = getViewportOffsetX() + getChildOffset(i);
+ int newX = getViewportOffsetX() + getChildOffset(i + shiftDelta);
+
+ // Animate the view translation from its old position to its new
+ // position
+ AnimatorSet anim = (AnimatorSet) v.getTag();
+ if (anim != null) {
+ anim.cancel();
+ }
+
+ v.setTranslationX(oldX - newX);
+ anim = new AnimatorSet();
+ anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
+ anim.playTogether(
+ ObjectAnimator.ofFloat(v, "translationX", 0f));
+ anim.start();
+ v.setTag(anim);
+ }
+
+ removeView(mDragView);
+ onRemoveView(mDragView, false);
+ addView(mDragView, pageUnderPointIndex);
+ onAddView(mDragView, pageUnderPointIndex);
+ mSidePageHoverIndex = -1;
+ }
+ };
+ postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
+ }
+ } else {
+ removeCallbacks(mSidePageHoverRunnable);
+ mSidePageHoverIndex = -1;
+ }
+ } else {
+ determineScrollingStart(ev);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ final int activePointerId = mActivePointerId;
+ final int pointerIndex = ev.findPointerIndex(activePointerId);
+ final float x = ev.getX(pointerIndex);
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
+ final int deltaX = (int) (x - mDownMotionX);
+ final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage));
+ boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
+ SIGNIFICANT_MOVE_THRESHOLD;
+
+ mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
+
+ boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
+ Math.abs(velocityX) > mFlingThresholdVelocity;
+
+ // In the case that the page is moved far to one direction and then is flung
+ // in the opposite direction, we use a threshold to determine whether we should
+ // just return to the starting page, or if we should skip one further.
+ boolean returnToOriginalPage = false;
+ if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
+ Math.signum(velocityX) != Math.signum(deltaX) && isFling) {
+ returnToOriginalPage = true;
+ }
+
+ int finalPage;
+ // We give flings precedence over large moves, which is why we short-circuit our
+ // test for a large move if a fling has been registered. That is, a large
+ // move to the left and fling to the right will register as a fling to the right.
+ if (((isSignificantMove && deltaX > 0 && !isFling) ||
+ (isFling && velocityX > 0)) && mCurrentPage > 0) {
+ finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
+ snapToPageWithVelocity(finalPage, velocityX);
+ } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
+ (isFling && velocityX < 0)) &&
+ mCurrentPage < getChildCount() - 1) {
+ finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
+ snapToPageWithVelocity(finalPage, velocityX);
+ } else {
+ snapToDestination();
+ }
+ } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
+ // at this point we have not moved beyond the touch slop
+ // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
+ // we can just page
+ int nextPage = Math.max(0, mCurrentPage - 1);
+ if (nextPage != mCurrentPage) {
+ snapToPage(nextPage);
+ } else {
+ snapToDestination();
+ }
+ } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
+ // at this point we have not moved beyond the touch slop
+ // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
+ // we can just page
+ int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
+ if (nextPage != mCurrentPage) {
+ snapToPage(nextPage);
+ } else {
+ snapToDestination();
+ }
+ } else if (mTouchState == TOUCH_STATE_REORDERING) {
+ // Update the last motion position
+ mLastMotionX = ev.getX();
+ mLastMotionY = ev.getY();
+
+ // Update the parent down so that our zoom animations take this new movement into
+ // account
+ float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
+ mParentDownMotionX = pt[0];
+ mParentDownMotionY = pt[1];
+ updateDragViewTranslationDuringDrag();
+ boolean handledFling = false;
+ if (!DISABLE_FLING_TO_DELETE) {
+ // Check the velocity and see if we are flinging-to-delete
+ PointF flingToDeleteVector = isFlingingToDelete();
+ if (flingToDeleteVector != null) {
+ onFlingToDelete(flingToDeleteVector);
+ handledFling = true;
+ }
+ }
+ if (!handledFling && isHoveringOverDeleteDropTarget((int) mParentDownMotionX,
+ (int) mParentDownMotionY)) {
+ onDropToDelete();
+ }
+ } else {
+ onUnhandledTap(ev);
+ }
+
+ // Remove the callback to wait for the side page hover timeout
+ removeCallbacks(mSidePageHoverRunnable);
+ // End any intermediate reordering states
+ resetTouchState();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ snapToDestination();
+ }
+ resetTouchState();
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ break;
+ }
+
+ return true;
+ }
+
+ //public abstract void onFlingToDelete(View v);
+ public abstract void onRemoveView(View v, boolean deletePermanently);
+ public abstract void onRemoveViewAnimationCompleted();
+ public abstract void onAddView(View v, int index);
+
+ private void resetTouchState() {
+ releaseVelocityTracker();
+ endReordering();
+ mTouchState = TOUCH_STATE_REST;
+ mActivePointerId = INVALID_POINTER;
+ mDownEventOnEdge = false;
+ }
+
+ protected void onUnhandledTap(MotionEvent ev) {}
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_SCROLL: {
+ // Handle mouse (or ext. device) by shifting the page depending on the scroll
+ final float vscroll;
+ final float hscroll;
+ if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
+ vscroll = 0;
+ hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ } else {
+ vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ }
+ if (hscroll != 0 || vscroll != 0) {
+ if (hscroll > 0 || vscroll > 0) {
+ scrollRight();
+ } else {
+ scrollLeft();
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
+ private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+ }
+
+ private void releaseVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+ MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ // TODO: Make this decision more intelligent.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
+ mLastMotionY = ev.getY(newPointerIndex);
+ mLastMotionXRemainder = 0;
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ }
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ super.requestChildFocus(child, focused);
+ int page = indexToPage(indexOfChild(child));
+ if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
+ snapToPage(page);
+ }
+ }
+
+ protected int getChildIndexForRelativeOffset(int relativeOffset) {
+ final int childCount = getChildCount();
+ int left;
+ int right;
+ for (int i = 0; i < childCount; ++i) {
+ left = getRelativeChildOffset(i);
+ right = (left + getScaledMeasuredWidth(getPageAt(i)));
+ if (left <= relativeOffset && relativeOffset <= right) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ protected int getChildWidth(int index) {
+ // This functions are called enough times that it actually makes a difference in the
+ // profiler -- so just inline the max() here
+ final int measuredWidth = getPageAt(index).getMeasuredWidth();
+ final int minWidth = mMinimumWidth;
+ return (minWidth > measuredWidth) ? minWidth : measuredWidth;
+ }
+
+ int getPageNearestToPoint(float x) {
+ int index = 0;
+ for (int i = 0; i < getChildCount(); ++i) {
+ if (x < getChildAt(i).getRight() - getScrollX()) {
+ return index;
+ } else {
+ index++;
+ }
+ }
+ return Math.min(index, getChildCount() - 1);
+ }
+
+ int getPageNearestToCenterOfScreen() {
+ int minDistanceFromScreenCenter = Integer.MAX_VALUE;
+ int minDistanceFromScreenCenterIndex = -1;
+ int screenCenter = getViewportOffsetX() + getScrollX() + (getViewportWidth() / 2);
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ View layout = (View) getPageAt(i);
+ int childWidth = getScaledMeasuredWidth(layout);
+ int halfChildWidth = (childWidth / 2);
+ int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth;
+ int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
+ if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
+ minDistanceFromScreenCenter = distanceFromScreenCenter;
+ minDistanceFromScreenCenterIndex = i;
+ }
+ }
+ return minDistanceFromScreenCenterIndex;
+ }
+
+ protected void snapToDestination() {
+ snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
+ }
+
+ private static class ScrollInterpolator implements Interpolator {
+ public ScrollInterpolator() {
+ }
+
+ public float getInterpolation(float t) {
+ t -= 1.0f;
+ return t*t*t*t*t + 1;
+ }
+ }
+
+ // We want the duration of the page snap animation to be influenced by the distance that
+ // the screen has to travel, however, we don't want this duration to be effected in a
+ // purely linear fashion. Instead, we use this method to moderate the effect that the distance
+ // of travel has on the overall snap duration.
+ float distanceInfluenceForSnapDuration(float f) {
+ f -= 0.5f; // center the values about 0.
+ f *= 0.3f * Math.PI / 2.0f;
+ return (float) Math.sin(f);
+ }
+
+ protected void snapToPageWithVelocity(int whichPage, int velocity) {
+ whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
+ int halfScreenSize = getViewportWidth() / 2;
+
+ if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
+ if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
+ + getViewportWidth() + ", " + getChildWidth(whichPage));
+ final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
+ int delta = newX - mUnboundedScrollX;
+ int duration = 0;
+
+ if (Math.abs(velocity) < mMinFlingVelocity) {
+ // If the velocity is low enough, then treat this more as an automatic page advance
+ // as opposed to an apparent physical response to flinging
+ snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+ return;
+ }
+
+ // Here we compute a "distance" that will be used in the computation of the overall
+ // snap duration. This is a function of the actual distance that needs to be traveled;
+ // we keep this value close to half screen size in order to reduce the variance in snap
+ // duration as a function of the distance the page needs to travel.
+ float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize));
+ float distance = halfScreenSize + halfScreenSize *
+ distanceInfluenceForSnapDuration(distanceRatio);
+
+ velocity = Math.abs(velocity);
+ velocity = Math.max(mMinSnapVelocity, velocity);
+
+ // we want the page's snap velocity to approximately match the velocity at which the
+ // user flings, so we scale the duration by a value near to the derivative of the scroll
+ // interpolator at zero, ie. 5. We use 4 to make it a little slower.
+ duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
+
+ snapToPage(whichPage, delta, duration);
+ }
+
+ protected void snapToPage(int whichPage) {
+ snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+ }
+ protected void snapToPageImmediately(int whichPage) {
+ snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
+ }
+
+ protected void snapToPage(int whichPage, int duration) {
+ snapToPage(whichPage, duration, false);
+ }
+ protected void snapToPage(int whichPage, int duration, boolean immediate) {
+ whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
+
+ if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
+ if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getViewportWidth() + ", "
+ + getChildWidth(whichPage));
+ int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
+ final int sX = mUnboundedScrollX;
+ final int delta = newX - sX;
+ snapToPage(whichPage, delta, duration, immediate);
+ }
+
+ protected void snapToPage(int whichPage, int delta, int duration) {
+ snapToPage(whichPage, delta, duration, false);
+ }
+ protected void snapToPage(int whichPage, int delta, int duration, boolean immediate) {
+ mNextPage = whichPage;
+ notifyPageSwitching(whichPage);
+ View focusedChild = getFocusedChild();
+ if (focusedChild != null && whichPage != mCurrentPage &&
+ focusedChild == getPageAt(mCurrentPage)) {
+ focusedChild.clearFocus();
+ }
+
+ pageBeginMoving();
+ awakenScrollBars(duration);
+ if (immediate) {
+ duration = 0;
+ } else if (duration == 0) {
+ duration = Math.abs(delta);
+ }
+
+ if (!mScroller.isFinished()) mScroller.abortAnimation();
+ mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
+
+ notifyPageSwitched();
+
+ // Trigger a compute() to finish switching pages if necessary
+ if (immediate) {
+ computeScroll();
+ }
+
+ mForceScreenScrolled = true;
+ invalidate();
+ }
+
+ public void scrollLeft() {
+ if (mScroller.isFinished()) {
+ if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
+ } else {
+ if (mNextPage > 0) snapToPage(mNextPage - 1);
+ }
+ }
+
+ public void scrollRight() {
+ if (mScroller.isFinished()) {
+ if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
+ } else {
+ if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
+ }
+ }
+
+ public int getPageForView(View v) {
+ int result = -1;
+ if (v != null) {
+ ViewParent vp = v.getParent();
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ if (vp == getPageAt(i)) {
+ return i;
+ }
+ }
+ }
+ return result;
+ }
+
+ public static class SavedState extends BaseSavedState {
+ int currentPage = -1;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ currentPage = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(currentPage);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ protected View getScrollingIndicator() {
+ return null;
+ }
+
+ protected boolean isScrollingIndicatorEnabled() {
+ return false;
+ }
+
+ Runnable hideScrollingIndicatorRunnable = new Runnable() {
+ @Override
+ public void run() {
+ hideScrollingIndicator(false);
+ }
+ };
+
+ protected void flashScrollingIndicator(boolean animated) {
+ removeCallbacks(hideScrollingIndicatorRunnable);
+ showScrollingIndicator(!animated);
+ postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration);
+ }
+
+ protected void showScrollingIndicator(boolean immediately) {
+ mShouldShowScrollIndicator = true;
+ mShouldShowScrollIndicatorImmediately = true;
+ if (getChildCount() <= 1) return;
+ if (!isScrollingIndicatorEnabled()) return;
+
+ mShouldShowScrollIndicator = false;
+ getScrollingIndicator();
+ if (mScrollIndicator != null) {
+ // Fade the indicator in
+ updateScrollingIndicatorPosition();
+ mScrollIndicator.setVisibility(View.VISIBLE);
+ cancelScrollingIndicatorAnimations();
+ if (immediately) {
+ mScrollIndicator.setAlpha(1f);
+ } else {
+ mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f);
+ mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration);
+ mScrollIndicatorAnimator.start();
+ }
+ }
+ }
+
+ protected void cancelScrollingIndicatorAnimations() {
+ if (mScrollIndicatorAnimator != null) {
+ mScrollIndicatorAnimator.cancel();
+ }
+ }
+
+ protected void hideScrollingIndicator(boolean immediately) {
+ if (getChildCount() <= 1) return;
+ if (!isScrollingIndicatorEnabled()) return;
+
+ getScrollingIndicator();
+ if (mScrollIndicator != null) {
+ // Fade the indicator out
+ updateScrollingIndicatorPosition();
+ cancelScrollingIndicatorAnimations();
+ if (immediately) {
+ mScrollIndicator.setVisibility(View.INVISIBLE);
+ mScrollIndicator.setAlpha(0f);
+ } else {
+ mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f);
+ mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration);
+ mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() {
+ private boolean cancelled = false;
+ @Override
+ public void onAnimationCancel(android.animation.Animator animation) {
+ cancelled = true;
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!cancelled) {
+ mScrollIndicator.setVisibility(View.INVISIBLE);
+ }
+ }
+ });
+ mScrollIndicatorAnimator.start();
+ }
+ }
+ }
+
+ /**
+ * To be overridden by subclasses to determine whether the scroll indicator should stretch to
+ * fill its space on the track or not.
+ */
+ protected boolean hasElasticScrollIndicator() {
+ return true;
+ }
+
+ private void updateScrollingIndicator() {
+ if (getChildCount() <= 1) return;
+ if (!isScrollingIndicatorEnabled()) return;
+
+ getScrollingIndicator();
+ if (mScrollIndicator != null) {
+ updateScrollingIndicatorPosition();
+ }
+ if (mShouldShowScrollIndicator) {
+ showScrollingIndicator(mShouldShowScrollIndicatorImmediately);
+ }
+ }
+
+ private void updateScrollingIndicatorPosition() {
+ if (!isScrollingIndicatorEnabled()) return;
+ if (mScrollIndicator == null) return;
+ int numPages = getChildCount();
+ int pageWidth = getViewportWidth();
+ int lastChildIndex = Math.max(0, getChildCount() - 1);
+ int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
+ int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
+ int indicatorWidth = mScrollIndicator.getMeasuredWidth() -
+ mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight();
+
+ float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX));
+ int indicatorSpace = trackWidth / numPages;
+ int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft;
+ if (hasElasticScrollIndicator()) {
+ if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) {
+ mScrollIndicator.getLayoutParams().width = indicatorSpace;
+ mScrollIndicator.requestLayout();
+ }
+ } else {
+ int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2;
+ indicatorPos += indicatorCenterOffset;
+ }
+ mScrollIndicator.setTranslationX(indicatorPos);
+ }
+
+ // Animate the drag view back to the original position
+ void animateDragViewToOriginalPosition() {
+ if (mDragView != null) {
+ AnimatorSet anim = new AnimatorSet();
+ anim.setDuration(REORDERING_DROP_REPOSITION_DURATION);
+ anim.playTogether(
+ ObjectAnimator.ofFloat(mDragView, "translationX", 0f),
+ ObjectAnimator.ofFloat(mDragView, "translationY", 0f));
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onPostReorderingAnimationCompleted();
+ }
+ });
+ anim.start();
+ }
+ }
+
+ // "Zooms out" the PagedView to reveal more side pages
+ protected boolean zoomOut() {
+ if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+ mZoomInOutAnim.cancel();
+ }
+
+ if (!(getScaleX() < 1f || getScaleY() < 1f)) {
+ mZoomInOutAnim = new AnimatorSet();
+ mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
+ mZoomInOutAnim.playTogether(
+ ObjectAnimator.ofFloat(this, "scaleX", mMinScale),
+ ObjectAnimator.ofFloat(this, "scaleY", mMinScale));
+ mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Show the delete drop target
+ if (mDeleteDropTarget != null) {
+ mDeleteDropTarget.setVisibility(View.VISIBLE);
+ mDeleteDropTarget.animate().alpha(1f)
+ .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mDeleteDropTarget.setAlpha(0f);
+ }
+ });
+ }
+ }
+ });
+ mZoomInOutAnim.start();
+ return true;
+ }
+ return false;
+ }
+
+ protected void onStartReordering() {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ announceForAccessibility(mContext.getString(
+ R.string.keyguard_accessibility_widget_reorder_start));
+ }
+
+ // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
+ mTouchState = TOUCH_STATE_REORDERING;
+ mIsReordering = true;
+
+ // Mark all the non-widget pages as invisible
+ getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ for (int i = 0; i < getPageCount(); ++i) {
+ if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
+ getPageAt(i).setAlpha(0f);
+ }
+ }
+
+ // We must invalidate to trigger a redraw to update the layers such that the drag view
+ // is always drawn on top
+ invalidate();
+ }
+
+ private void onPostReorderingAnimationCompleted() {
+ // Trigger the callback when reordering has settled
+ --mPostReorderingPreZoomInRemainingAnimationCount;
+ if (mPostReorderingPreZoomInRunnable != null &&
+ mPostReorderingPreZoomInRemainingAnimationCount == 0) {
+ mPostReorderingPreZoomInRunnable.run();
+ mPostReorderingPreZoomInRunnable = null;
+ }
+ }
+
+ protected void onEndReordering() {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ announceForAccessibility(mContext.getString(
+ R.string.keyguard_accessibility_widget_reorder_end));
+ }
+ mIsReordering = false;
+
+ // Mark all the non-widget pages as visible again
+ getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ for (int i = 0; i < getPageCount(); ++i) {
+ if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
+ getPageAt(i).setAlpha(1f);
+ }
+ }
+ }
+
+ public boolean startReordering() {
+ int dragViewIndex = getPageNearestToCenterOfScreen();
+ mTempVisiblePagesRange[0] = 0;
+ mTempVisiblePagesRange[1] = getPageCount() - 1;
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ mReorderingStarted = true;
+
+ // Check if we are within the reordering range
+ if (mTempVisiblePagesRange[0] <= dragViewIndex &&
+ dragViewIndex <= mTempVisiblePagesRange[1]) {
+ if (zoomOut()) {
+ // Find the drag view under the pointer
+ mDragView = getChildAt(dragViewIndex);
+
+ onStartReordering();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ boolean isReordering(boolean testTouchState) {
+ boolean state = mIsReordering;
+ if (testTouchState) {
+ state &= (mTouchState == TOUCH_STATE_REORDERING);
+ }
+ return state;
+ }
+ void endReordering() {
+ // For simplicity, we call endReordering sometimes even if reordering was never started.
+ // In that case, we don't want to do anything.
+ if (!mReorderingStarted) return;
+ mReorderingStarted = false;
+
+ // If we haven't flung-to-delete the current child, then we just animate the drag view
+ // back into position
+ final Runnable onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ onEndReordering();
+ }
+ };
+ if (!mDeferringForDelete) {
+ mPostReorderingPreZoomInRunnable = new Runnable() {
+ public void run() {
+ zoomIn(onCompleteRunnable);
+ };
+ };
+
+ mPostReorderingPreZoomInRemainingAnimationCount =
+ NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT;
+ // Snap to the current page
+ snapToPage(indexOfChild(mDragView), 0);
+ // Animate the drag view back to the front position
+ animateDragViewToOriginalPosition();
+ } else {
+ // Handled in post-delete-animation-callbacks
+ }
+ }
+
+ // "Zooms in" the PagedView to highlight the current page
+ protected boolean zoomIn(final Runnable onCompleteRunnable) {
+ if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+ mZoomInOutAnim.cancel();
+ }
+ if (getScaleX() < 1f || getScaleY() < 1f) {
+ mZoomInOutAnim = new AnimatorSet();
+ mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
+ mZoomInOutAnim.playTogether(
+ ObjectAnimator.ofFloat(this, "scaleX", 1f),
+ ObjectAnimator.ofFloat(this, "scaleY", 1f));
+ mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Hide the delete drop target
+ if (mDeleteDropTarget != null) {
+ mDeleteDropTarget.animate().alpha(0f)
+ .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDeleteDropTarget.setVisibility(View.GONE);
+ }
+ });
+ }
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mDragView = null;
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDragView = null;
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ }
+ });
+ mZoomInOutAnim.start();
+ return true;
+ } else {
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Flinging to delete - IN PROGRESS
+ */
+ private PointF isFlingingToDelete() {
+ ViewConfiguration config = ViewConfiguration.get(getContext());
+ mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
+
+ if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
+ // Do a quick dot product test to ensure that we are flinging upwards
+ PointF vel = new PointF(mVelocityTracker.getXVelocity(),
+ mVelocityTracker.getYVelocity());
+ PointF upVec = new PointF(0f, -1f);
+ float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
+ (vel.length() * upVec.length()));
+ if (theta <= Math.toRadians(FLING_TO_DELETE_MAX_FLING_DEGREES)) {
+ return vel;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Creates an animation from the current drag view along its current velocity vector.
+ * For this animation, the alpha runs for a fixed duration and we update the position
+ * progressively.
+ */
+ private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener {
+ private View mDragView;
+ private PointF mVelocity;
+ private Rect mFrom;
+ private long mPrevTime;
+ private float mFriction;
+
+ private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
+
+ public FlingAlongVectorAnimatorUpdateListener(View dragView, PointF vel, Rect from,
+ long startTime, float friction) {
+ mDragView = dragView;
+ mVelocity = vel;
+ mFrom = from;
+ mPrevTime = startTime;
+ mFriction = 1f - (mDragView.getResources().getDisplayMetrics().density * friction);
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = ((Float) animation.getAnimatedValue()).floatValue();
+ long curTime = AnimationUtils.currentAnimationTimeMillis();
+
+ mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f);
+ mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f);
+
+ mDragView.setTranslationX(mFrom.left);
+ mDragView.setTranslationY(mFrom.top);
+ mDragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
+
+ mVelocity.x *= mFriction;
+ mVelocity.y *= mFriction;
+ mPrevTime = curTime;
+ }
+ };
+
+ private Runnable createPostDeleteAnimationRunnable(final View dragView) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ int dragViewIndex = indexOfChild(dragView);
+
+ // For each of the pages around the drag view, animate them from the previous
+ // position to the new position in the layout (as a result of the drag view moving
+ // in the layout)
+ // NOTE: We can make an assumption here because we have side-bound pages that we
+ // will always have pages to animate in from the left
+ getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]);
+ boolean slideFromLeft = (isLastWidgetPage ||
+ dragViewIndex > mTempVisiblePagesRange[0]);
+
+ // Setup the scroll to the correct page before we swap the views
+ if (slideFromLeft) {
+ snapToPageImmediately(dragViewIndex - 1);
+ }
+
+ int firstIndex = (isLastWidgetPage ? 0 : mTempVisiblePagesRange[0]);
+ int lastIndex = Math.min(mTempVisiblePagesRange[1], getPageCount() - 1);
+ int lowerIndex = (slideFromLeft ? firstIndex : dragViewIndex + 1 );
+ int upperIndex = (slideFromLeft ? dragViewIndex - 1 : lastIndex);
+ ArrayList<Animator> animations = new ArrayList<Animator>();
+ for (int i = lowerIndex; i <= upperIndex; ++i) {
+ View v = getChildAt(i);
+ // dragViewIndex < pageUnderPointIndex, so after we remove the
+ // drag view all subsequent views to pageUnderPointIndex will
+ // shift down.
+ int oldX = 0;
+ int newX = 0;
+ if (slideFromLeft) {
+ if (i == 0) {
+ // Simulate the page being offscreen with the page spacing
+ oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i)
+ - mPageSpacing;
+ } else {
+ oldX = getViewportOffsetX() + getChildOffset(i - 1);
+ }
+ newX = getViewportOffsetX() + getChildOffset(i);
+ } else {
+ oldX = getChildOffset(i) - getChildOffset(i - 1);
+ newX = 0;
+ }
+
+ // Animate the view translation from its old position to its new
+ // position
+ AnimatorSet anim = (AnimatorSet) v.getTag();
+ if (anim != null) {
+ anim.cancel();
+ }
+
+ // Note: Hacky, but we want to skip any optimizations to not draw completely
+ // hidden views
+ v.setAlpha(Math.max(v.getAlpha(), 0.01f));
+ v.setTranslationX(oldX - newX);
+ anim = new AnimatorSet();
+ anim.playTogether(
+ ObjectAnimator.ofFloat(v, "translationX", 0f),
+ ObjectAnimator.ofFloat(v, "alpha", 1f));
+ animations.add(anim);
+ v.setTag(anim);
+ }
+
+ AnimatorSet slideAnimations = new AnimatorSet();
+ slideAnimations.playTogether(animations);
+ slideAnimations.setDuration(DELETE_SLIDE_IN_SIDE_PAGE_DURATION);
+ slideAnimations.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final Runnable onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mDeferringForDelete = false;
+ onEndReordering();
+ onRemoveViewAnimationCompleted();
+ }
+ };
+ zoomIn(onCompleteRunnable);
+ }
+ });
+ slideAnimations.start();
+
+ removeView(dragView);
+ onRemoveView(dragView, true);
+ }
+ };
+ }
+
+ public void onFlingToDelete(PointF vel) {
+ final long startTime = AnimationUtils.currentAnimationTimeMillis();
+
+ // NOTE: Because it takes time for the first frame of animation to actually be
+ // called and we expect the animation to be a continuation of the fling, we have
+ // to account for the time that has elapsed since the fling finished. And since
+ // we don't have a startDelay, we will always get call to update when we call
+ // start() (which we want to ignore).
+ final TimeInterpolator tInterpolator = new TimeInterpolator() {
+ private int mCount = -1;
+ private long mStartTime;
+ private float mOffset;
+ /* Anonymous inner class ctor */ {
+ mStartTime = startTime;
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ if (mCount < 0) {
+ mCount++;
+ } else if (mCount == 0) {
+ mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
+ mStartTime) / FLING_TO_DELETE_FADE_OUT_DURATION);
+ mCount++;
+ }
+ return Math.min(1f, mOffset + t);
+ }
+ };
+
+ final Rect from = new Rect();
+ final View dragView = mDragView;
+ from.left = (int) dragView.getTranslationX();
+ from.top = (int) dragView.getTranslationY();
+ AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel,
+ from, startTime, FLING_TO_DELETE_FRICTION);
+
+ final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);
+
+ // Create and start the animation
+ ValueAnimator mDropAnim = new ValueAnimator();
+ mDropAnim.setInterpolator(tInterpolator);
+ mDropAnim.setDuration(FLING_TO_DELETE_FADE_OUT_DURATION);
+ mDropAnim.setFloatValues(0f, 1f);
+ mDropAnim.addUpdateListener(updateCb);
+ mDropAnim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ onAnimationEndRunnable.run();
+ }
+ });
+ mDropAnim.start();
+ mDeferringForDelete = true;
+ }
+
+ /* Drag to delete */
+ private boolean isHoveringOverDeleteDropTarget(int x, int y) {
+ if (mDeleteDropTarget != null) {
+ mAltTmpRect.set(0, 0, 0, 0);
+ View parent = (View) mDeleteDropTarget.getParent();
+ if (parent != null) {
+ parent.getGlobalVisibleRect(mAltTmpRect);
+ }
+ mDeleteDropTarget.getGlobalVisibleRect(mTmpRect);
+ mTmpRect.offset(-mAltTmpRect.left, -mAltTmpRect.top);
+ return mTmpRect.contains(x, y);
+ }
+ return false;
+ }
+
+ protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {}
+
+ private void onDropToDelete() {
+ final View dragView = mDragView;
+
+ final float toScale = 0f;
+ final float toAlpha = 0f;
+
+ // Create and start the complex animation
+ ArrayList<Animator> animations = new ArrayList<Animator>();
+ AnimatorSet motionAnim = new AnimatorSet();
+ motionAnim.setInterpolator(new DecelerateInterpolator(2));
+ motionAnim.playTogether(
+ ObjectAnimator.ofFloat(dragView, "scaleX", toScale),
+ ObjectAnimator.ofFloat(dragView, "scaleY", toScale));
+ animations.add(motionAnim);
+
+ AnimatorSet alphaAnim = new AnimatorSet();
+ alphaAnim.setInterpolator(new LinearInterpolator());
+ alphaAnim.playTogether(
+ ObjectAnimator.ofFloat(dragView, "alpha", toAlpha));
+ animations.add(alphaAnim);
+
+ final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);
+
+ AnimatorSet anim = new AnimatorSet();
+ anim.playTogether(animations);
+ anim.setDuration(DRAG_TO_DELETE_FADE_OUT_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ onAnimationEndRunnable.run();
+ }
+ });
+ anim.start();
+
+ mDeferringForDelete = true;
+ }
+
+ /* Accessibility */
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setScrollable(getPageCount() > 1);
+ if (getCurrentPage() < getPageCount() - 1) {
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ }
+ if (getCurrentPage() > 0) {
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setScrollable(true);
+ if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+ event.setFromIndex(mCurrentPage);
+ event.setToIndex(mCurrentPage);
+ event.setItemCount(getChildCount());
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (super.performAccessibilityAction(action, arguments)) {
+ return true;
+ }
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ if (getCurrentPage() < getPageCount() - 1) {
+ scrollRight();
+ return true;
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ if (getCurrentPage() > 0) {
+ scrollLeft();
+ return true;
+ }
+ } break;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onHoverEvent(android.view.MotionEvent event) {
+ return true;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
new file mode 100644
index 0000000..e2f91e3
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+public interface SecurityMessageDisplay {
+ public void setMessage(CharSequence msg, boolean important);
+
+ public void setMessage(int resId, boolean important);
+
+ public void setMessage(int resId, boolean important, Object... formatArgs);
+
+ public void setTimeout(int timeout_ms);
+
+ public void showBouncer(int animationDuration);
+
+ public void hideBouncer(int animationDuration);
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
new file mode 100644
index 0000000..05b35a1
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.util.Property;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+/**
+ * This layout handles interaction with the sliding security challenge views
+ * that overlay/resize other keyguard contents.
+ */
+public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
+ private static final String TAG = "SlidingChallengeLayout";
+ private static final boolean DEBUG = false;
+
+ // The drag handle is measured in dp above & below the top edge of the
+ // challenge view; these parameters change based on whether the challenge
+ // is open or closed.
+ private static final int DRAG_HANDLE_CLOSED_ABOVE = 8; // dp
+ private static final int DRAG_HANDLE_CLOSED_BELOW = 0; // dp
+ private static final int DRAG_HANDLE_OPEN_ABOVE = 8; // dp
+ private static final int DRAG_HANDLE_OPEN_BELOW = 0; // dp
+
+ private static final int HANDLE_ANIMATE_DURATION = 250; // ms
+
+ // Drawn to show the drag handle in closed state; crossfades to the challenge view
+ // when challenge is fully visible
+ private boolean mEdgeCaptured;
+
+ private DisplayMetrics mDisplayMetrics;
+
+ // Initialized during measurement from child layoutparams
+ private View mExpandChallengeView;
+ private KeyguardSecurityContainer mChallengeView;
+ private View mScrimView;
+ private View mWidgetsView;
+
+ // Range: 0 (fully hidden) to 1 (fully visible)
+ private float mChallengeOffset = 1.f;
+ private boolean mChallengeShowing = true;
+ private boolean mChallengeShowingTargetState = true;
+ private boolean mWasChallengeShowing = true;
+ private boolean mIsBouncing = false;
+
+ private final Scroller mScroller;
+ private ObjectAnimator mFader;
+ private int mScrollState;
+ private OnChallengeScrolledListener mScrollListener;
+ private OnBouncerStateChangedListener mBouncerListener;
+
+ public static final int SCROLL_STATE_IDLE = 0;
+ public static final int SCROLL_STATE_DRAGGING = 1;
+ public static final int SCROLL_STATE_SETTLING = 2;
+ public static final int SCROLL_STATE_FADING = 3;
+
+ private static final int CHALLENGE_FADE_OUT_DURATION = 100;
+ private static final int CHALLENGE_FADE_IN_DURATION = 160;
+
+ private static final int MAX_SETTLE_DURATION = 600; // ms
+
+ // ID of the pointer in charge of a current drag
+ private int mActivePointerId = INVALID_POINTER;
+ private static final int INVALID_POINTER = -1;
+
+ // True if the user is currently dragging the slider
+ private boolean mDragging;
+ // True if the user may not drag until a new gesture begins
+ private boolean mBlockDrag;
+
+ private VelocityTracker mVelocityTracker;
+ private int mMinVelocity;
+ private int mMaxVelocity;
+ private float mGestureStartX, mGestureStartY; // where did you first touch the screen?
+ private int mGestureStartChallengeBottom; // where was the challenge at that time?
+
+ private int mDragHandleClosedBelow; // handle hitrect extension into the challenge view
+ private int mDragHandleClosedAbove; // extend the handle's hitrect this far above the line
+ private int mDragHandleOpenBelow; // handle hitrect extension into the challenge view
+ private int mDragHandleOpenAbove; // extend the handle's hitrect this far above the line
+
+ private int mDragHandleEdgeSlop;
+ private int mChallengeBottomBound; // Number of pixels from the top of the challenge view
+ // that should remain on-screen
+
+ private int mTouchSlop;
+ private int mTouchSlopSquare;
+
+ float mHandleAlpha;
+ float mFrameAlpha;
+ float mFrameAnimationTarget = Float.MIN_VALUE;
+ private ObjectAnimator mHandleAnimation;
+ private ObjectAnimator mFrameAnimation;
+
+ private boolean mHasGlowpad;
+
+ // We have an internal and external version, and we and them together.
+ private boolean mChallengeInteractiveExternal = true;
+ private boolean mChallengeInteractiveInternal = true;
+
+ static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA =
+ new FloatProperty<SlidingChallengeLayout>("handleAlpha") {
+ @Override
+ public void setValue(SlidingChallengeLayout view, float value) {
+ view.mHandleAlpha = value;
+ view.invalidate();
+ }
+
+ @Override
+ public Float get(SlidingChallengeLayout view) {
+ return view.mHandleAlpha;
+ }
+ };
+
+ // True if at least one layout pass has happened since the view was attached.
+ private boolean mHasLayout;
+
+ private static final Interpolator sMotionInterpolator = new Interpolator() {
+ public float getInterpolation(float t) {
+ t -= 1.0f;
+ return t * t * t * t * t + 1.0f;
+ }
+ };
+
+ private static final Interpolator sHandleFadeInterpolator = new Interpolator() {
+ public float getInterpolation(float t) {
+ return t * t;
+ }
+ };
+
+ private final Runnable mEndScrollRunnable = new Runnable () {
+ public void run() {
+ completeChallengeScroll();
+ }
+ };
+
+ private final OnClickListener mScrimClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ hideBouncer();
+ }
+ };
+
+ private final OnClickListener mExpandChallengeClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!isChallengeShowing()) {
+ showChallenge(true);
+ }
+ }
+ };
+
+ /**
+ * Listener interface that reports changes in scroll state of the challenge area.
+ */
+ public interface OnChallengeScrolledListener {
+ /**
+ * The scroll state itself changed.
+ *
+ * <p>scrollState will be one of the following:</p>
+ *
+ * <ul>
+ * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li>
+ * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging
+ * the challenge area.</li>
+ * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating
+ * into place.</li>
+ * </ul>
+ *
+ * <p>Do not perform expensive operations (e.g. layout)
+ * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p>
+ *
+ * @param scrollState The new scroll state of the challenge area.
+ */
+ public void onScrollStateChanged(int scrollState);
+
+ /**
+ * The precise position of the challenge area has changed.
+ *
+ * <p>NOTE: It is NOT safe to modify layout or call any View methods that may
+ * result in a requestLayout anywhere in your view hierarchy as a result of this call.
+ * It may be called during drawing.</p>
+ *
+ * @param scrollPosition New relative position of the challenge area.
+ * 1.f = fully visible/ready to be interacted with.
+ * 0.f = fully invisible/inaccessible to the user.
+ * @param challengeTop Position of the top edge of the challenge view in px in the
+ * SlidingChallengeLayout's coordinate system.
+ */
+ public void onScrollPositionChanged(float scrollPosition, int challengeTop);
+ }
+
+ public SlidingChallengeLayout(Context context) {
+ this(context, null);
+ }
+
+ public SlidingChallengeLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ mScroller = new Scroller(context, sMotionInterpolator);
+
+ final ViewConfiguration vc = ViewConfiguration.get(context);
+ mMinVelocity = vc.getScaledMinimumFlingVelocity();
+ mMaxVelocity = vc.getScaledMaximumFlingVelocity();
+
+ final Resources res = getResources();
+ mDragHandleEdgeSlop = res.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
+
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mTouchSlopSquare = mTouchSlop * mTouchSlop;
+
+ mDisplayMetrics = res.getDisplayMetrics();
+ final float density = mDisplayMetrics.density;
+
+ // top half of the lock icon, plus another 25% to be sure
+ mDragHandleClosedAbove = (int) (DRAG_HANDLE_CLOSED_ABOVE * density + 0.5f);
+ mDragHandleClosedBelow = (int) (DRAG_HANDLE_CLOSED_BELOW * density + 0.5f);
+ mDragHandleOpenAbove = (int) (DRAG_HANDLE_OPEN_ABOVE * density + 0.5f);
+ mDragHandleOpenBelow = (int) (DRAG_HANDLE_OPEN_BELOW * density + 0.5f);
+
+ // how much space to account for in the handle when closed
+ mChallengeBottomBound = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding);
+
+ setWillNotDraw(false);
+ setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ }
+
+ public void setHandleAlpha(float alpha) {
+ if (mExpandChallengeView != null) {
+ mExpandChallengeView.setAlpha(alpha);
+ }
+ }
+
+ public void setChallengeInteractive(boolean interactive) {
+ mChallengeInteractiveExternal = interactive;
+ if (mExpandChallengeView != null) {
+ mExpandChallengeView.setEnabled(interactive);
+ }
+ }
+
+ void animateHandle(boolean visible) {
+ if (mHandleAnimation != null) {
+ mHandleAnimation.cancel();
+ mHandleAnimation = null;
+ }
+ final float targetAlpha = visible ? 1.f : 0.f;
+ if (targetAlpha == mHandleAlpha) {
+ return;
+ }
+ mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, targetAlpha);
+ mHandleAnimation.setInterpolator(sHandleFadeInterpolator);
+ mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION);
+ mHandleAnimation.start();
+ }
+
+ private void sendInitialListenerUpdates() {
+ if (mScrollListener != null) {
+ int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
+ mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop);
+ mScrollListener.onScrollStateChanged(mScrollState);
+ }
+ }
+
+ public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) {
+ mScrollListener = listener;
+ if (mHasLayout) {
+ sendInitialListenerUpdates();
+ }
+ }
+
+ public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
+ mBouncerListener = listener;
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mHasLayout = false;
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ removeCallbacks(mEndScrollRunnable);
+ mHasLayout = false;
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ if (mIsBouncing && child != mChallengeView) {
+ // Clear out of the bouncer if the user tries to move focus outside of
+ // the security challenge view.
+ hideBouncer();
+ }
+ super.requestChildFocus(child, focused);
+ }
+
+ // We want the duration of the page snap animation to be influenced by the distance that
+ // the screen has to travel, however, we don't want this duration to be effected in a
+ // purely linear fashion. Instead, we use this method to moderate the effect that the distance
+ // of travel has on the overall snap duration.
+ float distanceInfluenceForSnapDuration(float f) {
+ f -= 0.5f; // center the values about 0.
+ f *= 0.3f * Math.PI / 2.0f;
+ return (float) Math.sin(f);
+ }
+
+ void setScrollState(int state) {
+ if (mScrollState != state) {
+ mScrollState = state;
+
+ animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
+ if (mScrollListener != null) {
+ mScrollListener.onScrollStateChanged(state);
+ }
+ }
+ }
+
+ void completeChallengeScroll() {
+ setChallengeShowing(mChallengeShowingTargetState);
+ mChallengeOffset = mChallengeShowing ? 1.f : 0.f;
+ setScrollState(SCROLL_STATE_IDLE);
+ mChallengeInteractiveInternal = true;
+ mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
+ }
+
+ void setScrimView(View scrim) {
+ if (mScrimView != null) {
+ mScrimView.setOnClickListener(null);
+ }
+ mScrimView = scrim;
+ mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
+ mScrimView.setFocusable(true);
+ mScrimView.setOnClickListener(mScrimClickListener);
+ }
+
+ /**
+ * Animate the bottom edge of the challenge view to the given position.
+ *
+ * @param y desired final position for the bottom edge of the challenge view in px
+ * @param velocity velocity in
+ */
+ void animateChallengeTo(int y, int velocity) {
+ if (mChallengeView == null) {
+ // Nothing to do.
+ return;
+ }
+
+ cancelTransitionsInProgress();
+
+ mChallengeInteractiveInternal = false;
+ mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
+ final int sy = mChallengeView.getBottom();
+ final int dy = y - sy;
+ if (dy == 0) {
+ completeChallengeScroll();
+ return;
+ }
+
+ setScrollState(SCROLL_STATE_SETTLING);
+
+ final int childHeight = mChallengeView.getHeight();
+ final int halfHeight = childHeight / 2;
+ final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight);
+ final float distance = halfHeight + halfHeight *
+ distanceInfluenceForSnapDuration(distanceRatio);
+
+ int duration = 0;
+ velocity = Math.abs(velocity);
+ if (velocity > 0) {
+ duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
+ } else {
+ final float childDelta = (float) Math.abs(dy) / childHeight;
+ duration = (int) ((childDelta + 1) * 100);
+ }
+ duration = Math.min(duration, MAX_SETTLE_DURATION);
+
+ mScroller.startScroll(0, sy, 0, dy, duration);
+ postInvalidateOnAnimation();
+ }
+
+ private void setChallengeShowing(boolean showChallenge) {
+ if (mChallengeShowing == showChallenge) {
+ return;
+ }
+ mChallengeShowing = showChallenge;
+
+ if (mExpandChallengeView == null || mChallengeView == null) {
+ // These might not be here yet if we haven't been through layout.
+ // If we haven't, the first layout pass will set everything up correctly
+ // based on mChallengeShowing as set above.
+ return;
+ }
+
+ if (mChallengeShowing) {
+ mExpandChallengeView.setVisibility(View.INVISIBLE);
+ mChallengeView.setVisibility(View.VISIBLE);
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ mChallengeView.requestAccessibilityFocus();
+ mChallengeView.announceForAccessibility(mContext.getString(
+ R.string.keyguard_accessibility_unlock_area_expanded));
+ }
+ } else {
+ mExpandChallengeView.setVisibility(View.VISIBLE);
+ mChallengeView.setVisibility(View.INVISIBLE);
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ mExpandChallengeView.requestAccessibilityFocus();
+ mChallengeView.announceForAccessibility(mContext.getString(
+ R.string.keyguard_accessibility_unlock_area_collapsed));
+ }
+ }
+ }
+
+ /**
+ * @return true if the challenge is at all visible.
+ */
+ public boolean isChallengeShowing() {
+ return mChallengeShowing;
+ }
+
+ @Override
+ public boolean isChallengeOverlapping() {
+ return mChallengeShowing;
+ }
+
+ @Override
+ public boolean isBouncing() {
+ return mIsBouncing;
+ }
+
+ @Override
+ public int getBouncerAnimationDuration() {
+ return HANDLE_ANIMATE_DURATION;
+ }
+
+ @Override
+ public void showBouncer() {
+ if (mIsBouncing) return;
+ mWasChallengeShowing = mChallengeShowing;
+ mIsBouncing = true;
+ showChallenge(true);
+ if (mScrimView != null) {
+ Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f);
+ anim.setDuration(HANDLE_ANIMATE_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mScrimView.setVisibility(VISIBLE);
+ }
+ });
+ anim.start();
+ }
+ if (mChallengeView != null) {
+ mChallengeView.showBouncer(HANDLE_ANIMATE_DURATION);
+ }
+
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(true);
+ }
+ }
+
+ @Override
+ public void hideBouncer() {
+ if (!mIsBouncing) return;
+ if (!mWasChallengeShowing) showChallenge(false);
+ mIsBouncing = false;
+
+ if (mScrimView != null) {
+ Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f);
+ anim.setDuration(HANDLE_ANIMATE_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrimView.setVisibility(GONE);
+ }
+ });
+ anim.start();
+ }
+ if (mChallengeView != null) {
+ mChallengeView.hideBouncer(HANDLE_ANIMATE_DURATION);
+ }
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(false);
+ }
+ }
+
+ private int getChallengeMargin(boolean expanded) {
+ return expanded && mHasGlowpad ? 0 : mDragHandleEdgeSlop;
+ }
+
+ private float getChallengeAlpha() {
+ float x = mChallengeOffset - 1;
+ return x * x * x + 1.f;
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
+ // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
+ // If there are one or more pointers in the challenge view before we take over
+ // touch events, onInterceptTouchEvent will set mBlockDrag.
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ final int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mGestureStartX = ev.getX();
+ mGestureStartY = ev.getY();
+ mBlockDrag = false;
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ resetTouch();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ final float x = ev.getX(i);
+ final float y = ev.getY(i);
+ if (!mIsBouncing && mActivePointerId == INVALID_POINTER
+ && (crossedDragHandle(x, y, mGestureStartY)
+ || (isInChallengeView(x, y) &&
+ mScrollState == SCROLL_STATE_SETTLING))) {
+ mActivePointerId = ev.getPointerId(i);
+ mGestureStartX = x;
+ mGestureStartY = y;
+ mGestureStartChallengeBottom = getChallengeBottom();
+ mDragging = true;
+ mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
+ } else if (mChallengeShowing && isInChallengeView(x, y)) {
+ mBlockDrag = true;
+ }
+ }
+ break;
+ }
+
+ if (mBlockDrag || isChallengeInteractionBlocked()) {
+ mActivePointerId = INVALID_POINTER;
+ mDragging = false;
+ }
+
+ return mDragging;
+ }
+
+ private boolean isChallengeInteractionBlocked() {
+ return !mChallengeInteractiveExternal || !mChallengeInteractiveInternal;
+ }
+
+ private void resetTouch() {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ mActivePointerId = INVALID_POINTER;
+ mDragging = mBlockDrag = false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ final int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mBlockDrag = false;
+ mGestureStartX = ev.getX();
+ mGestureStartY = ev.getY();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ if (mDragging && !isChallengeInteractionBlocked()) {
+ showChallenge(0);
+ }
+ resetTouch();
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ if (mDragging && !isChallengeInteractionBlocked()) {
+ mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
+ showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
+ }
+ resetTouch();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (!mDragging && !mBlockDrag && !mIsBouncing) {
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ final float x = ev.getX(i);
+ final float y = ev.getY(i);
+
+ if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
+ (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
+ && mActivePointerId == INVALID_POINTER
+ && !isChallengeInteractionBlocked()) {
+ mGestureStartX = x;
+ mGestureStartY = y;
+ mActivePointerId = ev.getPointerId(i);
+ mGestureStartChallengeBottom = getChallengeBottom();
+ mDragging = true;
+ mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
+ break;
+ }
+ }
+ }
+ // Not an else; this can be set above.
+ if (mDragging) {
+ // No-op if already in this state, but set it here in case we arrived
+ // at this point from either intercept or the above.
+ setScrollState(SCROLL_STATE_DRAGGING);
+
+ final int index = ev.findPointerIndex(mActivePointerId);
+ if (index < 0) {
+ // Oops, bogus state. We lost some touch events somewhere.
+ // Just drop it with no velocity and let things settle.
+ resetTouch();
+ showChallenge(0);
+ return true;
+ }
+ final float y = ev.getY(index);
+ final float pos = Math.min(y - mGestureStartY,
+ getLayoutBottom() - mChallengeBottomBound);
+
+ moveChallengeTo(mGestureStartChallengeBottom + (int) pos);
+ }
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * The lifecycle of touch events is subtle and it's very easy to do something
+ * that will cause bugs that will be nasty to track when overriding this method.
+ * Normally one should always override onInterceptTouchEvent instead.
+ *
+ * To put it another way, don't try this at home.
+ */
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ final int action = ev.getActionMasked();
+ boolean handled = false;
+ if (action == MotionEvent.ACTION_DOWN) {
+ // Defensive programming: if we didn't get the UP or CANCEL, reset anyway.
+ mEdgeCaptured = false;
+ }
+ if (mWidgetsView != null && !mIsBouncing && (mEdgeCaptured || isEdgeSwipeBeginEvent(ev))) {
+ // Normally we would need to do a lot of extra stuff here.
+ // We can only get away with this because we haven't padded in
+ // the widget pager or otherwise transformed it during layout.
+ // We also don't support things like splitting MotionEvents.
+
+ // We set handled to captured even if dispatch is returning false here so that
+ // we don't send a different view a busted or incomplete event stream.
+ handled = mEdgeCaptured |= mWidgetsView.dispatchTouchEvent(ev);
+ }
+
+ if (!handled && !mEdgeCaptured) {
+ handled = super.dispatchTouchEvent(ev);
+ }
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ mEdgeCaptured = false;
+ }
+
+ return handled;
+ }
+
+ private boolean isEdgeSwipeBeginEvent(MotionEvent ev) {
+ if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ return false;
+ }
+
+ final float x = ev.getX();
+ return x < mDragHandleEdgeSlop || x >= getWidth() - mDragHandleEdgeSlop;
+ }
+
+ /**
+ * We only want to add additional vertical space to the drag handle when the panel is fully
+ * closed.
+ */
+ private int getDragHandleSizeAbove() {
+ return isChallengeShowing() ? mDragHandleOpenAbove : mDragHandleClosedAbove;
+ }
+ private int getDragHandleSizeBelow() {
+ return isChallengeShowing() ? mDragHandleOpenBelow : mDragHandleClosedBelow;
+ }
+
+ private boolean isInChallengeView(float x, float y) {
+ return isPointInView(x, y, mChallengeView);
+ }
+
+ private boolean isInDragHandle(float x, float y) {
+ return isPointInView(x, y, mExpandChallengeView);
+ }
+
+ private boolean isPointInView(float x, float y, View view) {
+ if (view == null) {
+ return false;
+ }
+ return x >= view.getLeft() && y >= view.getTop()
+ && x < view.getRight() && y < view.getBottom();
+ }
+
+ private boolean crossedDragHandle(float x, float y, float initialY) {
+
+ final int challengeTop = mChallengeView.getTop();
+ final boolean horizOk = x >= 0 && x < getWidth();
+
+ final boolean vertOk;
+ if (mChallengeShowing) {
+ vertOk = initialY < (challengeTop - getDragHandleSizeAbove()) &&
+ y > challengeTop + getDragHandleSizeBelow();
+ } else {
+ vertOk = initialY > challengeTop + getDragHandleSizeBelow() &&
+ y < challengeTop - getDragHandleSizeAbove();
+ }
+ return horizOk && vertOk;
+ }
+
+ private int makeChildMeasureSpec(int maxSize, int childDimen) {
+ final int mode;
+ final int size;
+ switch (childDimen) {
+ case LayoutParams.WRAP_CONTENT:
+ mode = MeasureSpec.AT_MOST;
+ size = maxSize;
+ break;
+ case LayoutParams.MATCH_PARENT:
+ mode = MeasureSpec.EXACTLY;
+ size = maxSize;
+ break;
+ default:
+ mode = MeasureSpec.EXACTLY;
+ size = Math.min(maxSize, childDimen);
+ break;
+ }
+ return MeasureSpec.makeMeasureSpec(size, mode);
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
+ MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
+ throw new IllegalArgumentException(
+ "SlidingChallengeLayout must be measured with an exact size");
+ }
+
+ final int width = MeasureSpec.getSize(widthSpec);
+ final int height = MeasureSpec.getSize(heightSpec);
+ setMeasuredDimension(width, height);
+
+ // Find one and only one challenge view.
+ final View oldChallengeView = mChallengeView;
+ final View oldExpandChallengeView = mChallengeView;
+ mChallengeView = null;
+ mExpandChallengeView = null;
+ final int count = getChildCount();
+
+ // First iteration through the children finds special children and sets any associated
+ // state.
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
+ if (mChallengeView != null) {
+ throw new IllegalStateException(
+ "There may only be one child with layout_isChallenge=\"true\"");
+ }
+ if (!(child instanceof KeyguardSecurityContainer)) {
+ throw new IllegalArgumentException(
+ "Challenge must be a KeyguardSecurityContainer");
+ }
+ mChallengeView = (KeyguardSecurityContainer) child;
+ if (mChallengeView != oldChallengeView) {
+ mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
+ }
+ // We're going to play silly games with the frame's background drawable later.
+ if (!mHasLayout) {
+ // Set up the margin correctly based on our content for the first run.
+ mHasGlowpad = child.findViewById(R.id.keyguard_selector_view) != null;
+ lp.leftMargin = lp.rightMargin = getChallengeMargin(true);
+ }
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) {
+ if (mExpandChallengeView != null) {
+ throw new IllegalStateException(
+ "There may only be one child with layout_childType"
+ + "=\"expandChallengeHandle\"");
+ }
+ mExpandChallengeView = child;
+ if (mExpandChallengeView != oldExpandChallengeView) {
+ mExpandChallengeView.setVisibility(mChallengeShowing ? INVISIBLE : VISIBLE);
+ mExpandChallengeView.setOnClickListener(mExpandChallengeClickListener);
+ }
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
+ setScrimView(child);
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) {
+ mWidgetsView = child;
+ }
+ }
+
+ // We want to measure the challenge view first, since the KeyguardWidgetPager
+ // needs to do things its measure pass that are dependent on the challenge view
+ // having been measured.
+ if (mChallengeView != null && mChallengeView.getVisibility() != View.GONE) {
+ // This one's a little funny. If the IME is present - reported in the form
+ // of insets on the root view - we only give the challenge the space it would
+ // have had if the IME wasn't there in order to keep the rest of the layout stable.
+ // We base this on the layout_maxHeight on the challenge view. If it comes out
+ // negative or zero, either we didn't have a maxHeight or we're totally out of space,
+ // so give up and measure as if this rule weren't there.
+ int challengeHeightSpec = heightSpec;
+ final View root = getRootView();
+ if (root != null) {
+ final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
+ final int specSize = MeasureSpec.getSize(heightSpec);
+ final int windowHeight = mDisplayMetrics.heightPixels - root.getPaddingTop();
+ final int diff = windowHeight - specSize;
+ final int maxChallengeHeight = lp.maxHeight - diff;
+ if (maxChallengeHeight > 0) {
+ challengeHeightSpec = makeChildMeasureSpec(maxChallengeHeight, lp.height);
+ }
+ }
+ measureChildWithMargins(mChallengeView, widthSpec, 0, challengeHeightSpec, 0);
+ }
+
+ // Measure the rest of the children
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+ // Don't measure the challenge view twice!
+ if (child == mChallengeView) continue;
+
+ // Measure children. Widget frame measures special, so that we can ignore
+ // insets for the IME.
+ int parentWidthSpec = widthSpec, parentHeightSpec = heightSpec;
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) {
+ final View root = getRootView();
+ if (root != null) {
+ // This calculation is super dodgy and relies on several assumptions.
+ // Specifically that the root of the window will be padded in for insets
+ // and that the window is LAYOUT_IN_SCREEN.
+ final int windowWidth = mDisplayMetrics.widthPixels;
+ final int windowHeight = mDisplayMetrics.heightPixels - root.getPaddingTop();
+ parentWidthSpec = MeasureSpec.makeMeasureSpec(
+ windowWidth, MeasureSpec.EXACTLY);
+ parentHeightSpec = MeasureSpec.makeMeasureSpec(
+ windowHeight, MeasureSpec.EXACTLY);
+ }
+ }
+ measureChildWithMargins(child, parentWidthSpec, 0, parentHeightSpec, 0);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = getPaddingTop();
+ final int paddingRight = getPaddingRight();
+ final int paddingBottom = getPaddingBottom();
+ final int width = r - l;
+ final int height = b - t;
+
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+
+ if (child.getVisibility() == GONE) continue;
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
+ // Challenge views pin to the bottom, offset by a portion of their height,
+ // and center horizontally.
+ final int center = (paddingLeft + width - paddingRight) / 2;
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+ final int left = center - childWidth / 2;
+ final int layoutBottom = height - paddingBottom - lp.bottomMargin;
+ // We use the top of the challenge view to position the handle, so
+ // we never want less than the handle size showing at the bottom.
+ final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound)
+ * (1 - mChallengeOffset));
+ child.setAlpha(getChallengeAlpha());
+ child.layout(left, bottom - childHeight, left + childWidth, bottom);
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) {
+ final int center = (paddingLeft + width - paddingRight) / 2;
+ final int left = center - child.getMeasuredWidth() / 2;
+ final int right = left + child.getMeasuredWidth();
+ final int bottom = height - paddingBottom - lp.bottomMargin;
+ final int top = bottom - child.getMeasuredHeight();
+ child.layout(left, top, right, bottom);
+ } else {
+ // Non-challenge views lay out from the upper left, layered.
+ child.layout(paddingLeft + lp.leftMargin,
+ paddingTop + lp.topMargin,
+ paddingLeft + child.getMeasuredWidth(),
+ paddingTop + child.getMeasuredHeight());
+ }
+ }
+
+ if (!mHasLayout) {
+ mHasLayout = true;
+ }
+ }
+
+ @Override
+ public void draw(Canvas c) {
+ super.draw(c);
+ if (DEBUG) {
+ final Paint debugPaint = new Paint();
+ debugPaint.setColor(0x40FF00CC);
+ // show the isInDragHandle() rect
+ c.drawRect(mDragHandleEdgeSlop,
+ mChallengeView.getTop() - getDragHandleSizeAbove(),
+ getWidth() - mDragHandleEdgeSlop,
+ mChallengeView.getTop() + getDragHandleSizeBelow(),
+ debugPaint);
+ }
+ }
+
+ public void computeScroll() {
+ super.computeScroll();
+
+ if (!mScroller.isFinished()) {
+ if (mChallengeView == null) {
+ // Can't scroll if the view is missing.
+ Log.e(TAG, "Challenge view missing in computeScroll");
+ mScroller.abortAnimation();
+ return;
+ }
+
+ mScroller.computeScrollOffset();
+ moveChallengeTo(mScroller.getCurrY());
+
+ if (mScroller.isFinished()) {
+ post(mEndScrollRunnable);
+ }
+ }
+ }
+
+ private void cancelTransitionsInProgress() {
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ completeChallengeScroll();
+ }
+ if (mFader != null) {
+ mFader.cancel();
+ }
+ }
+
+ public void fadeInChallenge() {
+ fadeChallenge(true);
+ }
+
+ public void fadeOutChallenge() {
+ fadeChallenge(false);
+ }
+
+ public void fadeChallenge(final boolean show) {
+ if (mChallengeView != null) {
+
+ cancelTransitionsInProgress();
+ float alpha = show ? 1f : 0f;
+ int duration = show ? CHALLENGE_FADE_IN_DURATION : CHALLENGE_FADE_OUT_DURATION;
+ mFader = ObjectAnimator.ofFloat(mChallengeView, "alpha", alpha);
+ mFader.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ onFadeStart(show);
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onFadeEnd(show);
+ }
+ });
+ mFader.setDuration(duration);
+ mFader.start();
+ }
+ }
+
+ private int getMaxChallengeBottom() {
+ if (mChallengeView == null) return 0;
+ final int layoutBottom = getLayoutBottom();
+ final int challengeHeight = mChallengeView.getMeasuredHeight();
+
+ return (layoutBottom + challengeHeight - mChallengeBottomBound);
+ }
+
+ private int getMinChallengeBottom() {
+ return getLayoutBottom();
+ }
+
+
+ private void onFadeStart(boolean show) {
+ mChallengeInteractiveInternal = false;
+ mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
+
+ if (show) {
+ moveChallengeTo(getMinChallengeBottom());
+ }
+
+ setScrollState(SCROLL_STATE_FADING);
+ }
+
+ private void onFadeEnd(boolean show) {
+ mChallengeInteractiveInternal = true;
+ setChallengeShowing(show);
+
+ if (!show) {
+ moveChallengeTo(getMaxChallengeBottom());
+ }
+
+ mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
+ mFader = null;
+ setScrollState(SCROLL_STATE_IDLE);
+ }
+
+ public int getMaxChallengeTop() {
+ if (mChallengeView == null) return 0;
+
+ final int layoutBottom = getLayoutBottom();
+ final int challengeHeight = mChallengeView.getMeasuredHeight();
+ return layoutBottom - challengeHeight;
+ }
+
+ /**
+ * Move the bottom edge of mChallengeView to a new position and notify the listener
+ * if it represents a change in position. Changes made through this method will
+ * be stable across layout passes. If this method is called before first layout of
+ * this SlidingChallengeLayout it will have no effect.
+ *
+ * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system.
+ * @return true if the challenge view was moved
+ */
+ private boolean moveChallengeTo(int bottom) {
+ if (mChallengeView == null || !mHasLayout) {
+ return false;
+ }
+
+ final int layoutBottom = getLayoutBottom();
+ final int challengeHeight = mChallengeView.getHeight();
+
+ bottom = Math.max(getMinChallengeBottom(),
+ Math.min(bottom, getMaxChallengeBottom()));
+
+ float offset = 1.f - (float) (bottom - layoutBottom) /
+ (challengeHeight - mChallengeBottomBound);
+ mChallengeOffset = offset;
+ if (offset > 0 && !mChallengeShowing) {
+ setChallengeShowing(true);
+ }
+
+ mChallengeView.layout(mChallengeView.getLeft(),
+ bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
+
+ mChallengeView.setAlpha(getChallengeAlpha());
+ if (mScrollListener != null) {
+ mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop());
+ }
+ postInvalidateOnAnimation();
+ return true;
+ }
+
+ /**
+ * The bottom edge of this SlidingChallengeLayout's coordinate system; will coincide with
+ * the bottom edge of mChallengeView when the challenge is fully opened.
+ */
+ private int getLayoutBottom() {
+ final int bottomMargin = (mChallengeView == null)
+ ? 0
+ : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
+ final int layoutBottom = getMeasuredHeight() - getPaddingBottom() - bottomMargin;
+ return layoutBottom;
+ }
+
+ /**
+ * The bottom edge of mChallengeView; essentially, where the sliding challenge 'is'.
+ */
+ private int getChallengeBottom() {
+ if (mChallengeView == null) return 0;
+
+ return mChallengeView.getBottom();
+ }
+
+ /**
+ * Show or hide the challenge view, animating it if necessary.
+ * @param show true to show, false to hide
+ */
+ public void showChallenge(boolean show) {
+ showChallenge(show, 0);
+ if (!show) {
+ // Block any drags in progress so that callers can use this to disable dragging
+ // for other touch interactions.
+ mBlockDrag = true;
+ }
+ }
+
+ private void showChallenge(int velocity) {
+ boolean show = false;
+ if (Math.abs(velocity) > mMinVelocity) {
+ show = velocity < 0;
+ } else {
+ show = mChallengeOffset >= 0.5f;
+ }
+ showChallenge(show, velocity);
+ }
+
+ private void showChallenge(boolean show, int velocity) {
+ if (mChallengeView == null) {
+ setChallengeShowing(false);
+ return;
+ }
+
+ if (mHasLayout) {
+ mChallengeShowingTargetState = show;
+ final int layoutBottom = getLayoutBottom();
+ animateChallengeTo(show ? layoutBottom :
+ layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity);
+ }
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
+ p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
+ new LayoutParams(p);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams();
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ public static class LayoutParams extends MarginLayoutParams {
+ public int childType = CHILD_TYPE_NONE;
+ public static final int CHILD_TYPE_NONE = 0;
+ public static final int CHILD_TYPE_CHALLENGE = 2;
+ public static final int CHILD_TYPE_SCRIM = 4;
+ public static final int CHILD_TYPE_WIDGETS = 5;
+ public static final int CHILD_TYPE_EXPAND_CHALLENGE_HANDLE = 6;
+
+ public int maxHeight;
+
+ public LayoutParams() {
+ this(MATCH_PARENT, WRAP_CONTENT);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(android.view.ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(LayoutParams source) {
+ super(source);
+
+ childType = source.childType;
+ }
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ final TypedArray a = c.obtainStyledAttributes(attrs,
+ R.styleable.SlidingChallengeLayout_Layout);
+ childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType,
+ CHILD_TYPE_NONE);
+ maxHeight = a.getDimensionPixelSize(
+ R.styleable.SlidingChallengeLayout_Layout_layout_maxHeight, 0);
+ a.recycle();
+ }
+ }
+}
diff --git a/packages/Keyguard/test/Android.mk b/packages/Keyguard/test/Android.mk
new file mode 100644
index 0000000..15059c6
--- /dev/null
+++ b/packages/Keyguard/test/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := KeyguardTest
+
+# Remove these to verify permission checks are working correctly
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+# LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+include $(BUILD_PACKAGE)
diff --git a/packages/Keyguard/test/AndroidManifest.xml b/packages/Keyguard/test/AndroidManifest.xml
new file mode 100644
index 0000000..b801e4b
--- /dev/null
+++ b/packages/Keyguard/test/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.keyguard.test">
+ <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17"/>
+ <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+ <application android:label="@string/app_name" android:icon="@drawable/app_icon">
+ <activity android:name=".KeyguardTestActivity"
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.Holo">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_lock_normal.png b/packages/Keyguard/test/res/drawable-hdpi/app_icon.png
similarity index 100%
copy from core/res/res/drawable-hdpi/ic_lockscreen_lock_normal.png
copy to packages/Keyguard/test/res/drawable-hdpi/app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_lock_normal.png b/packages/Keyguard/test/res/drawable-mdpi/app_icon.png
similarity index 100%
copy from core/res/res/drawable-mdpi/ic_lockscreen_lock_normal.png
copy to packages/Keyguard/test/res/drawable-mdpi/app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_lock_normal.png b/packages/Keyguard/test/res/drawable-xhdpi/app_icon.png
similarity index 100%
copy from core/res/res/drawable-xhdpi/ic_lockscreen_lock_normal.png
copy to packages/Keyguard/test/res/drawable-xhdpi/app_icon.png
Binary files differ
diff --git a/packages/Keyguard/test/res/layout/keyguard_test_activity.xml b/packages/Keyguard/test/res/layout/keyguard_test_activity.xml
new file mode 100644
index 0000000..dab1088
--- /dev/null
+++ b/packages/Keyguard/test/res/layout/keyguard_test_activity.xml
@@ -0,0 +1,47 @@
+<?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.
+*/
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Button android:id="@+id/do_keyguard"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/do_keyguard" />
+
+ <Button android:id="@+id/on_screen_turned_off"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/on_screen_turned_off" />
+
+ <Button android:id="@+id/on_screen_turned_on"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/on_screen_turned_on" />
+
+ <Button android:id="@+id/verify_unlock"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/verify_unlock" />
+
+</LinearLayout>
diff --git a/packages/Keyguard/test/res/menu/optionmenu.xml b/packages/Keyguard/test/res/menu/optionmenu.xml
new file mode 100644
index 0000000..22f300d
--- /dev/null
+++ b/packages/Keyguard/test/res/menu/optionmenu.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/none_menu_item"
+ android:title="@string/none_menu_item" />
+ <item android:id="@+id/pin_menu_item"
+ android:title="@string/pin_menu_item" />
+ <item android:id="@+id/password_menu_item"
+ android:title="@string/password_menu_item" />
+ <item android:id="@+id/pattern_menu_item"
+ android:title="@string/pattern_menu_item" />
+ <item android:id="@+id/sim_pin_menu_item"
+ android:title="@string/sim_pin_menu_item" />
+ <item android:id="@+id/sim_puk_menu_item"
+ android:title="@string/sim_puk_menu_item" />
+ <item android:id="@+id/add_widget_item"
+ android:title="@string/add_widget_item" />
+</menu>
diff --git a/packages/Keyguard/test/res/values/strings.xml b/packages/Keyguard/test/res/values/strings.xml
new file mode 100644
index 0000000..129204b
--- /dev/null
+++ b/packages/Keyguard/test/res/values/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name">KeyguardTestActivity</string>
+ <string name="secure_app_name">UnifiedCamera</string>
+ <string name="none_menu_item">No security</string>
+ <string name="pin_menu_item">PIN</string>
+ <string name="password_menu_item">Password</string>
+ <string name="pattern_menu_item">Pattern</string>
+ <string name="sim_pin_menu_item">SIM PIN</string>
+ <string name="sim_puk_menu_item">SIM PUK</string>
+ <string name="add_widget_item">Choose widget...</string>
+ <string name="on_screen_turned_off">onScreenTurnedOff</string>
+ <string name="on_screen_turned_on">onScreenTurnedOn</string>
+ <string name="do_keyguard">doKeyguard</string>
+ <string name="verify_unlock">verifyUnlock</string>
+</resources>
diff --git a/packages/Keyguard/test/src/com/android/keyguard/test/KeyguardTestActivity.java b/packages/Keyguard/test/src/com/android/keyguard/test/KeyguardTestActivity.java
new file mode 100644
index 0000000..e89c10e
--- /dev/null
+++ b/packages/Keyguard/test/src/com/android/keyguard/test/KeyguardTestActivity.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard.test;
+
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.policy.IKeyguardExitCallback;
+import com.android.internal.policy.IKeyguardService;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.WindowManagerPolicy;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView.Cell;
+
+import java.util.List;
+
+public class KeyguardTestActivity extends Activity implements OnClickListener {
+ private static final String KEYGUARD_PACKAGE = "com.android.keyguard";
+ private static final String KEYGUARD_CLASS = "com.android.keyguard.KeyguardService";
+ private static final String TAG = "LockScreenTestActivity";
+ private static final int MODE_NONE = 0;
+ private static final int MODE_PIN = 1;
+ private static final int MODE_PASSWORD = 2;
+ private static final int MODE_PATTERN = 3;
+ private static final int MODE_SIM_PIN = 4;
+ private static final int MODE_SIM_PUK = 5;
+ private static final String SECURITY_MODE = "security_mode";
+ Handler mHandler = new Handler();
+
+ IKeyguardService mService = null;
+
+ KeyguardShowCallback mKeyguardShowCallback = new KeyguardShowCallback();
+ KeyguardExitCallback mKeyguardExitCallback = new KeyguardExitCallback();
+
+ RemoteServiceConnection mConnection;
+ private boolean mSentSystemReady;
+
+ class KeyguardShowCallback extends IKeyguardShowCallback.Stub {
+
+ @Override
+ public void onShown(IBinder windowToken) throws RemoteException {
+ Log.v(TAG, "Keyguard is shown, windowToken = " + windowToken);
+ }
+ }
+
+ class KeyguardExitCallback extends IKeyguardExitCallback.Stub {
+
+ @Override
+ public void onKeyguardExitResult(final boolean success) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ new AlertDialog.Builder(KeyguardTestActivity.this)
+ .setMessage("Result: " + success)
+ .setPositiveButton("OK", null)
+ .show();
+ }
+ });
+ }
+ };
+
+ private class RemoteServiceConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.v(TAG, "onServiceConnected()");
+ mService = IKeyguardService.Stub.asInterface(service);
+ try {
+ mService.asBinder().linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ new AlertDialog.Builder(KeyguardTestActivity.this)
+ .setMessage("Oops! Keygued died")
+ .setPositiveButton("OK", null)
+ .show();
+ }
+ }, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Couldn't linkToDeath");
+ e.printStackTrace();
+ }
+// try {
+// mService.onSystemReady();
+// } catch (RemoteException e) {
+// Log.v(TAG, "Remote service died trying to call onSystemReady");
+// e.printStackTrace();
+// }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.v(TAG, "onServiceDisconnected()");
+ mService = null;
+ }
+ };
+
+ private void bindService() {
+ if (mConnection == null) {
+ mConnection = new RemoteServiceConnection();
+ Intent intent = new Intent();
+ intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS);
+ Log.v(TAG, "BINDING SERVICE: " + KEYGUARD_CLASS);
+ if (!bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+ Log.v(TAG, "FAILED TO BIND TO KEYGUARD!");
+ }
+ } else {
+ Log.v(TAG, "Service already bound");
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.keyguard_test_activity);
+ final int[] buttons = {
+ R.id.on_screen_turned_off, R.id.on_screen_turned_on,
+ R.id.do_keyguard, R.id.verify_unlock
+ };
+ for (int i = 0; i < buttons.length; i++) {
+ findViewById(buttons[i]).setOnClickListener(this);
+ }
+ Log.v(TAG, "Binding service...");
+ bindService();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(SECURITY_MODE, mSecurityMode);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ setMode(savedInstanceState.getInt(SECURITY_MODE));
+ }
+
+// TODO: Find a secure way to inject mock into keyguard...
+// @Override
+// public boolean onCreateOptionsMenu(Menu menu) {
+// MenuInflater inflater = getMenuInflater();
+// inflater.inflate(R.menu.optionmenu, menu);
+// return true;
+// }
+
+ private void setMode(int mode) {
+ mTestSimPin = false;
+ mTestSimPuk = false;
+ mLockPasswordEnabled = false;
+ mLockPatternEnabled = false;
+ switch(mode) {
+ case MODE_NONE:
+ mSecurityModeMock = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ break;
+ case MODE_PIN:
+ mSecurityModeMock = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+ mLockPasswordEnabled = true;
+ break;
+ case MODE_PASSWORD:
+ mSecurityModeMock = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+ mLockPasswordEnabled = true;
+ break;
+ case MODE_PATTERN:
+ mSecurityModeMock = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+ mLockPatternEnabled = true;
+ break;
+ case MODE_SIM_PIN:
+ mTestSimPin = true;
+ break;
+ case MODE_SIM_PUK:
+ mTestSimPuk = true;
+ break;
+ }
+ mSecurityMode = mode;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle item selection
+ switch (item.getItemId()) {
+ case R.id.none_menu_item:
+ setMode(MODE_NONE);
+ break;
+ case R.id.pin_menu_item:
+ setMode(MODE_PIN);
+ break;
+ case R.id.password_menu_item:
+ setMode(MODE_PASSWORD);
+ break;
+ case R.id.pattern_menu_item:
+ setMode(MODE_PATTERN);
+ break;
+ case R.id.sim_pin_menu_item:
+ setMode(MODE_SIM_PIN);
+ break;
+ case R.id.sim_puk_menu_item:
+ setMode(MODE_SIM_PUK);
+ break;
+ case R.id.add_widget_item:
+ startWidgetPicker();
+ break;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ try {
+ mService.doKeyguardTimeout(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote service died");
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ private void startWidgetPicker() {
+ startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS));
+ }
+
+ @Override
+ public void onClick(View v) {
+ try {
+ switch (v.getId()) {
+ case R.id.on_screen_turned_on:
+ mService.onScreenTurnedOn(mKeyguardShowCallback);
+ break;
+ case R.id.on_screen_turned_off:
+ mService.onScreenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
+ break;
+ case R.id.do_keyguard:
+ if (!mSentSystemReady) {
+ mSentSystemReady = true;
+ mService.onSystemReady();
+ }
+ mService.doKeyguardTimeout(null);
+ break;
+ case R.id.verify_unlock:
+ mService.doKeyguardTimeout(null);
+ // Wait for keyguard to lock and then try this...
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mService.verifyUnlock(mKeyguardExitCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed verifyUnlock()", e);
+ }
+ }
+ }, 5000);
+ break;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "onClick(): Failed due to remote exeption", e);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ try {
+ if (mService != null) {
+ mService.setHidden(true);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote service died");
+ e.printStackTrace();
+ }
+ }
+
+ protected void onResume() {
+ super.onResume();
+ try {
+ if (mService != null) {
+ mService.setHidden(false);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote service died");
+ e.printStackTrace();
+ }
+ }
+
+ public int mSecurityModeMock;
+ private boolean mTestSimPin;
+ private boolean mTestSimPuk;
+ private boolean mLockPasswordEnabled;
+ public boolean mLockPatternEnabled;
+ private int mSecurityMode;
+
+ class LockPatternUtilsMock extends LockPatternUtils {
+ private long mDeadline;
+ public LockPatternUtilsMock(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean checkPattern(List<Cell> pattern) {
+ return pattern.size() > 4;
+ }
+
+ @Override
+ public boolean checkPassword(String password) {
+ return password.length() > 4;
+ }
+ @Override
+ public long setLockoutAttemptDeadline() {
+ final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
+ mDeadline = deadline;
+ return deadline;
+ }
+ @Override
+ public boolean isLockScreenDisabled() {
+ return false;
+ }
+ @Override
+ public long getLockoutAttemptDeadline() {
+ return mDeadline;
+ }
+ @Override
+ public void reportFailedPasswordAttempt() {
+ // Ignored
+ }
+ @Override
+ public void reportSuccessfulPasswordAttempt() {
+ // Ignored
+ }
+ @Override
+ public boolean isLockPatternEnabled() {
+ return mLockPatternEnabled;
+ }
+
+ @Override
+ public boolean isLockPasswordEnabled() {
+ return mLockPasswordEnabled;
+ }
+
+ @Override
+ public int getKeyguardStoredPasswordQuality() {
+ return mSecurityModeMock;
+ }
+
+ public boolean isSecure() {
+ return mLockPatternEnabled || mLockPasswordEnabled || mTestSimPin || mTestSimPuk;
+ }
+
+ }
+}
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
new file mode 100644
index 0000000..a68fcdf
--- /dev/null
+++ b/packages/PrintSpooler/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PrintSpooler
+
+LOCAL_JAVA_LIBRARIES := framework
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
new file mode 100644
index 0000000..fbb0060
--- /dev/null
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.printspooler"
+ android:sharedUserId="android.uid.printspooler"
+ android:versionName="1"
+ android:versionCode="1"
+ coreApp="true">
+
+ <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="17"/>
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+
+ <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
+ android:label="@string/permlab_bindPrintSpoolerService"
+ android:description="@string/permdesc_bindPrintSpoolerService"
+ android:protectionLevel="signature" />
+
+ <application
+ android:allowClearUserData="false"
+ android:label="@string/app_label"
+ android:allowBackup= "false">
+
+ <service
+ android:name=".PrintSpoolerService"
+ android:exported="true"
+ android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE">
+ </service>
+
+ <activity
+ android:name=".PrintJobConfigActivity"
+ android:exported="true">
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/media/libdrm/MODULE_LICENSE_APACHE2 b/packages/PrintSpooler/MODULE_LICENSE_APACHE2
similarity index 100%
rename from media/libdrm/MODULE_LICENSE_APACHE2
rename to packages/PrintSpooler/MODULE_LICENSE_APACHE2
diff --git a/media/libdrm/NOTICE b/packages/PrintSpooler/NOTICE
similarity index 100%
rename from media/libdrm/NOTICE
rename to packages/PrintSpooler/NOTICE
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity.xml b/packages/PrintSpooler/res/layout/print_job_config_activity.xml
new file mode 100644
index 0000000..51e425d
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity.xml
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <GridLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical"
+ android:columnCount="2">
+
+ <EditText
+ android:id="@+id/copies_edittext"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="0"
+ android:layout_column="1"
+ android:minWidth="150dip"
+ android:inputType="number"
+ android:selectAllOnFocus="true">
+ </EditText>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="0"
+ android:layout_column="0"
+ android:text="@string/label_copies"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:labelFor="@id/copies_edittext">
+ </TextView>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="1"
+ android:layout_column="0"
+ android:text="@string/label_destination"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/destination_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="1"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
+
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="2"
+ android:layout_column="0"
+ android:text="@string/label_media_size"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/media_size_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="2"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
+
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="3"
+ android:layout_column="0"
+ android:text="@string/label_resolution"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/resolution_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="3"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
+
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="4"
+ android:layout_column="0"
+ android:text="@string/label_input_tray"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/input_tray_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="4"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
+
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="5"
+ android:layout_column="0"
+ android:text="@string/label_output_tray"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/output_tray_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="5"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
+
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="6"
+ android:layout_column="0"
+ android:text="@string/label_duplex_mode"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/duplex_mode_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="6"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
+
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="7"
+ android:layout_column="0"
+ android:text="@string/label_color_mode"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/color_mode_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="7"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
+
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="8"
+ android:layout_column="0"
+ android:text="@string/label_fitting_mode"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/fitting_mode_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="8"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
+
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="9"
+ android:layout_column="0"
+ android:text="@string/label_orientation"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/orientation_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="9"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
+
+ </GridLayout>
+
+</ScrollView>
diff --git a/packages/PrintSpooler/res/menu/print_job_config_activity.xml b/packages/PrintSpooler/res/menu/print_job_config_activity.xml
new file mode 100644
index 0000000..149c274
--- /dev/null
+++ b/packages/PrintSpooler/res/menu/print_job_config_activity.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/print_button"
+ android:title="@string/print_button"
+ android:showAsAction="ifRoom">
+ </item>
+</menu>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
new file mode 100644
index 0000000..8b4b40a
--- /dev/null
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <!-- Title of the PrintSpooler application. [CHAR LIMIT=16] -->
+ <string name="app_label">Print Spooler</string>
+
+ <!-- Title of the print dialog. [CHAR LIMIT=10] -->
+ <string name="print_job_config_dialog_title">Print</string>
+
+ <!-- Label of the print dialog's print button. [CHAR LIMIT=16] -->
+ <string name="print_button">Print</string>
+
+ <!-- Label of the print dialog's cancel button. [CHAR LIMIT=16] -->
+ <string name="cancel_button">Cancel</string>
+
+ <!-- Label of the destination spinner. [CHAR LIMIT=16] -->
+ <string name="label_destination">Destination</string>
+
+ <!-- Label of the copies count edit text. [CHAR LIMIT=16] -->
+ <string name="label_copies">Copies</string>
+
+ <!-- Label of the media size spinner. [CHAR LIMIT=16] -->
+ <string name="label_media_size">Media size</string>
+
+ <!-- Label of the resolution spinner. [CHAR LIMIT=16] -->
+ <string name="label_resolution">Resolution</string>
+
+ <!-- Label of the input tray spinner. [CHAR LIMIT=16] -->
+ <string name="label_input_tray">Input tray</string>
+
+ <!-- Label of the output tray spinner. [CHAR LIMIT=16] -->
+ <string name="label_output_tray">Output tray</string>
+
+ <!-- Label of the duplex mode spinner. [CHAR LIMIT=16] -->
+ <string name="label_duplex_mode">Duplex mode</string>
+
+ <!-- Label of the color mode spinner. [CHAR LIMIT=16] -->
+ <string name="label_color_mode">Color mode</string>
+
+ <!-- Label of the fitting mode spinner. [CHAR LIMIT=16] -->
+ <string name="label_fitting_mode">Fitting mode</string>
+
+ <!-- Label of the orientation spinner. [CHAR LIMIT=16] -->
+ <string name="label_orientation">Orientation</string>
+
+ <!-- Duplex mode labels. -->
+ <string-array name="duplex_mode_labels">
+ <!-- Duplex mode label: No duplexing. [CHAR LIMIT=20] -->
+ <item>None</item>
+ <!-- Duplex mode label: Turn a page along its long edge, e.g. like a book. [CHAR LIMIT=20] -->
+ <item>Long edge</item>
+ <!-- Duplex mode label: Turn a page along its short edge, e.g. like a notepad. [CHAR LIMIT=20] -->
+ <item>Short edge</item>
+ </string-array>
+
+ <!-- Color mode labels. -->
+ <string-array name="color_mode_labels">
+ <!-- Color modelabel: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] -->
+ <item>Monochrome</item>
+ <!-- Color mode label: Color color scheme, e.g. many colors are used. [CHAR LIMIT=20] -->
+ <item>Color</item>
+ </string-array>
+
+ <!-- Fitting mode labels. -->
+ <string-array name="fitting_mode_labels">
+ <!-- Fitting mode label: No fitting. [CHAR LIMIT=30] -->
+ <item>None</item>
+ <!-- Fitting mode label: Fit the content to the page. [CHAR LIMIT=30] -->
+ <item>Fit to page</item>
+ </string-array>
+
+ <!-- Orientation labels. -->
+ <string-array name="orientation_labels">
+ <!-- Orientation label: Portrait page orientation. [CHAR LIMIT=30] -->
+ <item>Portrait</item>
+ <!-- Orientation label: Landscape page orientation [CHAR LIMIT=30] -->
+ <item>Landscape</item>
+ </string-array>
+
+ <!-- Title of an application permission, listed so the user can choose
+ whether they want to allow the application to do this. -->
+ <string name="permlab_bindPrintSpoolerService">bind to a print spooler service</string>
+ <!-- Description of an application permission, listed so the user can
+ choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bindPrintSpoolerService">Allows the holder to bind to the top-level
+ interface of a print spooler service. Should never be needed for normal apps.</string>
+
+</resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
new file mode 100644
index 0000000..ae2fe5c
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.IBinder.DeathRecipient;
+import android.print.IPrintAdapter;
+import android.print.IPrintManager;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintAttributes.Tray;
+import android.print.PrintJobInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.Spinner;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Activity for configuring a print job.
+ */
+public class PrintJobConfigActivity extends Activity {
+
+ private static final boolean DEBUG = false;
+
+ private static final String LOG_TAG = PrintJobConfigActivity.class.getSimpleName();
+
+ public static final String EXTRA_PRINTABLE = "printable";
+ public static final String EXTRA_APP_ID = "appId";
+ public static final String EXTRA_ATTRIBUTES = "attributes";
+ public static final String EXTRA_PRINT_JOB_ID = "printJobId";
+
+ private static final int MIN_COPIES = 1;
+
+ private final List<QueuedAsyncTask<?>> mTaskQueue = new ArrayList<QueuedAsyncTask<?>>();
+
+ private IPrintManager mPrintManager;
+
+ private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
+
+ private int mAppId;
+ private int mPrintJobId;
+
+ private PrintAttributes mPrintAttributes;
+
+ private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
+
+ private RemotePrintAdapter mRemotePrintAdapter;
+
+ // UI elements
+
+ private EditText mCopiesEditText;
+
+ private Spinner mDestinationSpinner;
+ public ArrayAdapter<SpinnerItem<PrinterInfo>> mDestinationSpinnerAdapter;
+
+ private Spinner mMediaSizeSpinner;
+ public ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
+
+ private Spinner mResolutionSpinner;
+ public ArrayAdapter<SpinnerItem<Resolution>> mResolutionSpinnerAdapter;
+
+ private Spinner mInputTraySpinner;
+ public ArrayAdapter<SpinnerItem<Tray>> mInputTraySpinnerAdapter;
+
+ private Spinner mOutputTraySpinner;
+ public ArrayAdapter<SpinnerItem<Tray>> mOutputTraySpinnerAdapter;
+
+ private Spinner mDuplexModeSpinner;
+ public ArrayAdapter<SpinnerItem<Integer>> mDuplexModeSpinnerAdapter;
+
+ private Spinner mColorModeSpinner;
+ public ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
+
+ private Spinner mFittingModeSpinner;
+ public ArrayAdapter<SpinnerItem<Integer>> mFittingModeSpinnerAdapter;
+
+ private Spinner mOrientationSpinner;
+ public ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
+
+ private boolean mPrintStarted;
+
+ private boolean mPrintConfirmed;
+
+ private IBinder mPrinable;
+
+ // TODO: Implement store/restore state.
+
+ private final OnItemSelectedListener mOnItemSelectedListener =
+ new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) {
+ if (spinner == mDestinationSpinner) {
+ updateUi();
+ notifyPrintableStartIfNeeded();
+ } else if (spinner == mMediaSizeSpinner) {
+ SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
+ mPrintAttributes.setMediaSize(mediaItem.value);
+ updatePrintableContentIfNeeded();
+ } else if (spinner == mResolutionSpinner) {
+ SpinnerItem<Resolution> resolutionItem =
+ mResolutionSpinnerAdapter.getItem(position);
+ mPrintAttributes.setResolution(resolutionItem.value);
+ updatePrintableContentIfNeeded();
+ } else if (spinner == mInputTraySpinner) {
+ SpinnerItem<Tray> inputTrayItem =
+ mInputTraySpinnerAdapter.getItem(position);
+ mPrintAttributes.setInputTray(inputTrayItem.value);
+ } else if (spinner == mOutputTraySpinner) {
+ SpinnerItem<Tray> outputTrayItem =
+ mOutputTraySpinnerAdapter.getItem(position);
+ mPrintAttributes.setOutputTray(outputTrayItem.value);
+ } else if (spinner == mDuplexModeSpinner) {
+ SpinnerItem<Integer> duplexModeItem =
+ mDuplexModeSpinnerAdapter.getItem(position);
+ mPrintAttributes.setDuplexMode(duplexModeItem.value);
+ } else if (spinner == mColorModeSpinner) {
+ SpinnerItem<Integer> colorModeItem =
+ mColorModeSpinnerAdapter.getItem(position);
+ mPrintAttributes.setColorMode(colorModeItem.value);
+ } else if (spinner == mFittingModeSpinner) {
+ SpinnerItem<Integer> fittingModeItem =
+ mFittingModeSpinnerAdapter.getItem(position);
+ mPrintAttributes.setFittingMode(fittingModeItem.value);
+ } else if (spinner == mOrientationSpinner) {
+ SpinnerItem<Integer> orientationItem =
+ mOrientationSpinnerAdapter.getItem(position);
+ mPrintAttributes.setOrientation(orientationItem.value);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ /* do nothing*/
+ }
+ };
+
+ private final TextWatcher mTextWatcher = new TextWatcher() {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ final int copies = Integer.parseInt(mCopiesEditText.getText().toString());
+ mPrintAttributes.setCopies(copies);
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ /* do nothing */
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ /* do nothing */
+ }
+ };
+
+ private final InputFilter mInputFilter = new InputFilter() {
+ @Override
+ public CharSequence filter(CharSequence source, int start, int end,
+ Spanned dest, int dstart, int dend) {
+ StringBuffer text = new StringBuffer(dest.toString());
+ text.replace(dstart, dend, source.subSequence(start, end).toString());
+ if (TextUtils.isEmpty(text)) {
+ return dest;
+ }
+ final int copies = Integer.parseInt(text.toString());
+ if (copies < MIN_COPIES) {
+ return dest;
+ }
+ return null;
+ }
+ };
+
+ private final DeathRecipient mDeathRecipient = new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ finish();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ setContentView(R.layout.print_job_config_activity);
+
+ getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
+ | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+
+ mPrintManager = (IPrintManager) IPrintManager.Stub.asInterface(
+ ServiceManager.getService(PRINT_SERVICE));
+
+ Bundle extras = getIntent().getExtras();
+
+ mPrintJobId = extras.getInt(EXTRA_PRINT_JOB_ID, -1);
+ if (mPrintJobId < 0) {
+ throw new IllegalArgumentException("Invalid print job id: " + mPrintJobId);
+ }
+
+ mAppId = extras.getInt(EXTRA_APP_ID, -1);
+ if (mAppId < 0) {
+ throw new IllegalArgumentException("Invalid app id: " + mAppId);
+ }
+
+ mPrintAttributes = getIntent().getParcelableExtra(EXTRA_ATTRIBUTES);
+ if (mPrintAttributes == null) {
+ mPrintAttributes = new PrintAttributes.Builder().create();
+ }
+
+ mPrinable = extras.getBinder(EXTRA_PRINTABLE);
+ if (mPrinable == null) {
+ throw new IllegalArgumentException("Printable cannot be null");
+ }
+ mRemotePrintAdapter = new RemotePrintAdapter(IPrintAdapter.Stub.asInterface(mPrinable),
+ mPrintSpooler.generateFileForPrintJob(mPrintJobId));
+
+ try {
+ mPrinable.linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException re) {
+ finish();
+ }
+
+ mPrinterDiscoveryObserver = new PrintDiscoveryObserver(getMainLooper());
+
+ bindUi();
+ }
+
+ @Override
+ protected void onDestroy() {
+ mPrinable.unlinkToDeath(mDeathRecipient, 0);
+ super.onDestroy();
+ }
+
+ private void bindUi() {
+ // Copies
+ mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
+ mCopiesEditText.setText(String.valueOf(MIN_COPIES));
+ mCopiesEditText.addTextChangedListener(mTextWatcher);
+ mCopiesEditText.setFilters(new InputFilter[] {mInputFilter});
+
+ // Destination.
+ mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
+ mDestinationSpinnerAdapter = new ArrayAdapter<SpinnerItem<PrinterInfo>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
+ mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Media size.
+ mMediaSizeSpinner = (Spinner) findViewById(R.id.media_size_spinner);
+ mMediaSizeSpinnerAdapter = new ArrayAdapter<SpinnerItem<MediaSize>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
+ mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Resolution.
+ mResolutionSpinner = (Spinner) findViewById(R.id.resolution_spinner);
+ mResolutionSpinnerAdapter = new ArrayAdapter<SpinnerItem<Resolution>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mResolutionSpinner.setAdapter(mResolutionSpinnerAdapter);
+ mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Input tray.
+ mInputTraySpinner = (Spinner) findViewById(R.id.input_tray_spinner);
+ mInputTraySpinnerAdapter = new ArrayAdapter<SpinnerItem<Tray>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mInputTraySpinner.setAdapter(mInputTraySpinnerAdapter);
+
+ // Output tray.
+ mOutputTraySpinner = (Spinner) findViewById(R.id.output_tray_spinner);
+ mOutputTraySpinnerAdapter = new ArrayAdapter<SpinnerItem<Tray>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mOutputTraySpinner.setAdapter(mOutputTraySpinnerAdapter);
+ mOutputTraySpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Duplex mode.
+ mDuplexModeSpinner = (Spinner) findViewById(R.id.duplex_mode_spinner);
+ mDuplexModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mDuplexModeSpinner.setAdapter(mDuplexModeSpinnerAdapter);
+ mDuplexModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Color mode.
+ mColorModeSpinner = (Spinner) findViewById(R.id.color_mode_spinner);
+ mColorModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
+ mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Color mode.
+ mFittingModeSpinner = (Spinner) findViewById(R.id.fitting_mode_spinner);
+ mFittingModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mFittingModeSpinner.setAdapter(mFittingModeSpinnerAdapter);
+ mFittingModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Orientation
+ mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner);
+ mOrientationSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter);
+ mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ }
+
+ private void updateUi() {
+ final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
+ PrinterInfo printer = mDestinationSpinnerAdapter.getItem(selectedIndex).value;
+ printer.getDefaults(mPrintAttributes);
+
+ // Copies.
+ mCopiesEditText.setText(String.valueOf(
+ Math.max(mPrintAttributes.getCopies(), MIN_COPIES)));
+
+ // Media size.
+ mMediaSizeSpinnerAdapter.clear();
+ List<MediaSize> mediaSizes = printer.getMediaSizes();
+ final int mediaSizeCount = mediaSizes.size();
+ for (int i = 0; i < mediaSizeCount; i++) {
+ MediaSize mediaSize = mediaSizes.get(i);
+ mMediaSizeSpinnerAdapter.add(new SpinnerItem<MediaSize>(
+ mediaSize, mediaSize.getLabel(getPackageManager())));
+ }
+ final int selectedMediaSizeIndex = mediaSizes.indexOf(
+ mPrintAttributes.getMediaSize());
+ mMediaSizeSpinner.setOnItemSelectedListener(null);
+ mMediaSizeSpinner.setSelection(selectedMediaSizeIndex);
+ mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Resolution.
+ mResolutionSpinnerAdapter.clear();
+ List<Resolution> resolutions = printer.getResolutions();
+ final int resolutionCount = resolutions.size();
+ for (int i = 0; i < resolutionCount; i++) {
+ Resolution resolution = resolutions.get(i);
+ mResolutionSpinnerAdapter.add(new SpinnerItem<Resolution>(
+ resolution, resolution.getLabel(getPackageManager())));
+ }
+ final int selectedResolutionIndex = resolutions.indexOf(
+ mPrintAttributes.getResolution());
+ mResolutionSpinner.setOnItemSelectedListener(null);
+ mResolutionSpinner.setSelection(selectedResolutionIndex);
+ mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Input tray.
+ mInputTraySpinnerAdapter.clear();
+ List<Tray> inputTrays = printer.getInputTrays();
+ final int inputTrayCount = inputTrays.size();
+ for (int i = 0; i < inputTrayCount; i++) {
+ Tray inputTray = inputTrays.get(i);
+ mInputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
+ inputTray, inputTray.getLabel(getPackageManager())));
+ }
+ final int selectedInputTrayIndex = inputTrays.indexOf(
+ mPrintAttributes.getInputTray());
+ mInputTraySpinner.setSelection(selectedInputTrayIndex);
+
+ // Output tray.
+ mOutputTraySpinnerAdapter.clear();
+ List<Tray> outputTrays = printer.getOutputTrays();
+ final int outputTrayCount = outputTrays.size();
+ for (int i = 0; i < outputTrayCount; i++) {
+ Tray outputTray = outputTrays.get(i);
+ mOutputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
+ outputTray, outputTray.getLabel(getPackageManager())));
+ }
+ final int selectedOutputTrayIndex = outputTrays.indexOf(
+ mPrintAttributes.getOutputTray());
+ mOutputTraySpinner.setSelection(selectedOutputTrayIndex);
+
+ // Duplex mode.
+ final int duplexModes = printer.getDuplexModes();
+ mDuplexModeSpinnerAdapter.clear();
+ String[] duplexModeLabels = getResources().getStringArray(
+ R.array.duplex_mode_labels);
+ int remainingDuplexModes = duplexModes;
+ while (remainingDuplexModes != 0) {
+ final int duplexBitOffset = Integer.numberOfTrailingZeros(remainingDuplexModes);
+ final int duplexMode = 1 << duplexBitOffset;
+ remainingDuplexModes &= ~duplexMode;
+ mDuplexModeSpinnerAdapter.add(new SpinnerItem<Integer>(duplexMode,
+ duplexModeLabels[duplexBitOffset]));
+ }
+ final int selectedDuplexModeIndex = Integer.numberOfTrailingZeros(
+ (duplexModes & mPrintAttributes.getDuplexMode()));
+ mDuplexModeSpinner.setSelection(selectedDuplexModeIndex);
+
+ // Color mode.
+ final int colorModes = printer.getColorModes();
+ mColorModeSpinnerAdapter.clear();
+ String[] colorModeLabels = getResources().getStringArray(
+ R.array.color_mode_labels);
+ int remainingColorModes = colorModes;
+ while (remainingColorModes != 0) {
+ final int colorBitOffset = Integer.numberOfTrailingZeros(remainingColorModes);
+ final int colorMode = 1 << colorBitOffset;
+ remainingColorModes &= ~colorMode;
+ mColorModeSpinnerAdapter.add(new SpinnerItem<Integer>(colorMode,
+ colorModeLabels[colorBitOffset]));
+ }
+ final int selectedColorModeIndex = Integer.numberOfTrailingZeros(
+ (colorModes & mPrintAttributes.getColorMode()));
+ mColorModeSpinner.setSelection(selectedColorModeIndex);
+
+ // Fitting mode.
+ final int fittingModes = printer.getFittingModes();
+ mFittingModeSpinnerAdapter.clear();
+ String[] fittingModeLabels = getResources().getStringArray(
+ R.array.fitting_mode_labels);
+ int remainingFittingModes = fittingModes;
+ while (remainingFittingModes != 0) {
+ final int fittingBitOffset = Integer.numberOfTrailingZeros(remainingFittingModes);
+ final int fittingMode = 1 << fittingBitOffset;
+ remainingFittingModes &= ~fittingMode;
+ mFittingModeSpinnerAdapter.add(new SpinnerItem<Integer>(fittingMode,
+ fittingModeLabels[fittingBitOffset]));
+ }
+ final int selectedFittingModeIndex = Integer.numberOfTrailingZeros(
+ (fittingModes & mPrintAttributes.getFittingMode()));
+ mFittingModeSpinner.setSelection(selectedFittingModeIndex);
+
+ // Orientation.
+ final int orientations = printer.getOrientations();
+ mOrientationSpinnerAdapter.clear();
+ String[] orientationLabels = getResources().getStringArray(
+ R.array.orientation_labels);
+ int remainingOrientations = orientations;
+ while (remainingOrientations != 0) {
+ final int orientationBitOffset = Integer.numberOfTrailingZeros(remainingOrientations);
+ final int orientation = 1 << orientationBitOffset;
+ remainingOrientations &= ~orientation;
+ mOrientationSpinnerAdapter.add(new SpinnerItem<Integer>(orientation,
+ orientationLabels[orientationBitOffset]));
+ }
+ final int selectedOrientationIndex = Integer.numberOfTrailingZeros(
+ (orientations & mPrintAttributes.getOrientation()));
+ mOrientationSpinner.setSelection(selectedOrientationIndex);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ try {
+ mPrintManager.startDiscoverPrinters(mPrinterDiscoveryObserver);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error starting printer discovery!", re);
+ }
+ notifyPrintableStartIfNeeded();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ try {
+ mPrintManager.stopDiscoverPrinters();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error starting printer discovery!", re);
+ }
+ notifyPrintableFinishIfNeeded();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.print_job_config_activity, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.print_button) {
+ mPrintConfirmed = true;
+ finish();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void notifyPrintableStartIfNeeded() {
+ if (mDestinationSpinner.getSelectedItemPosition() < 0
+ || mPrintStarted) {
+ return;
+ }
+ mPrintStarted = true;
+ new QueuedAsyncTask<Void>(mTaskQueue) {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ mRemotePrintAdapter.start();
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error reading printed data!", ioe);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ updatePrintableContentIfNeeded();
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ private void updatePrintableContentIfNeeded() {
+ if (!mPrintStarted) {
+ return;
+ }
+
+ mPrintSpooler.setPrintJobAttributes(mPrintJobId, mPrintAttributes);
+
+ // TODO: Implement page selector.
+ final List<PageRange> pages = new ArrayList<PageRange>();
+ pages.add(PageRange.ALL_PAGES);
+
+ new QueuedAsyncTask<File>(mTaskQueue) {
+ @Override
+ protected File doInBackground(Void... params) {
+ try {
+ mRemotePrintAdapter.printAttributesChanged(mPrintAttributes);
+ mRemotePrintAdapter.cancelPrint();
+ mRemotePrintAdapter.print(pages);
+ return mRemotePrintAdapter.getFile();
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error reading printed data!", ioe);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(File file) {
+ super.onPostExecute(file);
+ updatePrintPreview(file);
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ private void notifyPrintableFinishIfNeeded() {
+ if (!mPrintStarted) {
+ return;
+ }
+ mPrintStarted = false;
+
+ // Cancel all pending async tasks if the activity was canceled.
+ if (!mPrintConfirmed) {
+ final int taskCount = mTaskQueue.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ mTaskQueue.remove(i).cancel();
+ }
+ }
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // Notify the app that printing completed.
+ try {
+ mRemotePrintAdapter.finish();
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error reading printed data!", ioe);
+ }
+
+ // If canceled, nothing to do.
+ if (!mPrintConfirmed) {
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_CANCELED);
+ return null;
+ }
+
+ // No printer, nothing to do.
+ final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
+ if (selectedIndex < 0) {
+ // Update the print job's status.
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_CANCELED);
+ return null;
+ }
+
+ // Update the print job's printer.
+ SpinnerItem<PrinterInfo> printerItem =
+ mDestinationSpinnerAdapter.getItem(selectedIndex);
+ PrinterId printerId = printerItem.value.getId();
+ mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printerId);
+
+ // Update the print job's status.
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_QUEUED);
+ return null;
+ }
+
+ // Important: If we are canceling, then we do not wait for the write
+ // to complete since the result will be discarded anyway, we simply
+ // execute the finish immediately which will interrupt the write.
+ }.executeOnExecutor(mPrintConfirmed ? AsyncTask.SERIAL_EXECUTOR
+ : AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+
+ if (DEBUG) {
+ if (mPrintConfirmed) {
+ File file = mRemotePrintAdapter.getFile();
+ if (file.exists()) {
+ new ViewSpooledFileAsyncTask(file).executeOnExecutor(
+ AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+ }
+ }
+ }
+
+ private void updatePrintPreview(File file) {
+ // TODO: Implement
+ }
+
+ private void addPrinters(List<PrinterInfo> addedPrinters) {
+ final int addedPrinterCount = addedPrinters.size();
+ for (int i = 0; i < addedPrinterCount; i++) {
+ PrinterInfo addedPrinter = addedPrinters.get(i);
+ boolean duplicate = false;
+ final int existingPrinterCount = mDestinationSpinnerAdapter.getCount();
+ for (int j = 0; j < existingPrinterCount; j++) {
+ PrinterInfo existingPrinter = mDestinationSpinnerAdapter.getItem(j).value;
+ if (addedPrinter.getId().equals(existingPrinter.getId())) {
+ duplicate = true;
+ break;
+ }
+ }
+ if (!duplicate) {
+ mDestinationSpinnerAdapter.add(new SpinnerItem<PrinterInfo>(
+ addedPrinter, addedPrinter.getLabel()));
+ } else {
+ Log.w(LOG_TAG, "Skipping a duplicate printer: " + addedPrinter);
+ }
+ }
+ }
+
+ private void removePrinters(List<PrinterId> pritnerIds) {
+ final int printerIdCount = pritnerIds.size();
+ for (int i = 0; i < printerIdCount; i++) {
+ PrinterId removedPrinterId = pritnerIds.get(i);
+ boolean removed = false;
+ final int existingPrinterCount = mDestinationSpinnerAdapter.getCount();
+ for (int j = 0; j < existingPrinterCount; j++) {
+ PrinterInfo existingPrinter = mDestinationSpinnerAdapter.getItem(j).value;
+ if (removedPrinterId.equals(existingPrinter.getId())) {
+ mDestinationSpinnerAdapter.remove(mDestinationSpinnerAdapter.getItem(j));
+ removed = true;
+ break;
+ }
+ }
+ if (!removed) {
+ Log.w(LOG_TAG, "Ignoring not added printer with id: " + removedPrinterId);
+ }
+ }
+ }
+
+ private abstract class QueuedAsyncTask<T> extends AsyncTask<Void, Void, T> {
+
+ private final List<QueuedAsyncTask<?>> mPendingOrRunningTasks;
+
+ public QueuedAsyncTask(List<QueuedAsyncTask<?>> pendingOrRunningTasks) {
+ mPendingOrRunningTasks = pendingOrRunningTasks;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mPendingOrRunningTasks.add(this);
+ }
+
+ @Override
+ protected void onPostExecute(T result) {
+ mPendingOrRunningTasks.remove(this);
+ }
+
+ public void cancel() {
+ super.cancel(true);
+ mPendingOrRunningTasks.remove(this);
+ }
+ }
+
+ private final class ViewSpooledFileAsyncTask extends AsyncTask<Void, Void, Void> {
+
+ private final File mFile;
+
+ public ViewSpooledFileAsyncTask(File file) {
+ mFile = file;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ mFile.setExecutable(true, false);
+ mFile.setWritable(true, false);
+ mFile.setReadable(true, false);
+
+ final long identity = Binder.clearCallingIdentity();
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.fromFile(mFile), "application/pdf");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivityAsUser(intent, null, UserHandle.CURRENT);
+ Binder.restoreCallingIdentity(identity);
+ return null;
+ }
+ }
+
+ private final class PrintDiscoveryObserver extends IPrinterDiscoveryObserver.Stub {
+ private static final int MESSAGE_ADD_DICOVERED_PRINTERS = 1;
+ private static final int MESSAGE_REMOVE_DICOVERED_PRINTERS = 2;
+
+ private final Handler mHandler;
+
+ @SuppressWarnings("unchecked")
+ public PrintDiscoveryObserver(Looper looper) {
+ mHandler = new Handler(looper, null, true) {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_ADD_DICOVERED_PRINTERS: {
+ List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
+ addPrinters(printers);
+ // Just added the first printer, so select it and start printing.
+ if (mDestinationSpinnerAdapter.getCount() == 1) {
+ mDestinationSpinner.setSelection(0);
+ }
+ } break;
+ case MESSAGE_REMOVE_DICOVERED_PRINTERS: {
+ List<PrinterId> printerIds = (List<PrinterId>) message.obj;
+ removePrinters(printerIds);
+ // TODO: Handle removing the last printer.
+ } break;
+ }
+ }
+ };
+ }
+
+ @Override
+ public void addDiscoveredPrinters(List<PrinterInfo> printers) {
+ mHandler.obtainMessage(MESSAGE_ADD_DICOVERED_PRINTERS, printers).sendToTarget();
+ }
+
+ @Override
+ public void removeDiscoveredPrinters(List<PrinterId> printers) {
+ mHandler.obtainMessage(MESSAGE_REMOVE_DICOVERED_PRINTERS, printers).sendToTarget();
+ }
+ }
+
+ private final class SpinnerItem<T> {
+ final T value;
+ CharSequence label;
+
+ public SpinnerItem(T value, CharSequence label) {
+ this.value = value;
+ this.label = label;
+ }
+
+ public String toString() {
+ return label.toString();
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
new file mode 100644
index 0000000..2b27b69
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.print.IPrintClient;
+import android.print.IPrintManager;
+import android.print.PrintAttributes;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+
+public class PrintSpooler {
+
+ private static final String LOG_TAG = PrintSpooler.class.getSimpleName();
+
+ private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
+
+ private static final boolean DEBUG_PERSISTENCE = false;
+
+ private static final boolean PERSISTNECE_MANAGER_ENABLED = false;
+
+ private static final String PRINT_FILE_EXTENSION = "pdf";
+
+ private static int sPrintJobIdCounter;
+
+ private static final Object sLock = new Object();
+
+ private final Object mLock = new Object();
+
+ private static PrintSpooler sInstance;
+
+ private final List<PrintJobInfo> mPrintJobs = new ArrayList<PrintJobInfo>();
+
+ private final PersistenceManager mPersistanceManager;
+
+ private final Context mContext;
+
+ private final IPrintManager mPrintManager;
+
+ public static PrintSpooler getInstance(Context context) {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new PrintSpooler(context);
+ }
+ return sInstance;
+ }
+ }
+
+ private PrintSpooler(Context context) {
+ mContext = context;
+ mPersistanceManager = new PersistenceManager();
+ mPersistanceManager.readStateLocked();
+ mPrintManager = IPrintManager.Stub.asInterface(
+ ServiceManager.getService("print"));
+ }
+
+ public List<PrintJobInfo> getPrintJobs(ComponentName componentName, int state, int appId) {
+ synchronized (mLock) {
+ List<PrintJobInfo> foundPrintJobs = null;
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ PrinterId printerId = printJob.getPrinterId();
+ final boolean sameComponent = (componentName == null
+ || (printerId != null
+ && componentName.equals(printerId.getServiceComponentName())));
+ final boolean sameAppId = appId == PrintManager.APP_ID_ANY
+ || printJob.getAppId() == appId;
+ final boolean sameState = state == PrintJobInfo.STATE_ANY
+ || state == printJob.getState();
+ if (sameComponent && sameAppId && sameState) {
+ if (foundPrintJobs == null) {
+ foundPrintJobs = new ArrayList<PrintJobInfo>();
+ }
+ foundPrintJobs.add(printJob);
+ }
+ }
+ return foundPrintJobs;
+ }
+ }
+
+ public PrintJobInfo getPrintJob(int printJobId, int appId) {
+ synchronized (mLock) {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ if (printJob.getId() == printJobId
+ && (appId == PrintManager.APP_ID_ANY || appId == printJob.getAppId())) {
+ return printJob;
+ }
+ }
+ return null;
+ }
+ }
+
+ public boolean cancelPrintJob(int printJobId, int appId) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJob(printJobId, appId);
+ if (printJob != null) {
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_CREATED: {
+ removePrintJobLocked(printJob);
+ } return true;
+ case PrintJobInfo.STATE_QUEUED: {
+ removePrintJobLocked(printJob);
+ } return true;
+ default: {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ public PrintJobInfo createPrintJob(CharSequence label, IPrintClient client,
+ PrintAttributes attributes, int appId) {
+ synchronized (mLock) {
+ final int printJobId = generatePrintJobIdLocked();
+ PrintJobInfo printJob = new PrintJobInfo();
+ printJob.setId(printJobId);
+ printJob.setAppId(appId);
+ printJob.setLabel(label);
+ printJob.setAttributes(attributes);
+
+ addPrintJobLocked(printJob);
+ setPrintJobState(printJobId, PrintJobInfo.STATE_CREATED);
+
+ return printJob;
+ }
+ }
+
+ private int generatePrintJobIdLocked() {
+ int printJobId = sPrintJobIdCounter++;
+ while (isDuplicatePrintJobId(printJobId)) {
+ printJobId = sPrintJobIdCounter++;
+ }
+ return printJobId;
+ }
+
+ private boolean isDuplicatePrintJobId(int printJobId) {
+ final int printJobCount = mPrintJobs.size();
+ for (int j = 0; j < printJobCount; j++) {
+ PrintJobInfo printJob = mPrintJobs.get(j);
+ if (printJob.getId() == printJobId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("resource")
+ public boolean writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
+ synchronized (mLock) {
+ FileInputStream in = null;
+ FileOutputStream out = null;
+ try {
+ PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ File file = generateFileForPrintJob(printJobId);
+ in = new FileInputStream(file);
+ out = new FileOutputStream(fd.getFileDescriptor());
+ final byte[] buffer = new byte[8192];
+ while (true) {
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ return true;
+ }
+ out.write(buffer, 0, readByteCount);
+ }
+ }
+ } catch (FileNotFoundException fnfe) {
+ Log.e(LOG_TAG, "Error writing print job data!", fnfe);
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error writing print job data!", ioe);
+ } finally {
+ closeIfNotNullNoException(in);
+ closeIfNotNullNoException(out);
+ closeIfNotNullNoException(fd);
+ }
+ }
+ return false;
+ }
+
+ private void closeIfNotNullNoException(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException ioe) {
+ /* ignore */;
+ }
+ }
+ }
+
+ public File generateFileForPrintJob(int printJobId) {
+ return new File(mContext.getFilesDir(), "print_job_"
+ + printJobId + "." + PRINT_FILE_EXTENSION);
+ }
+
+ private void addPrintJobLocked(PrintJobInfo printJob) {
+ mPrintJobs.add(printJob);
+ if (DEBUG_PRINT_JOB_LIFECYCLE) {
+ Slog.i(LOG_TAG, "[ADD] " + printJob);
+ }
+ }
+
+ private void removePrintJobLocked(PrintJobInfo printJob) {
+ if (mPrintJobs.remove(printJob)) {
+ generateFileForPrintJob(printJob.getId()).delete();
+ if (DEBUG_PRINT_JOB_LIFECYCLE) {
+ Slog.i(LOG_TAG, "[REMOVE] " + printJob);
+ }
+ }
+ }
+
+ public boolean setPrintJobState(int printJobId, int state) {
+ boolean success = false;
+ PrintJobInfo queuedPrintJob = null;
+
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null && printJob.getState() < state) {
+ success = true;
+ printJob.setState(state);
+ // TODO: Update notifications.
+ switch (state) {
+ case PrintJobInfo.STATE_COMPLETED:
+ case PrintJobInfo.STATE_CANCELED: {
+ removePrintJobLocked(printJob);
+ } break;
+ case PrintJobInfo.STATE_QUEUED: {
+ queuedPrintJob = new PrintJobInfo(printJob);
+ } break;
+ }
+ if (DEBUG_PRINT_JOB_LIFECYCLE) {
+ Slog.i(LOG_TAG, "[STATUS CHANGED] " + printJob);
+ }
+ mPersistanceManager.writeStateLocked();
+ }
+ }
+
+ if (queuedPrintJob != null) {
+ try {
+ mPrintManager.onPrintJobQueued(queuedPrintJob.getPrinterId(),
+ queuedPrintJob);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+
+ return success;
+ }
+
+ public boolean setPrintJobTag(int printJobId, String tag) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ printJob.setTag(tag);
+ mPersistanceManager.writeStateLocked();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setPrintJobAttributes(int printJobId, PrintAttributes attributes) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ printJob.setAttributes(attributes);
+ mPersistanceManager.writeStateLocked();
+ }
+ }
+ }
+
+ public void setPrintJobPrinterId(int printJobId, PrinterId printerId) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ printJob.setPrinterId(printerId);
+ mPersistanceManager.writeStateLocked();
+ }
+ }
+ }
+
+ private final class PersistenceManager {
+ private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
+
+ private static final String TAG_SPOOLER = "spooler";
+ private static final String TAG_JOB = "job";
+ private static final String TAG_ID = "id";
+ private static final String TAG_TAG = "tag";
+ private static final String TAG_APP_ID = "app-id";
+ private static final String TAG_STATE = "state";
+ private static final String TAG_ATTRIBUTES = "attributes";
+ private static final String TAG_LABEL = "label";
+ private static final String TAG_PRINTER = "printer";
+
+ private static final String ATTRIBUTE_MEDIA_SIZE = "mediaSize";
+ private static final String ATTRIBUTE_RESOLUTION = "resolution";
+ private static final String ATTRIBUTE_MARGINS = "margins";
+ private static final String ATTRIBUTE_INPUT_TRAY = "inputTray";
+ private static final String ATTRIBUTE_OUTPUT_TRAY = "outputTray";
+ private static final String ATTRIBUTE_DUPLEX_MODE = "duplexMode";
+ private static final String ATTRIBUTE_COLOR_MODE = "colorMode";
+ private static final String ATTRIBUTE_FITTING_MODE = "fittingMode";
+ private static final String ATTRIBUTE_ORIENTATION = "orientation";
+
+ private final AtomicFile mStatePersistFile;
+
+ private boolean mWriteStateScheduled;
+
+ private PersistenceManager() {
+ mStatePersistFile = new AtomicFile(new File(mContext.getFilesDir(),
+ PERSIST_FILE_NAME));
+ }
+
+ public void writeStateLocked() {
+ // TODO: Implement persistence of PrintableInfo
+ if (!PERSISTNECE_MANAGER_ENABLED) {
+ return;
+ }
+ if (mWriteStateScheduled) {
+ return;
+ }
+ mWriteStateScheduled = true;
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ synchronized (mLock) {
+ mWriteStateScheduled = false;
+ doWriteStateLocked();
+ }
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ private void doWriteStateLocked() {
+ FileOutputStream out = null;
+ try {
+ out = mStatePersistFile.startWrite();
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(out, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_SPOOLER);
+
+ List<PrintJobInfo> printJobs = mPrintJobs;
+
+ final int printJobCount = printJobs.size();
+ for (int j = 0; j < printJobCount; j++) {
+ PrintJobInfo printJob = printJobs.get(j);
+
+ final int state = printJob.getState();
+ if (state < PrintJobInfo.STATE_QUEUED
+ || state > PrintJobInfo.STATE_FAILED) {
+ continue;
+ }
+
+ serializer.startTag(null, TAG_JOB);
+
+ serializer.startTag(null, TAG_ID);
+ serializer.text(String.valueOf(printJob.getId()));
+ serializer.endTag(null, TAG_ID);
+
+ serializer.startTag(null, TAG_TAG);
+ serializer.text(printJob.getTag());
+ serializer.endTag(null, TAG_TAG);
+
+ serializer.startTag(null, TAG_APP_ID);
+ serializer.text(String.valueOf(printJob.getAppId()));
+ serializer.endTag(null, TAG_APP_ID);
+
+ serializer.startTag(null, TAG_LABEL);
+ serializer.text(printJob.getLabel().toString());
+ serializer.endTag(null, TAG_LABEL);
+
+ serializer.startTag(null, TAG_STATE);
+ serializer.text(String.valueOf(printJob.getState()));
+ serializer.endTag(null, TAG_STATE);
+
+ serializer.startTag(null, TAG_PRINTER);
+ serializer.text(printJob.getPrinterId().flattenToString());
+ serializer.endTag(null, TAG_PRINTER);
+
+ PrintAttributes attributes = printJob.getAttributes();
+ if (attributes != null) {
+ serializer.startTag(null, TAG_ATTRIBUTES);
+
+ //TODO: Implement persistence of the attributes below.
+
+// MediaSize mediaSize = attributes.getMediaSize();
+// if (mediaSize != null) {
+// serializer.attribute(null, ATTRIBUTE_MEDIA_SIZE,
+// mediaSize.flattenToString());
+// }
+//
+// Resolution resolution = attributes.getResolution();
+// if (resolution != null) {
+// serializer.attribute(null, ATTRIBUTE_RESOLUTION,
+// resolution.flattenToString());
+// }
+//
+// Margins margins = attributes.getMargins();
+// if (margins != null) {
+// serializer.attribute(null, ATTRIBUTE_MARGINS,
+// margins.flattenToString());
+// }
+//
+// Tray inputTray = attributes.getInputTray();
+// if (inputTray != null) {
+// serializer.attribute(null, ATTRIBUTE_INPUT_TRAY,
+// inputTray.flattenToString());
+// }
+//
+// Tray outputTray = attributes.getOutputTray();
+// if (outputTray != null) {
+// serializer.attribute(null, ATTRIBUTE_OUTPUT_TRAY,
+// outputTray.flattenToString());
+// }
+
+ final int duplexMode = attributes.getDuplexMode();
+ if (duplexMode > 0) {
+ serializer.attribute(null, ATTRIBUTE_DUPLEX_MODE,
+ String.valueOf(duplexMode));
+ }
+
+ final int colorMode = attributes.getColorMode();
+ if (colorMode > 0) {
+ serializer.attribute(null, ATTRIBUTE_COLOR_MODE,
+ String.valueOf(colorMode));
+ }
+
+ final int fittingMode = attributes.getFittingMode();
+ if (fittingMode > 0) {
+ serializer.attribute(null, ATTRIBUTE_FITTING_MODE,
+ String.valueOf(fittingMode));
+ }
+
+ final int orientation = attributes.getOrientation();
+ if (orientation > 0) {
+ serializer.attribute(null, ATTRIBUTE_ORIENTATION,
+ String.valueOf(orientation));
+ }
+
+ serializer.endTag(null, TAG_ATTRIBUTES);
+ }
+
+ serializer.endTag(null, TAG_JOB);
+
+ if (DEBUG_PERSISTENCE) {
+ Log.i(LOG_TAG, "[PERSISTED] " + printJob);
+ }
+ }
+
+ serializer.endTag(null, TAG_SPOOLER);
+ serializer.endDocument();
+ mStatePersistFile.finishWrite(out);
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
+ mStatePersistFile.failWrite(out);
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+ }
+
+ public void readStateLocked() {
+ if (!PERSISTNECE_MANAGER_ENABLED) {
+ return;
+ }
+ FileInputStream in = null;
+ try {
+ in = mStatePersistFile.openRead();
+ } catch (FileNotFoundException e) {
+ Log.i(LOG_TAG, "No existing print spooler state.");
+ return;
+ }
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parseState(parser);
+ } catch (IllegalStateException ise) {
+ Slog.w(LOG_TAG, "Failed parsing " + ise);
+ } catch (NullPointerException npe) {
+ Slog.w(LOG_TAG, "Failed parsing " + npe);
+ } catch (NumberFormatException nfe) {
+ Slog.w(LOG_TAG, "Failed parsing " + nfe);
+ } catch (XmlPullParserException xppe) {
+ Slog.w(LOG_TAG, "Failed parsing " + xppe);
+ } catch (IOException ioe) {
+ Slog.w(LOG_TAG, "Failed parsing " + ioe);
+ } catch (IndexOutOfBoundsException iobe) {
+ Slog.w(LOG_TAG, "Failed parsing " + iobe);
+ } finally {
+ try {
+ in.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+
+ private void parseState(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER);
+ parser.next();
+
+ while (parsePrintJob(parser)) {
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER);
+ }
+
+ private boolean parsePrintJob(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
+ return false;
+ }
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_ID);
+ parser.next();
+ final int printJobId = Integer.parseInt(parser.getText());
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ID);
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_TAG);
+ parser.next();
+ String tag = parser.getText();
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_TAG);
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_APP_ID);
+ parser.next();
+ final int appId = Integer.parseInt(parser.getText());
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_APP_ID);
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_LABEL);
+ parser.next();
+ String label = parser.getText();
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_LABEL);
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_STATE);
+ parser.next();
+ final int state = Integer.parseInt(parser.getText());
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_STATE);
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_PRINTER);
+ parser.next();
+ PrinterId printerId = PrinterId.unflattenFromString(parser.getText());
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES);
+
+ final int attributeCount = parser.getAttributeCount();
+ PrintAttributes attributes = null;
+ if (attributeCount > 0) {
+ PrintAttributes.Builder builder = new PrintAttributes.Builder();
+
+ // TODO: Implement reading of the attributes below.
+
+// String mediaSize = parser.getAttributeValue(null, ATTRIBUTE_MEDIA_SIZE);
+// if (mediaSize != null) {
+// builder.setMediaSize(MediaSize.unflattenFromString(mediaSize));
+// }
+//
+// String resolution = parser.getAttributeValue(null, ATTRIBUTE_RESOLUTION);
+// if (resolution != null) {
+// builder.setMediaSize(Resolution.unflattenFromString(resolution));
+// }
+//
+// String margins = parser.getAttributeValue(null, ATTRIBUTE_MARGINS);
+// if (margins != null) {
+// builder.setMediaSize(Margins.unflattenFromString(margins));
+// }
+//
+// String inputTray = parser.getAttributeValue(null, ATTRIBUTE_INPUT_TRAY);
+// if (inputTray != null) {
+// builder.setMediaSize(Tray.unflattenFromString(inputTray));
+// }
+//
+// String outputTray = parser.getAttributeValue(null, ATTRIBUTE_OUTPUT_TRAY);
+// if (outputTray != null) {
+// builder.setMediaSize(Tray.unflattenFromString(outputTray));
+// }
+//
+// String duplexMode = parser.getAttributeValue(null, ATTRIBUTE_DUPLEX_MODE);
+// if (duplexMode != null) {
+// builder.setDuplexMode(Integer.parseInt(duplexMode));
+// }
+
+ String colorMode = parser.getAttributeValue(null, ATTRIBUTE_COLOR_MODE);
+ if (colorMode != null) {
+ builder.setColorMode(Integer.parseInt(colorMode));
+ }
+
+ String fittingMode = parser.getAttributeValue(null, ATTRIBUTE_COLOR_MODE);
+ if (fittingMode != null) {
+ builder.setFittingMode(Integer.parseInt(fittingMode));
+ }
+ }
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
+ parser.next();
+
+ PrintJobInfo printJob = new PrintJobInfo();
+ printJob.setId(printJobId);
+ printJob.setTag(tag);
+ printJob.setAppId(appId);
+ printJob.setLabel(label);
+ printJob.setState(state);
+ printJob.setAttributes(attributes);
+ printJob.setPrinterId(printerId);
+
+ mPrintJobs.add(printJob);
+
+ if (DEBUG_PERSISTENCE) {
+ Log.i(LOG_TAG, "[RESTORED] " + printJob);
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_JOB);
+
+ return true;
+ }
+
+ private void expect(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (!accept(parser, type, tag)) {
+ throw new XmlPullParserException("Exepected event: " + type
+ + " and tag: " + tag + " but got event: " + parser.getEventType()
+ + " and tag:" + parser.getName());
+ }
+ }
+
+ private void skipEmptyTextTags(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ while (accept(parser, XmlPullParser.TEXT, null)
+ && "\n".equals(parser.getText())) {
+ parser.next();
+ }
+ }
+
+ private boolean accept(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (parser.getEventType() != type) {
+ return false;
+ }
+ if (tag != null) {
+ if (!tag.equals(parser.getName())) {
+ return false;
+ }
+ } else if (parser.getName() != null) {
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
new file mode 100644
index 0000000..57c4557
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler;
+
+import java.util.List;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.IPrintAdapter;
+import android.print.IPrintClient;
+import android.print.IPrintSpoolerService;
+import android.print.IPrintSpoolerServiceCallbacks;
+import android.print.PrintAttributes;
+import android.print.PrintJobInfo;
+import android.util.Slog;
+
+import com.android.internal.os.SomeArgs;
+
+/**
+ * Service for exposing some of the {@link PrintSpooler} functionality to
+ * another process.
+ */
+public final class PrintSpoolerService extends Service {
+
+ private static final String LOG_TAG = "PrintSpoolerService";
+
+ private Intent mStartPrintJobConfigActivityIntent;
+
+ private PrintSpooler mSpooler;
+
+ private Handler mHanlder;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mStartPrintJobConfigActivityIntent = new Intent(PrintSpoolerService.this,
+ PrintJobConfigActivity.class);
+ mSpooler = PrintSpooler.getInstance(this);
+ mHanlder = new MyHandler(getMainLooper());
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new IPrintSpoolerService.Stub() {
+ @Override
+ public void getPrintJobs(IPrintSpoolerServiceCallbacks callback,
+ ComponentName componentName, int state, int appId, int sequence)
+ throws RemoteException {
+ List<PrintJobInfo> printJobs = null;
+ try {
+ printJobs = mSpooler.getPrintJobs(componentName, state, appId);
+ } finally {
+ callback.onGetPrintJobsResult(printJobs, sequence);
+ }
+ }
+
+ @Override
+ public void getPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ int appId, int sequence) throws RemoteException {
+ PrintJobInfo printJob = null;
+ try {
+ printJob = mSpooler.getPrintJob(printJobId, appId);
+ } finally {
+ callback.onGetPrintJobInfoResult(printJob, sequence);
+ }
+ }
+
+ @Override
+ public void cancelPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ int appId, int sequence) throws RemoteException {
+ boolean success = false;
+ try {
+ success = mSpooler.cancelPrintJob(printJobId, appId);
+ } finally {
+ callback.onCancelPrintJobResult(success, sequence);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void createPrintJob(String printJobName, IPrintClient client,
+ IPrintAdapter printAdapter, PrintAttributes attributes,
+ IPrintSpoolerServiceCallbacks callback, int appId, int sequence)
+ throws RemoteException {
+ PrintJobInfo printJob = null;
+ try {
+ printJob = mSpooler.createPrintJob(printJobName, client,
+ attributes, appId);
+ if (printJob != null) {
+ Intent intent = mStartPrintJobConfigActivityIntent;
+ intent.putExtra(PrintJobConfigActivity.EXTRA_PRINTABLE,
+ printAdapter.asBinder());
+ intent.putExtra(PrintJobConfigActivity.EXTRA_APP_ID, appId);
+ intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_JOB_ID,
+ printJob.getId());
+ intent.putExtra(PrintJobConfigActivity.EXTRA_ATTRIBUTES, attributes);
+
+ IntentSender sender = PendingIntent.getActivity(
+ PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = client;
+ args.arg2 = sender;
+ mHanlder.obtainMessage(0, args).sendToTarget();
+ }
+ } finally {
+ callback.onCreatePrintJobResult(printJob, sequence);
+ }
+ }
+
+ @Override
+ public void setPrintJobState(int printJobId, int state,
+ IPrintSpoolerServiceCallbacks callback, int sequece)
+ throws RemoteException {
+ boolean success = false;
+ try {
+ // TODO: Make sure the clients (print services) can set the state
+ // only to acceptable ones, e.g. not settings STATE_CREATED.
+ success = mSpooler.setPrintJobState(printJobId, state);
+ } finally {
+ callback.onSetPrintJobStateResult(success, sequece);
+ }
+ }
+
+ @Override
+ public void setPrintJobTag(int printJobId, String tag,
+ IPrintSpoolerServiceCallbacks callback, int sequece)
+ throws RemoteException {
+ boolean success = false;
+ try {
+ success = mSpooler.setPrintJobTag(printJobId, tag);
+ } finally {
+ callback.onSetPrintJobTagResult(success, sequece);
+ }
+ }
+
+ @Override
+ public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
+ mSpooler.writePrintJobData(fd, printJobId);
+ }
+ };
+ }
+
+ private static final class MyHandler extends Handler {
+
+ public MyHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ SomeArgs args = (SomeArgs) message.obj;
+ IPrintClient client = (IPrintClient) args.arg1;
+ IntentSender sender = (IntentSender) args.arg2;
+ args.recycle();
+ try {
+ client.startPrintJobConfigActivity(sender);
+ } catch (RemoteException re) {
+ Slog.i(LOG_TAG, "Error starting print job config activity!", re);
+ }
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java
new file mode 100644
index 0000000..c81b00c
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler;
+
+import android.os.ICancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.IPrintAdapter;
+import android.print.IPrintResultCallback;
+import android.print.PageRange;
+import android.print.PrintAdapterInfo;
+import android.print.PrintAttributes;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * This class represents a remote print adapter instance.
+ */
+final class RemotePrintAdapter {
+ private static final String LOG_TAG = "RemotePrintAdapter";
+
+ private static final boolean DEBUG = true;
+
+ private final Object mLock = new Object();
+
+ private final IPrintAdapter mRemoteInterface;
+
+ private final File mFile;
+
+ private final IPrintResultCallback mIPrintProgressListener;
+
+ private PrintAdapterInfo mInfo;
+
+ private ICancellationSignal mCancellationSignal;
+
+ private Thread mWriteThread;
+
+ public RemotePrintAdapter(IPrintAdapter printAdatper, File file) {
+ mRemoteInterface = printAdatper;
+ mFile = file;
+ mIPrintProgressListener = new IPrintResultCallback.Stub() {
+ @Override
+ public void onPrintStarted(PrintAdapterInfo info,
+ ICancellationSignal cancellationSignal) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "IPrintProgressListener#onPrintStarted()");
+ }
+ synchronized (mLock) {
+ mInfo = info;
+ mCancellationSignal = cancellationSignal;
+ }
+ }
+
+ @Override
+ public void onPrintFinished(List<PageRange> pages) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "IPrintProgressListener#onPrintFinished(" + pages + ")");
+ }
+ synchronized (mLock) {
+ if (isPrintingLocked()) {
+ mWriteThread.interrupt();
+ mCancellationSignal = null;
+ }
+ }
+ }
+
+ @Override
+ public void onPrintFailed(CharSequence error) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "IPrintProgressListener#onPrintFailed(" + error + ")");
+ }
+ synchronized (mLock) {
+ if (isPrintingLocked()) {
+ mWriteThread.interrupt();
+ mCancellationSignal = null;
+ }
+ }
+ }
+ };
+ }
+
+ public File getFile() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "getFile()");
+ }
+ return mFile;
+ }
+
+ public void start() throws IOException {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "start()");
+ }
+ try {
+ mRemoteInterface.start();
+ } catch (RemoteException re) {
+ throw new IOException("Error reading file", re);
+ }
+ }
+
+ public void printAttributesChanged(PrintAttributes attributes) throws IOException {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "printAttributesChanged(" + attributes +")");
+ }
+ try {
+ mRemoteInterface.printAttributesChanged(attributes);
+ } catch (RemoteException re) {
+ throw new IOException("Error reading file", re);
+ }
+ }
+
+ public void print(List<PageRange> pages) throws IOException {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "print(" + pages +")");
+ }
+ InputStream in = null;
+ OutputStream out = null;
+ ParcelFileDescriptor source = null;
+ ParcelFileDescriptor sink = null;
+ synchronized (mLock) {
+ mWriteThread = Thread.currentThread();
+ }
+ try {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ source = pipe[0];
+ sink = pipe[1];
+
+ in = new FileInputStream(source.getFileDescriptor());
+ out = new FileOutputStream(mFile);
+
+ // Async call to initiate the other process writing the data.
+ mRemoteInterface.print(pages, sink, mIPrintProgressListener);
+
+ // Close the source. It is now held by the client.
+ sink.close();
+ sink = null;
+
+ final byte[] buffer = new byte[8192];
+ while (true) {
+ if (Thread.currentThread().isInterrupted()) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ break;
+ }
+ out.write(buffer, 0, readByteCount);
+ }
+ } catch (RemoteException re) {
+ throw new IOException("Error reading file", re);
+ } catch (IOException ioe) {
+ throw new IOException("Error reading file", ioe);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(sink);
+ IoUtils.closeQuietly(source);
+ }
+ }
+
+ public void cancelPrint() throws IOException {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "cancelPrint()");
+ }
+ synchronized (mLock) {
+ if (isPrintingLocked()) {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException re) {
+ throw new IOException("Error cancelling print", re);
+ }
+ }
+ }
+ }
+
+ public void finish() throws IOException {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "finish()");
+ }
+ try {
+ mRemoteInterface.finish();
+ } catch (RemoteException re) {
+ throw new IOException("Error reading file", re);
+ }
+ }
+
+ public PrintAdapterInfo getInfo() {
+ synchronized (mLock) {
+ return mInfo;
+ }
+ }
+
+ private boolean isPrintingLocked() {
+ return mCancellationSignal != null;
+ }
+}
diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk
index a2ea554..da929ae 100644
--- a/packages/SettingsProvider/Android.mk
+++ b/packages/SettingsProvider/Android.mk
@@ -9,6 +9,7 @@
LOCAL_PACKAGE_NAME := SettingsProvider
LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PACKAGE)
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 0177504..6753922 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -49,7 +49,6 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.DrmStore;
import android.provider.MediaStore;
import android.provider.Settings;
import android.text.TextUtils;
@@ -992,7 +991,7 @@
/*
* When a client attempts to openFile the default ringtone or
* notification setting Uri, we will proxy the call to the current
- * default ringtone's Uri (if it is in the DRM or media provider).
+ * default ringtone's Uri (if it is in the media provider).
*/
int ringtoneType = RingtoneManager.getDefaultType(uri);
// Above call returns -1 if the Uri doesn't match a default type
@@ -1003,22 +1002,9 @@
Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
if (soundUri != null) {
- // Only proxy the openFile call to drm or media providers
+ // Proxy the openFile call to media provider
String authority = soundUri.getAuthority();
- boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
- if (isDrmAuthority || authority.equals(MediaStore.AUTHORITY)) {
-
- if (isDrmAuthority) {
- try {
- // Check DRM access permission here, since once we
- // do the below call the DRM will be checking our
- // permission, not our caller's permission
- DrmStore.enforceAccessDrmPermission(context);
- } catch (SecurityException e) {
- throw new FileNotFoundException(e.getMessage());
- }
- }
-
+ if (authority.equals(MediaStore.AUTHORITY)) {
return context.getContentResolver().openFileDescriptor(soundUri, mode);
}
}
@@ -1033,7 +1019,7 @@
/*
* When a client attempts to openFile the default ringtone or
* notification setting Uri, we will proxy the call to the current
- * default ringtone's Uri (if it is in the DRM or media provider).
+ * default ringtone's Uri (if it is in the media provider).
*/
int ringtoneType = RingtoneManager.getDefaultType(uri);
// Above call returns -1 if the Uri doesn't match a default type
@@ -1044,22 +1030,9 @@
Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
if (soundUri != null) {
- // Only proxy the openFile call to drm or media providers
+ // Proxy the openFile call to media provider
String authority = soundUri.getAuthority();
- boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
- if (isDrmAuthority || authority.equals(MediaStore.AUTHORITY)) {
-
- if (isDrmAuthority) {
- try {
- // Check DRM access permission here, since once we
- // do the below call the DRM will be checking our
- // permission, not our caller's permission
- DrmStore.enforceAccessDrmPermission(context);
- } catch (SecurityException e) {
- throw new FileNotFoundException(e.getMessage());
- }
- }
-
+ if (authority.equals(MediaStore.AUTHORITY)) {
ParcelFileDescriptor pfd = null;
try {
pfd = context.getContentResolver().openFileDescriptor(soundUri, mode);
diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk
index 1d4f4da7..a213965f 100644
--- a/packages/SharedStorageBackup/Android.mk
+++ b/packages/SharedStorageBackup/Android.mk
@@ -25,6 +25,7 @@
LOCAL_PACKAGE_NAME := SharedStorageBackup
LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PACKAGE)
diff --git a/packages/Shell/Android.mk b/packages/Shell/Android.mk
index fc4c0f5..5bd48c6 100644
--- a/packages/Shell/Android.mk
+++ b/packages/Shell/Android.mk
@@ -9,5 +9,6 @@
LOCAL_PACKAGE_NAME := Shell
LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PACKAGE)
diff --git a/packages/Shell/res/values-af/strings.xml b/packages/Shell/res/values-af/strings.xml
new file mode 100644
index 0000000..3dc6a0f
--- /dev/null
+++ b/packages/Shell/res/values-af/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Tuisskerm"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Foutverslag vasgevang"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Raak om jou foutverslag te deel"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Foutverslae bevat data van die stelsel se verskillende loglêers af, insluitend persoonlike en private inligting. Deel foutverslae net met programme en mense wat jy vertrou."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Wys hierdie boodskap volgende keer"</string>
+</resources>
diff --git a/packages/Shell/res/values-am/strings.xml b/packages/Shell/res/values-am/strings.xml
new file mode 100644
index 0000000..c90a5f5
--- /dev/null
+++ b/packages/Shell/res/values-am/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"ቀፎ"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"የሳንካ ሪፖርት ተይዟል"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"የሳንካ ሪፖርትዎን ለማጋራት ይንክኩ"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"የሳንካ ሪፖርቶች የግል መረጃን ጨምሮ ከበርካታ የስርዓቱ ምዝግብ ማስታወሻዎች የመጣ ውሂብን ይዟል። የሳንካ ሪፖርቶች ለሚያምኗቸው መተግበሪያዎችን እና ሰዎችን ብቻ ያጋሩ።"</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ይህን መልዕክት በሚቀጥለው ጊዜ አሳይ"</string>
+</resources>
diff --git a/packages/Shell/res/values-ar/strings.xml b/packages/Shell/res/values-ar/strings.xml
new file mode 100644
index 0000000..6a595d5
--- /dev/null
+++ b/packages/Shell/res/values-ar/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"تم الحصول على تقرير الأخطاء"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"المس لمشاركة تقرير الأخطاء"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"تحتوي تقارير الأخطاء على بيانات من ملفات سجلات النظام المتنوعة، بما في ذلك معلومات شخصية وخاصة. لا تشارك تقارير الأخطاء إلا مع التطبيقات والأشخاص الموثوق بهم."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"إظهار هذه الرسالة في المرة القادمة"</string>
+</resources>
diff --git a/packages/Shell/res/values-be/strings.xml b/packages/Shell/res/values-be/strings.xml
new file mode 100644
index 0000000..e713975
--- /dev/null
+++ b/packages/Shell/res/values-be/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Абалонка"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Справаздача пра збой захавана"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Націсніце, каб падзяліцца сваёй справаздачай пра збой"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Справаздача пра памылку ўтрымлівае дадзеныя з гiсторыi сістэмных файлаў, у тым ліку персанальную і прыватную інфармацыю. Дзялiцеся справаздачамi пра збоi толькi з праверанымi карыстальнiкамi i прыкладаннямi."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"У наступны раз паказваць гэта паведамленне"</string>
+</resources>
diff --git a/packages/Shell/res/values-bg/strings.xml b/packages/Shell/res/values-bg/strings.xml
new file mode 100644
index 0000000..2fae953
--- /dev/null
+++ b/packages/Shell/res/values-bg/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Команден ред"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Отчетът за програмни грешки е записан"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Докоснете, за да споделите отчета си за програмни грешки"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Отчетите за програмни грешки съдържат данни от различни регистрационни файлове на системата, включително лична и поверителна информация. Споделяйте ги само с приложения и хора, на които имате доверие."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Това съобщение да се показва следващия път"</string>
+</resources>
diff --git a/packages/Shell/res/values-ca/strings.xml b/packages/Shell/res/values-ca/strings.xml
new file mode 100644
index 0000000..8bf368a
--- /dev/null
+++ b/packages/Shell/res/values-ca/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Protecció"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"S\'ha registrat l\'informe d\'error"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Toca aquí per compartir el teu informe d\'error."</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Els informes d\'error contenen dades dels diferents fitxers de registre del sistema, inclosa informació privada i personal. Comparteix els informes d\'error només amb les aplicacions i amb les persones en qui confies."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra aquest missatge la propera vegada"</string>
+</resources>
diff --git a/packages/Shell/res/values-cs/strings.xml b/packages/Shell/res/values-cs/strings.xml
new file mode 100644
index 0000000..effdcb9
--- /dev/null
+++ b/packages/Shell/res/values-cs/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Prostředí"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Byla vytvořena zpráva o chybě"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Zprávu o chybě můžete sdílet klepnutím."</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Zprávy o chybách obsahují data z různých souborů protokolů systému včetně osobních a soukromých informací. Zprávy o chybách sdílejte pouze s aplikacemi a uživateli, kterým důvěřujete."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobrazit tuto zprávu příště"</string>
+</resources>
diff --git a/packages/Shell/res/values-da/strings.xml b/packages/Shell/res/values-da/strings.xml
new file mode 100644
index 0000000..01ea42b
--- /dev/null
+++ b/packages/Shell/res/values-da/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Fejlrapporten er registreret"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Tryk for at dele din fejlrapport"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Fejlrapporter indeholder data fra systemets forskellige logfiler, herunder personlige og private oplysninger. Del kun fejlrapporter med apps og personer, du har tillid til."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne meddelelse næste gang"</string>
+</resources>
diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml
new file mode 100644
index 0000000..99522b1
--- /dev/null
+++ b/packages/Shell/res/values-de/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Fehlerbericht erfasst"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Berühren, um Fehlerbericht zu teilen"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Fehlerberichte enthalten Daten aus verschiedenen Protokolldateien des Systems, darunter auch personenbezogene und private Daten. Teilen Sie Fehlerberichte nur mit Apps und Personen, denen Sie vertrauen."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Diese Nachricht nächstes Mal zeigen"</string>
+</resources>
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
new file mode 100644
index 0000000..3669f78
--- /dev/null
+++ b/packages/Shell/res/values-el/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Κέλυφος"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Η λήψη της αναφοράς ήταν επιτυχής"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Αγγίξτε για κοινή χρήση της αναφοράς σας σφαλμάτων"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Οι αναφορές σφαλμάτων περιέχουν δεδομένα από τα διάφορα αρχεία καταγραφής του συστήματος, συμπεριλαμβανομένων προσωπικών και ιδιωτικών πληροφοριών. Να μοιράζεστε αναφορές σφαλμάτων μόνο με εφαρμογές και άτομα που εμπιστεύεστε."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Εμφάνιση αυτού του μηνύματος την επόμενη φορά"</string>
+</resources>
diff --git a/packages/Shell/res/values-en-rGB/strings.xml b/packages/Shell/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..68708e0
--- /dev/null
+++ b/packages/Shell/res/values-en-rGB/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Bug report captured"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Touch to share your bug report"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
+</resources>
diff --git a/packages/Shell/res/values-es-rUS/strings.xml b/packages/Shell/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..f1ec75c
--- /dev/null
+++ b/packages/Shell/res/values-es-rUS/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de errores capturado"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Toca para compartir tu informe de errores."</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida la información personal y privada. Comparte los informes de errores únicamente con aplicaciones y personas en las que confíes."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
+</resources>
diff --git a/packages/Shell/res/values-es/strings.xml b/packages/Shell/res/values-es/strings.xml
new file mode 100644
index 0000000..7990672
--- /dev/null
+++ b/packages/Shell/res/values-es/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de error capturado"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Toca para compartir tu informe de error"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida información personal y privada. Comparte los informes de errores únicamente con aplicaciones y usuarios en los que confíes."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
+</resources>
diff --git a/packages/Shell/res/values-et/strings.xml b/packages/Shell/res/values-et/strings.xml
new file mode 100644
index 0000000..7788158
--- /dev/null
+++ b/packages/Shell/res/values-et/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Kest"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Veaaruanne jäädvustati"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Veaaruande jagamiseks puudutage"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Veaaruanded sisaldavad andmeid erinevatest süsteemi logifailidest, sh isiklikku ja privaatset teavet. Jagage veaaruandeid ainult usaldusväärsete rakenduste ja inimestega."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Kuva see sõnum järgmisel korral"</string>
+</resources>
diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml
new file mode 100644
index 0000000..2d2c223
--- /dev/null
+++ b/packages/Shell/res/values-fa/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"گزارش اشکال دریافت شد"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"جهت اشتراکگذاری گزارش اشکال خود لمس کنید"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"گزارشهای اشکال حاوی دادههایی از فایلهای گزارش مختلف در سیستم هستند، شامل اطلاعات شخصی و خصوصی. گزارشهای اشکال را فقط با افراد و برنامههای مورد اعتماد خود به اشتراک بگذارید."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"دفعه بعد این پیام نشان داده شود"</string>
+</resources>
diff --git a/packages/Shell/res/values-fi/strings.xml b/packages/Shell/res/values-fi/strings.xml
new file mode 100644
index 0000000..ee57279
--- /dev/null
+++ b/packages/Shell/res/values-fi/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Komentotulkki"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Virheraportti tallennettu"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Jaa virheraportti koskettamalla tätä"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Virheraportit sisältävät järjestelmän lokitietoja, ja niihin voi sisältyä henkilökohtaisia ja yksityisiä tietoja. Jaa virheraportteja vain luotettaville sovelluksille ja käyttäjille."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Näytä tämä viesti seuraavalla kerralla"</string>
+</resources>
diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml
new file mode 100644
index 0000000..1da6f1f
--- /dev/null
+++ b/packages/Shell/res/values-fr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Rapport de bug enregistré"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Appuyer ici pour partager votre rapport de bug"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bug contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bug qu\'avec les applications et les personnes que vous estimez fiables."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
+</resources>
diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml
new file mode 100644
index 0000000..4ea0664
--- /dev/null
+++ b/packages/Shell/res/values-hi/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"शेल"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"बग रिपोर्ट कैप्चर कर ली गई"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"अपनी बग रिपोर्ट साझा करने के लिए स्पर्श करें"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"बग रिपोर्ट में व्यक्तिगत और निजी जानकारी सहित, सिस्टम की विभिन्न लॉग फ़ाइलों का डेटा होता है. बग रिपोर्ट केवल विश्वसनीय एप्लिकेशन और व्यक्तियों से ही साझा करें."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"यह संदेश अगली बार दिखाएं"</string>
+</resources>
diff --git a/packages/Shell/res/values-hr/strings.xml b/packages/Shell/res/values-hr/strings.xml
new file mode 100644
index 0000000..2c4ea23
--- /dev/null
+++ b/packages/Shell/res/values-hr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Ljuska"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Prijava programske pogreške snimljena je"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Dodirnite za dijeljenje prijave programske pogreške"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Prijave programskih pogrešaka sadržavaju podatke iz različitih datoteka zapisnika sustava, uključujući osobne i privatne informacije. Prijave programskih pogrešaka dijelite samo s aplikacijama i osobama koje smatrate pouzdanima."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Prikaži tu poruku sljedeći put"</string>
+</resources>
diff --git a/packages/Shell/res/values-hu/strings.xml b/packages/Shell/res/values-hu/strings.xml
new file mode 100644
index 0000000..8d684da
--- /dev/null
+++ b/packages/Shell/res/values-hu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Héj"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Programhiba-jelentés rögzítve"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Érintse meg a programhiba-jelentés megosztásához"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"A programhiba-jelentések a rendszer különféle naplófájljaiból származó adatokat tartalmaznak, köztük személyes és magánjellegű információkat is. Csak olyan alkalmazásokkal és személyekkel osszon meg programhiba-jelentéseket, amelyekben vagy akikben megbízik."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Üzenet mutatása legközelebb"</string>
+</resources>
diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml
new file mode 100644
index 0000000..8ea2584
--- /dev/null
+++ b/packages/Shell/res/values-in/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Kerangka"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Laporan bug tercatat"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Sentuh untuk membagikan laporan bug Anda"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Laporan bug berisi data dari berbagai file log sistem, termasuk informasi pribadi dan rahasia. Hanya bagikan laporan bug dengan aplikasi dan orang yang Anda percaya."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tampilkan pesan ini lain kali"</string>
+</resources>
diff --git a/packages/Shell/res/values-it/strings.xml b/packages/Shell/res/values-it/strings.xml
new file mode 100644
index 0000000..18a03fe
--- /dev/null
+++ b/packages/Shell/res/values-it/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Segnalazione di bug acquisita"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Tocca per condividere la segnalazione di bug"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Le segnalazioni di bug contengono dati da vari file di log del sistema, incluse informazioni personali e private. Condividi le segnalazioni di bug solo con app e persone attendibili."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra questo messaggio la prossima volta"</string>
+</resources>
diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml
new file mode 100644
index 0000000..e7715e9
--- /dev/null
+++ b/packages/Shell/res/values-iw/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"מעטפת"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"דוח הבאגים צולם"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"גע כדי לשתף את דוח הבאגים שלך"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"דוחות על באגים כוללים נתונים מקובצי היומן השונים במערכת, כולל מידע אישי ופרטי. שתף דוחות באגים רק עם יישומים ואנשים שאתה סומך עליהם."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"הצג את ההודעה הזו בפעם הבאה"</string>
+</resources>
diff --git a/packages/Shell/res/values-ja/strings.xml b/packages/Shell/res/values-ja/strings.xml
new file mode 100644
index 0000000..88b9c14
--- /dev/null
+++ b/packages/Shell/res/values-ja/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"シェル"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"バグレポートが記録されました"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"タップしてバグレポートを共有する"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"バグレポートには、個人の非公開情報など、システムのさまざまなログファイルのデータが含まれます。共有する場合は信頼するアプリとユーザーのみを選択してください。"</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"このメッセージを次回も表示する"</string>
+</resources>
diff --git a/packages/Shell/res/values-ko/strings.xml b/packages/Shell/res/values-ko/strings.xml
new file mode 100644
index 0000000..d22a8b0
--- /dev/null
+++ b/packages/Shell/res/values-ko/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"셸"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"버그 신고서 캡처됨"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"버그 신고서를 공유하려면 터치하세요."</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"버그 신고서는 시스템의 다양한 로그 파일 데이터(예: 개인 및 비공개 정보)를 포함합니다. 신뢰할 수 있는 앱과 사용자에게만 버그 신고서를 공유합니다."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"다음에 이 메시지 표시"</string>
+</resources>
diff --git a/packages/Shell/res/values-lt/strings.xml b/packages/Shell/res/values-lt/strings.xml
new file mode 100644
index 0000000..3ac4820
--- /dev/null
+++ b/packages/Shell/res/values-lt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Apvalkalas"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Trikčių ataskaita užfiksuota"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Palieskite, kad bendrintumėte trikčių ataskaitą"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Trikčių ataskaitose pateikiami duomenys iš įvairių sistemos žurnalo failų, įskaitant asmeninę ir privačią informaciją. Trikčių ataskaitas bendrinkite tik su patikimomis programomis ir žmonėmis."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rodyti šį pranešimą kitą kartą"</string>
+</resources>
diff --git a/packages/Shell/res/values-lv/strings.xml b/packages/Shell/res/values-lv/strings.xml
new file mode 100644
index 0000000..3f7f7c1
--- /dev/null
+++ b/packages/Shell/res/values-lv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Aizsargs"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Izveidots kļūdu pārskats"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Pieskarieties, lai kopīgotu kļūdu pārskatu."</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Kļūdu pārskatā ir iekļauti dati no dažādiem sistēmas žurnālfailiem, tostarp personas dati un privāta informācija. Kļūdu pārskatus ieteicams kopīgot tikai ar uzticamām lietotnēm un lietotājiem."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rādīt šo ziņojumu nākamajā reizē"</string>
+</resources>
diff --git a/packages/Shell/res/values-ms/strings.xml b/packages/Shell/res/values-ms/strings.xml
new file mode 100644
index 0000000..8d1e4a2
--- /dev/null
+++ b/packages/Shell/res/values-ms/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Laporan pepijat telah ditangkap"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Sentuh untuk berkongsi laporan pepijat anda"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Laporan pepijat mengandungi data dari pelbagai fail log sistem, termasuk maklumat peribadi dan sulit. Kongsikan laporan pepijat hanya dengan apl dan orang yang anda percayai."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tunjukkan mesej ini pada masa akan datang"</string>
+</resources>
diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml
new file mode 100644
index 0000000..96d53a6
--- /dev/null
+++ b/packages/Shell/res/values-nb/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Kommandoliste"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Feilrapporten er lagret"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Trykk for å dele feilrapporten din"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Feilrapporter inkluderer data fra systemets forskjellige loggfiler. Dette omfatter personlig og privat informasjon. Du bør bare dele feilrapporter ned apper og folk du stoler på."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne meldingen neste gang"</string>
+</resources>
diff --git a/packages/Shell/res/values-nl/strings.xml b/packages/Shell/res/values-nl/strings.xml
new file mode 100644
index 0000000..5c32c73
--- /dev/null
+++ b/packages/Shell/res/values-nl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Foutenrapport vastgelegd"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Raak aan om uw foutenrapport te delen"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Foutenrapporten bevatten gegevens uit de verschillende logbestanden van het systeem, waaronder persoonlijke en privégegevens. Deel foutenrapporten alleen met apps en mensen die u vertrouwt."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Dit bericht de volgende keer weergeven"</string>
+</resources>
diff --git a/packages/Shell/res/values-pl/strings.xml b/packages/Shell/res/values-pl/strings.xml
new file mode 100644
index 0000000..2e28f8d
--- /dev/null
+++ b/packages/Shell/res/values-pl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Powłoka"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Raport o błędach został zapisany"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Kliknij, by udostępnić raport o błędach"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Raporty o błędach zawierają dane z różnych plików dzienników systemu, w tym dane osobowe i prywatne. Udostępniaj je tylko aplikacjom i osobom, którym ufasz."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaż ten komunikat następnym razem"</string>
+</resources>
diff --git a/packages/Shell/res/values-pt-rPT/strings.xml b/packages/Shell/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..1c465e0
--- /dev/null
+++ b/packages/Shell/res/values-pt-rPT/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Relatório de erros capturado"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Toque para partilhar o relatório de erros"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de erros incluem dados de vários ficheiros de registo do sistema, nomeadamente informações pessoais e privadas. Partilhe relatórios de erros apenas com aplicações e pessoas fidedignas."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
+</resources>
diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml
new file mode 100644
index 0000000..20f4cc9
--- /dev/null
+++ b/packages/Shell/res/values-pt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Relatório de bugs capturado"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Toque para compartilhar seu relatório de bugs"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de bugs contêm dados de diversos arquivos de registro do sistema, inclusive informações pessoais e particulares. Compartilhe relatórios de bugs somente com aplicativos e pessoas nos quais você confia."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
+</resources>
diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml
new file mode 100644
index 0000000..45c2b0a
--- /dev/null
+++ b/packages/Shell/res/values-ro/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Raportul despre erori a fost creat"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Atingeți pentru a permite accesul la raportul despre erori"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Rapoartele despre erori conțin date din diferite fișiere de jurnal ale sistemului, inclusiv informații private și personale. Permiteți accesul la rapoartele despre erori numai aplicațiilor și persoanelor în care aveți încredere."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afișați acest mesaj data viitoare"</string>
+</resources>
diff --git a/packages/Shell/res/values-ru/strings.xml b/packages/Shell/res/values-ru/strings.xml
new file mode 100644
index 0000000..153e972
--- /dev/null
+++ b/packages/Shell/res/values-ru/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Оболочка"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Отчет об ошибках сохранен"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Нажмите, чтобы отправить отчет об ошибках"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Отчеты об ошибках содержат данные различных системных журналов и могут включать личную информацию. Рекомендуем открывать к ним доступ только лицам и приложениям, заслуживающим доверие."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показать это сообщение в следующий раз"</string>
+</resources>
diff --git a/packages/Shell/res/values-sk/strings.xml b/packages/Shell/res/values-sk/strings.xml
new file mode 100644
index 0000000..99f36f9
--- /dev/null
+++ b/packages/Shell/res/values-sk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Prostredie"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Správa o chybách sa zaznamenala"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Dotykom môžete zdieľať správu o chybách"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Správy o chybách obsahujú údaje z rôznych súborov denníkov systému vrátane osobných a súkromných informácií. Zdieľajte ich iba s dôveryhodnými aplikáciami a ľuďmi."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobraziť túto správu nabudúce"</string>
+</resources>
diff --git a/packages/Shell/res/values-sl/strings.xml b/packages/Shell/res/values-sl/strings.xml
new file mode 100644
index 0000000..8522d1b
--- /dev/null
+++ b/packages/Shell/res/values-sl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Lupina"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Poročilo o napaki je posneto"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Dotaknite se, če želite deliti sporočilo o napaki z drugimi"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Poročila o napakah vsebujejo podatke iz različnih dnevniških datotek sistema, vključno z osebnimi in zasebnimi podatki. Poročila o napakah delite samo z aplikacijami in ljudmi, ki jim zaupate."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaži to sporočilo naslednjič"</string>
+</resources>
diff --git a/packages/Shell/res/values-sr/strings.xml b/packages/Shell/res/values-sr/strings.xml
new file mode 100644
index 0000000..bef6ff4
--- /dev/null
+++ b/packages/Shell/res/values-sr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Извештај о грешци је снимљен"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Додирните да бисте делили извештај о грешци"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Извештаји о грешкама садрже податке из различитих системских датотека евиденције, укључујући личне и приватне податке. Делите извештаје о грешкама само са апликацијама и људима у које имате поверења."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Прикажи ову поруку следећи пут"</string>
+</resources>
diff --git a/packages/Shell/res/values-sv/strings.xml b/packages/Shell/res/values-sv/strings.xml
new file mode 100644
index 0000000..055dc41
--- /dev/null
+++ b/packages/Shell/res/values-sv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Skal"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Felrapporten har skapats"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Tryck om du vill dela felrapporten"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Felrapporter innehåller data från systemets olika loggfiler, inklusive personliga och privata uppgifter. Dela bara felrapporter med personer du litar på."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Visa det här meddelandet nästa gång"</string>
+</resources>
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
new file mode 100644
index 0000000..b1d4407
--- /dev/null
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Ganda"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Ripoti ya hitilafu imenaswa"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Gusa ili ushiriki ripoti yako ya hitilafu"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Ripoti ya hitilafu ina data kutoka kwenye faili za kumbukumbu mbalimbali za mfumo, pamoja na maelezo ya kibinafsi na faragha. Shiriki ripoti ya hitilafu na programu na watu unaowaamini pekee."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Onyesha ujumbe huu wakati mwingine"</string>
+</resources>
diff --git a/packages/Shell/res/values-th/strings.xml b/packages/Shell/res/values-th/strings.xml
new file mode 100644
index 0000000..b484a42
--- /dev/null
+++ b/packages/Shell/res/values-th/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"จับภาพรายงานข้อบกพร่องแล้ว"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"แตะเพื่อแชร์รายงานข้อบกพร่องของคุณ"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"รายงานข้อบกพร่องมีข้อมูลจากไฟล์บันทึกต่างๆ ของระบบ รวมถึงข้อมูลส่วนตัว แชร์รายงานข้อบกพร่องกับแอปและบุคคลที่คุณไว้ใจเท่านั้น"</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"แสดงข้อความนี้ในครั้งต่อไป"</string>
+</resources>
diff --git a/packages/Shell/res/values-tl/strings.xml b/packages/Shell/res/values-tl/strings.xml
new file mode 100644
index 0000000..20d1b09
--- /dev/null
+++ b/packages/Shell/res/values-tl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Na-capture ang ulat ng bug"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Pindutin upang ibahagi ang iyong ulat ng bug"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Naglalaman ang mga ulat ng bug ng data mula sa iba\'t ibang file ng log ng system, kabilang ang personal at pribadong impormasyon. Magbahagi lang ng mga ulat ng bug sa apps at mga tao na pinagkakatiwalaan mo."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ipakita ang mensaheng ito sa susunod"</string>
+</resources>
diff --git a/packages/Shell/res/values-tr/strings.xml b/packages/Shell/res/values-tr/strings.xml
new file mode 100644
index 0000000..56db3fc
--- /dev/null
+++ b/packages/Shell/res/values-tr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Kabuk"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Hata raporu kaydedildi"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Hata raporunuzu paylaşmak için dokunun"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Hata raporları, kişisel ve özel bilgiler dahil olmak üzere sistemin çeşitli günlük dosyalarından veriler içerir. Hata raporlarını sadece güvendiğiniz uygulamalar ve kişilerle paylaşın."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bir dahaki sefere bu mesajı göster"</string>
+</resources>
diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml
new file mode 100644
index 0000000..68e68a8
--- /dev/null
+++ b/packages/Shell/res/values-uk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Оболонка"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Звіт про помилки створено"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Торкніться, щоб надіслати звіт про помилки"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Звіти про помилки містять дані з різних файлів журналу системи, зокрема особисті та конфіденційні. Надсилайте звіт про помилки лише тим, кому довіряєте."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показати це повідомлення наступного разу"</string>
+</resources>
diff --git a/packages/Shell/res/values-vi/strings.xml b/packages/Shell/res/values-vi/strings.xml
new file mode 100644
index 0000000..ca4fcaa
--- /dev/null
+++ b/packages/Shell/res/values-vi/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Báo cáo lỗi đã được chụp"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Chạm để chia sẻ báo cáo lỗi của bạn"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Các báo cáo lỗi chứa dữ liệu từ nhiều tệp nhật ký khác nhau của hệ thống, bao gồm cả thông tin cá nhân và riêng tư. Chỉ chia sẻ báo cáo lỗi với các ứng dụng và những người mà bạn tin tưởng."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Hiển thị thông báo này vào lần tới"</string>
+</resources>
diff --git a/packages/Shell/res/values-zh-rCN/strings.xml b/packages/Shell/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..f1c385f
--- /dev/null
+++ b/packages/Shell/res/values-zh-rCN/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"已抓取错误报告"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"触摸即可分享您的错误报告"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"错误报告包含的数据来自于系统的各个日志文件,其中包含个人信息和隐私信息。请务必只与您信任的应用和用户分享错误报告。"</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再显示这条讯息"</string>
+</resources>
diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..d3d3140
--- /dev/null
+++ b/packages/Shell/res/values-zh-rTW/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"殼層"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"已擷取錯誤報告"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告的資料來自系統各個紀錄檔,包括個人和私密資訊。請務必只與您信任的應用程式和使用者分享錯誤報告。"</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次仍顯示這則訊息"</string>
+</resources>
diff --git a/packages/Shell/res/values-zu/strings.xml b/packages/Shell/res/values-zu/strings.xml
new file mode 100644
index 0000000..e524b80
--- /dev/null
+++ b/packages/Shell/res/values-zu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"I-Shell"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Umbiko wesiphazamisi uthwetshuliwe"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Thinta ukuze wabelane ngombiko wakho wesiphazamisi"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Imibiko yeziphazamisi iqukethe idatha yamafayela wokungena ahlukile wesistimu, afaka ulwazi lomuntu siqu noma lobumfihlo. Yabelana kuphela ngemibiko yeziphazamisi nezinhlelo zokusebenza nabantu obathembayo."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bonisa lo mlayezo ngesikhathi esilandelayo"</string>
+</resources>
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 015c0cc..f8f064a 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -6,10 +6,11 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
src/com/android/systemui/EventLogTags.logtags
-LOCAL_JAVA_LIBRARIES := services telephony-common
+LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 66080f3..dfc68f4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.systemui"
+ android:sharedUserId="android.uid.systemui"
coreApp="true">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
@@ -37,7 +38,6 @@
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
- <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.MASTER_CLEAR" />
<uses-permission android:name="android.permission.VIBRATE" />
@@ -71,6 +71,7 @@
android:hardwareAccelerated="true"
android:label="@string/app_label"
android:icon="@*android:drawable/platlogo"
+ android:process="com.android.systemui"
android:supportsRtl="true">
<!-- Broadcast receiver that gets the broadcast at boot time and starts
diff --git a/packages/SystemUI/assets/fonts/AndroidClock.ttf b/packages/SystemUI/assets/fonts/AndroidClock.ttf
deleted file mode 100644
index 7b550ee..0000000
--- a/packages/SystemUI/assets/fonts/AndroidClock.ttf
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/assets/fonts/AndroidClock2.ttf b/packages/SystemUI/assets/fonts/AndroidClock2.ttf
deleted file mode 100644
index a95d548..0000000
--- a/packages/SystemUI/assets/fonts/AndroidClock2.ttf
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index c886eea..ab45d99 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -1,10 +1,3 @@
--keep class com.android.systemui.statusbar.tablet.TabletStatusBarService {
- public void notificationIconsClicked(android.view.View);
- public void systemInfoClicked(android.view.View);
- public void recentButtonClicked(android.view.View);
- public void toggleLightsOut(android.view.View);
-}
-
-keep class com.android.systemui.statusbar.policy.KeyButtonView {
public float getDrawingAlpha();
public float getGlowAlpha();
diff --git a/packages/SystemUI/res/anim/priority_alert_enter.xml b/packages/SystemUI/res/anim/heads_up_enter.xml
similarity index 100%
rename from packages/SystemUI/res/anim/priority_alert_enter.xml
rename to packages/SystemUI/res/anim/heads_up_enter.xml
diff --git a/packages/SystemUI/res/anim/priority_alert_exit.xml b/packages/SystemUI/res/anim/heads_up_exit.xml
similarity index 100%
rename from packages/SystemUI/res/anim/priority_alert_exit.xml
rename to packages/SystemUI/res/anim/heads_up_exit.xml
diff --git a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml b/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml
index 121daae..73ae9f2 100644
--- a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml
@@ -18,6 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:detachWallpaper="true"
android:shareInterpolator="false"
android:zAdjustment="normal">
<!--scale android:fromXScale="2.0" android:toXScale="1.0"
diff --git a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml b/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml
index fa28cf4..7e257d9 100644
--- a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml
@@ -18,6 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:detachWallpaper="true"
android:shareInterpolator="false"
android:zAdjustment="top">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
diff --git a/packages/SystemUI/res/drawable/heads_up_notification_row_bg.xml b/packages/SystemUI/res/drawable/heads_up_notification_row_bg.xml
new file mode 100644
index 0000000..59d9fcf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/heads_up_notification_row_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+ <item android:state_pressed="true"
+ android:drawable="@drawable/heads_up_notification_bg_pressed" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/intruder_window_bg.9.png b/packages/SystemUI/res/drawable/heads_up_window_bg.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable/intruder_window_bg.9.png
rename to packages/SystemUI/res/drawable/heads_up_window_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/intruder_row_bg.xml b/packages/SystemUI/res/drawable/intruder_row_bg.xml
deleted file mode 100644
index 1c7c9c4..0000000
--- a/packages/SystemUI/res/drawable/intruder_row_bg.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- android:exitFadeDuration="@android:integer/config_mediumAnimTime">
- <item android:state_pressed="true" android:drawable="@drawable/intruder_bg_pressed" />
-</selector>
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
new file mode 100644
index 0000000..b7c1666
--- /dev/null
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -0,0 +1,34 @@
+<?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.
+*/
+-->
+
+<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<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:orientation="vertical"
+ >
+ <FrameLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/contentHolder"
+ android:background="@drawable/heads_up_window_bg"
+ />
+</com.android.systemui.statusbar.policy.HeadsUpNotificationView>
diff --git a/packages/SystemUI/res/layout/intruder_alert.xml b/packages/SystemUI/res/layout/intruder_alert.xml
deleted file mode 100644
index c4141ae..0000000
--- a/packages/SystemUI/res/layout/intruder_alert.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?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.
-*/
--->
-
-<!-- android:background="@drawable/status_bar_closed_default_background" -->
-<com.android.systemui.statusbar.policy.IntruderAlertView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:orientation="vertical"
- >
- <FrameLayout
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:id="@+id/contentHolder"
- android:background="@drawable/intruder_window_bg"
- />
-<!-- <ImageView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:src="@drawable/title_bar_shadow"
- android:scaleType="fitXY"
- /> -->
-</com.android.systemui.statusbar.policy.IntruderAlertView>
diff --git a/packages/SystemUI/res/layout/status_bar_icon.xml b/packages/SystemUI/res/layout/status_bar_icon.xml
deleted file mode 100644
index 063212e..0000000
--- a/packages/SystemUI/res/layout/status_bar_icon.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?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.
-*/
--->
-
-<!-- The icons are a fixed size so an app can't mess everything up with bogus images -->
-<!-- TODO: the icons are hard coded to 25x25 pixels. Their size should come from a theme -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="25dp"
- android:layout_height="25dp"
- >
-
- <com.android.systemui.statusbar.AnimatedImageView android:id="@+id/image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
-
- <TextView android:id="@+id/number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|bottom"
- android:layout_marginEnd="1dp"
- android:layout_marginBottom="1dp"
- android:textSize="10sp"
- android:textColor="#ffffffff"
- android:background="@drawable/ic_notification_overlay"
- android:gravity="center"
- android:textStyle="bold"
- />
-
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/system_bar.xml b/packages/SystemUI/res/layout/system_bar.xml
deleted file mode 100644
index 28c9dc0..0000000
--- a/packages/SystemUI/res/layout/system_bar.xml
+++ /dev/null
@@ -1,151 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
--->
-
-<!-- TabletStatusBarView extends FrameLayout -->
-<com.android.systemui.statusbar.tablet.TabletStatusBarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
- android:background="@drawable/system_bar_background"
- >
-
- <FrameLayout
- android:id="@+id/bar_contents_holder"
- android:layout_width="match_parent"
- android:layout_height="@*android:dimen/system_bar_height"
- android:layout_gravity="bottom"
- >
- <RelativeLayout
- android:id="@+id/bar_contents"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- >
-
- <!-- notification icons & panel access -->
- <include layout="@layout/system_bar_notification_area"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:layout_marginTop="1dp"
- />
-
- <!-- navigation controls -->
- <LinearLayout
- android:id="@+id/navigationArea"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentStart="true"
- android:orientation="horizontal"
- android:clipChildren="false"
- android:clipToPadding="false"
- >
- <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
- android:layout_width="@dimen/navigation_key_width"
- android:layout_height="match_parent"
- android:src="@drawable/ic_sysbar_back"
- systemui:keyCode="4"
- android:contentDescription="@string/accessibility_back"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
- />
- <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
- android:layout_width="@dimen/navigation_key_width"
- android:layout_height="match_parent"
- android:src="@drawable/ic_sysbar_home"
- systemui:keyCode="3"
- android:contentDescription="@string/accessibility_home"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
- />
- <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
- android:layout_width="@dimen/navigation_key_width"
- android:layout_height="match_parent"
- android:src="@drawable/ic_sysbar_recent"
- android:contentDescription="@string/accessibility_recent"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
- />
- <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
- android:layout_width="@dimen/navigation_menu_key_width"
- android:layout_height="match_parent"
- android:src="@drawable/ic_sysbar_menu"
- systemui:keyCode="82"
- android:visibility="invisible"
- android:contentDescription="@string/accessibility_menu"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
- />
- </LinearLayout>
-
- <!-- fake space bar zone -->
- <com.android.systemui.statusbar.policy.EventHole android:id="@+id/fake_space_bar"
- android:layout_height="match_parent"
- android:layout_width="0dp"
- android:paddingStart="8dip"
- android:paddingEnd="8dip"
- android:layout_toEndOf="@+id/navigationArea"
- android:layout_toStartOf="@+id/notificationArea"
- android:visibility="gone"
- />
- </RelativeLayout>
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/bar_shadow_holder"
- android:layout_width="match_parent"
- android:layout_height="@*android:dimen/system_bar_height"
- android:layout_gravity="bottom"
- >
- <!-- lights out shade -->
- <RelativeLayout
- android:id="@+id/bar_shadow"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#FF000000"
- android:visibility="gone"
- >
- <ImageView
- android:id="@+id/dot0"
- android:layout_width="80dip"
- android:layout_height="48dip"
- android:src="@drawable/ic_sysbar_lights_out_dot_small"
- android:layout_alignParentStart="true"
- android:layout_alignParentBottom="true"
- />
- <ImageView
- android:id="@+id/dot1"
- android:layout_width="80dip"
- android:layout_height="48dip"
- android:src="@drawable/ic_sysbar_lights_out_dot_large"
- android:layout_toEndOf="@+id/dot0"
- android:layout_alignParentBottom="true"
- />
- <ImageView
- android:id="@+id/dot2"
- android:layout_width="80dip"
- android:layout_height="48dip"
- android:src="@drawable/ic_sysbar_lights_out_dot_small"
- android:layout_toEndOf="@+id/dot1"
- android:layout_alignParentBottom="true"
- />
- <ImageView
- android:id="@+id/dot3"
- android:layout_width="80dip"
- android:layout_height="48dip"
- android:src="@drawable/ic_sysbar_lights_out_dot_small"
- android:layout_alignParentEnd="true"
- android:layout_alignParentBottom="true"
- />
- </RelativeLayout>
- </FrameLayout>
-</com.android.systemui.statusbar.tablet.TabletStatusBarView>
diff --git a/packages/SystemUI/res/layout/system_bar_compat_mode_panel.xml b/packages/SystemUI/res/layout/system_bar_compat_mode_panel.xml
deleted file mode 100644
index 9ad9e05..0000000
--- a/packages/SystemUI/res/layout/system_bar_compat_mode_panel.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.systemui.statusbar.tablet.CompatModePanel
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:paddingBottom="@dimen/panel_float"
- android:paddingEnd="20dp"
- >
- <RadioGroup android:id="@+id/compat_mode_radio_group"
- android:background="@*android:drawable/dialog_full_holo_dark"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:padding="10dp"
- >
- <RadioButton android:id="@+id/compat_mode_off_radio"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/compat_mode_off" />
- <RadioButton android:id="@+id/compat_mode_on_radio"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/compat_mode_on" />
- </RadioGroup>
-</com.android.systemui.statusbar.tablet.CompatModePanel>
diff --git a/packages/SystemUI/res/layout/system_bar_input_methods_item.xml b/packages/SystemUI/res/layout/system_bar_input_methods_item.xml
deleted file mode 100644
index 1a95ec1..0000000
--- a/packages/SystemUI/res/layout/system_bar_input_methods_item.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:background="@drawable/status_bar_item_background"
- android:orientation="vertical"
- android:paddingEnd="6dip"
- android:paddingStart="6dip"
- android:paddingTop="5dip"
- android:paddingBottom="5dip"
- android:gravity="center_vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="center_vertical"
- android:orientation="horizontal">
- <LinearLayout
- android:id="@+id/item_subtype"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:background="?android:attr/selectableItemBackground">
- <RadioButton
- android:id="@+id/item_radio"
- android:layout_width="30dip"
- android:layout_height="wrap_content"
- android:focusable="false"
- android:clickable="false" />
- <ImageView
- android:id="@+id/item_icon"
- android:layout_width="@android:dimen/app_icon_size"
- android:layout_height="wrap_content"
- android:scaleType="fitCenter"
- android:contentDescription="@null" />
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="0px"
- android:layout_weight="1"
- android:layout_height="wrap_content">
- <TextView
- android:id="@+id/item_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:layout_marginBottom="2dip" />
- <TextView
- android:id="@+id/item_subtitle"
- android:layout_marginTop="-4dip"
- android:layout_gravity="center_vertical|start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
- </LinearLayout>
- <View
- android:id="@+id/item_vertical_separator"
- android:layout_width="2dip"
- android:layout_height="match_parent"
- android:layout_marginBottom="5dip"
- android:background="@android:drawable/divider_horizontal_dark" />
- <ImageView
- android:id="@+id/item_settings_icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="5dip"
- android:layout_gravity="center_vertical"
- android:paddingEnd="10dip"
- android:paddingStart="10dip"
- android:src="@drawable/ic_sysbar_quicksettings"
- android:visibility="visible"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@string/accessibility_settings_button" />
- </LinearLayout>
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="@android:drawable/divider_horizontal_dark" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/system_bar_input_methods_panel.xml b/packages/SystemUI/res/layout/system_bar_input_methods_panel.xml
deleted file mode 100644
index 547f937..0000000
--- a/packages/SystemUI/res/layout/system_bar_input_methods_panel.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.systemui.statusbar.tablet.InputMethodsPanel
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:paddingBottom="7dip"
- android:orientation="vertical"
- android:visibility="gone">
- <View
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1" />
- <FrameLayout
- android:id="@+id/glow"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/recents_blue_glow">
- <LinearLayout
- android:layout_width="450dip"
- android:layout_height="wrap_content"
- android:layout_marginStart="20dip"
- android:orientation="vertical"
- android:background="@drawable/notify_panel_clock_bg">
- <!-- Hard keyboard switch -->
- <LinearLayout
- android:id="@+id/hard_keyboard_section"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:orientation="vertical">
- <LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:orientation="horizontal">
- <TextView
- android:id="@+id/use_physical_keyboard_label"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:background="?android:attr/selectableItemBackground"
- android:orientation="vertical"
- android:paddingEnd="6dip"
- android:paddingStart="30dip"
- android:paddingTop="5dip"
- android:paddingBottom="5dip"
- android:gravity="center_vertical"
- android:singleLine="true"
- android:text="@string/status_bar_use_physical_keyboard"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:ellipsize="marquee" />
- <Switch
- android:id="@+id/hard_keyboard_switch"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="16dip" />
- </LinearLayout>
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="@android:drawable/divider_horizontal_dark" />
- </LinearLayout>
-
- <!-- Input method list -->
- <ScrollView
- android:layout_width="wrap_content"
- android:layout_height="0dip"
- android:overScrollMode="ifContentScrolls"
- android:layout_marginTop="3dip"
- android:layout_weight="1"
- android:scrollbarAlwaysDrawVerticalTrack="true"
- android:scrollbarDefaultDelayBeforeFade="75000">
- <LinearLayout
- android:id="@+id/input_method_menu_list"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical" />
- </ScrollView>
-
- <!-- Configure input methods -->
- <TextView
- android:id="@+id/ime_settings_shortcut"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:background="?android:attr/selectableItemBackground"
- android:orientation="vertical"
- android:paddingEnd="6dip"
- android:paddingStart="30dip"
- android:paddingTop="5dip"
- android:paddingBottom="5dip"
- android:gravity="center_vertical"
- android:singleLine="true"
- android:text="@string/status_bar_input_method_settings_configure_input_methods"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:ellipsize="marquee" />
- </LinearLayout>
- </FrameLayout>
-</com.android.systemui.statusbar.tablet.InputMethodsPanel>
diff --git a/packages/SystemUI/res/layout/system_bar_no_recent_apps.xml b/packages/SystemUI/res/layout/system_bar_no_recent_apps.xml
deleted file mode 100644
index c023ef7..0000000
--- a/packages/SystemUI/res/layout/system_bar_no_recent_apps.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="20dp"
- android:textColor="@android:color/holo_blue_light"
- android:text="@string/status_bar_no_recent_apps"
- android:gravity="start"
- android:layout_gravity="bottom|start"
- />
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/system_bar_notification_area.xml b/packages/SystemUI/res/layout/system_bar_notification_area.xml
deleted file mode 100644
index 2fd91ef..0000000
--- a/packages/SystemUI/res/layout/system_bar_notification_area.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
--->
-
-<!-- notification icons & panel access -->
-<com.android.systemui.statusbar.tablet.NotificationArea
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
- android:id="@+id/notificationArea"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:orientation="horizontal"
- android:background="?android:attr/listChoiceBackgroundIndicator"
- android:clickable="true"
- >
-
- <LinearLayout
- android:id="@+id/feedbackIconArea"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- >
-
- <com.android.systemui.statusbar.tablet.InputMethodButton
- android:id="@+id/imeSwitchButton"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="8dip"
- android:src="@drawable/ic_sysbar_ime_default"
- android:visibility="gone"
- android:contentDescription="@string/accessibility_ime_switch_button"
- />
-
- <com.android.systemui.statusbar.policy.CompatModeButton
- android:id="@+id/compatModeButton"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="8dip"
- android:src="@drawable/ic_sysbar_zoom"
- android:visibility="gone"
- android:contentDescription="@string/accessibility_compatibility_zoom_button"
- />
-
- <com.android.systemui.statusbar.tablet.NotificationIconArea
- android:id="@+id/notificationIcons"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- >
- <view
- class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
- android:id="@+id/icons"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="8dp"
- android:alpha="0.4"
- />
- </com.android.systemui.statusbar.tablet.NotificationIconArea>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/notificationTrigger"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center"
- >
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="6dip"
- android:layout_marginEnd="8dip"
- android:gravity="center_vertical|start"
- />
-
- <TextView
- android:id="@+id/network_text"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginEnd="6dip"
- android:layout_marginStart="6dip"
- android:gravity="center"
- android:singleLine="true"
- android:visibility="gone"
- android:textSize="16sp"
- android:textColor="#606060"
- />
-
- <LinearLayout
- android:id="@+id/signal_battery_cluster"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginEnd="16dp"
- android:orientation="horizontal"
- android:gravity="center"
- >
- <include layout="@layout/signal_cluster_view"
- android:id="@+id/signal_cluster"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- <ImageView
- android:id="@+id/bluetooth"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingStart="4dip"
- android:visibility="gone"
- />
- <ImageView
- android:id="@+id/battery"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingStart="4dip"
- />
- </LinearLayout>
- </LinearLayout>
-</com.android.systemui.statusbar.tablet.NotificationArea>
diff --git a/packages/SystemUI/res/layout/system_bar_notification_panel.xml b/packages/SystemUI/res/layout/system_bar_notification_panel.xml
deleted file mode 100644
index 58a6de3..0000000
--- a/packages/SystemUI/res/layout/system_bar_notification_panel.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<!--
- Copyright (C) 2006 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- android:background="@drawable/system_bar_closed_default_background" -->
-<com.android.systemui.statusbar.tablet.NotificationPanel
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
- android:id="@+id/content_parent"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:gravity="end"
- >
-
- <!-- lift the panel up off the status bar while leaving a touchable are -->
- <Space
- android:id="@+id/system_bar_notification_panel_bottom_space"
- android:layout_height="56dp"
- android:layout_width="478dp"
- android:layout_alignParentEnd="true"
- android:layout_alignParentBottom="true"
- />
-
- <LinearLayout
- android:id="@+id/content_frame"
- android:background="@drawable/notification_panel_bg"
- android:layout_height="wrap_content"
- android:layout_width="478dp"
- android:orientation="vertical"
- android:layout_alignParentEnd="true"
- android:layout_above="@id/system_bar_notification_panel_bottom_space"
- android:paddingBottom="8dp"
- >
-
- <include layout="@layout/system_bar_notification_panel_title"
- android:layout_width="match_parent"
- android:layout_height="130dp"
- android:layout_above="@id/content_frame"
- android:layout_alignParentEnd="true"
- android:layout_weight="0"
- />
-
- <ScrollView
-
- android:id="@+id/notification_scroller"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:overScrollMode="ifContentScrolls"
- android:layout_weight="1"
- >
- <com.android.systemui.statusbar.policy.NotificationRowLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal|bottom"
- android:clickable="true"
- android:focusable="true"
- android:descendantFocusability="afterDescendants"
- systemui:rowHeight="@dimen/notification_row_min_height"
- />
- </ScrollView>
- </LinearLayout>
-</com.android.systemui.statusbar.tablet.NotificationPanel>
diff --git a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml b/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
deleted file mode 100644
index d08fbce..0000000
--- a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
+++ /dev/null
@@ -1,231 +0,0 @@
-<!--
- Copyright (C) 2006 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.systemui.statusbar.tablet.NotificationPanelTitle
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
- android:id="@+id/title_area"
- android:background="@drawable/system_bar_notification_header_bg"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="true"
- android:orientation="vertical"
- android:paddingStart="26dp"
- android:paddingTop="14dp"
- android:paddingEnd="26dp"
- >
-
- <TableLayout
- android:id="@+id/icons"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_alignParentBottom="true"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="16dp"
- android:shrinkColumns="2,4"
- android:stretchColumns="7"
- >
- <TableRow>
-
- <!-- to keep the column ids stable we wrap disappearing views in
- frames -->
- <FrameLayout
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- >
- <ImageView
- android:id="@+id/bluetooth"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingEnd="16dp"
- android:visibility="gone"
- android:contentDescription="@null"
- android:layout_gravity="center_vertical"
- />
- </FrameLayout>
-
- <!-- mobile data -->
- <FrameLayout
- android:id="@+id/mobile_icon"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingEnd="6dp"
- >
-
- <ImageView
- android:id="@+id/mobile_signal"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:contentDescription="@null"
- />
-
- <ImageView
- android:id="@+id/mobile_type"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:contentDescription="@null"
- />
-
- </FrameLayout>
- <TextView
- android:id="@+id/mobile_text"
- style="@style/SystemBarNotificationText"
- android:layout_gravity="start|center_vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingEnd="12dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:text="@string/status_bar_settings_settings_button"
- />
-
- <!-- wifi -->
- <FrameLayout
- android:id="@+id/wifi_icon"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingEnd="6dp"
- >
-
- <ImageView
- android:id="@+id/wifi_signal"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:contentDescription="@null"
- />
-
- <ImageView
- android:id="@+id/wifi_direction"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:contentDescription="@null"
- />
-
- </FrameLayout>
- <TextView
- android:id="@+id/wifi_text"
- style="@style/SystemBarNotificationText"
- android:layout_gravity="start|center_vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingEnd="12dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:text="@string/status_bar_settings_settings_button"
- />
-
- <ImageView
- android:id="@+id/battery"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:scaleType="centerInside"
- android:layout_gravity="center_vertical"
- android:layout_alignBaseline="@id/wifi_signal"
- android:paddingEnd="6dp"
- android:contentDescription="@null"
- />
-
- <TextView
- android:id="@+id/battery_text"
- style="@style/SystemBarNotificationText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:paddingEnd="2dp"
- android:singleLine="true"
- android:text="@string/status_bar_settings_settings_button"
- />
-
- <!-- this will stretch to eat up available space -->
- <View
- android:layout_width="0dp"
- android:layout_height="0dp"
- />
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- >
-
- <ImageView
- android:id="@+id/settings_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_sysbar_quicksettings"
- android:contentDescription="@string/accessibility_desc_quick_settings"
- />
-
- <ImageView
- android:id="@+id/notification_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_notification_open"
- android:visibility="invisible"
- android:contentDescription="@string/accessibility_notifications_button"
- />
- </FrameLayout>
-
- </TableRow>
- </TableLayout>
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/notification_panel_header_padding_top"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:baselineAligned="false"
- >
-
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.SystemBar.Expanded.Clock"
- />
-
- <com.android.systemui.statusbar.policy.DateView
- android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:textAppearance="@style/TextAppearance.SystemBar.Expanded.Date"
- />
-
- <Space
- android:layout_width="0dp"
- android:layout_height="48dp"
- android:layout_weight="1"
- />
-
- <ImageView android:id="@+id/clear_all_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scaleType="center"
- android:src="@drawable/ic_notify_clear"
- android:contentDescription="@string/accessibility_clear_all"
- />
- </LinearLayout>
-</com.android.systemui.statusbar.tablet.NotificationPanelTitle>
diff --git a/packages/SystemUI/res/layout/system_bar_notification_peek.xml b/packages/SystemUI/res/layout/system_bar_notification_peek.xml
deleted file mode 100644
index 3cff47b..0000000
--- a/packages/SystemUI/res/layout/system_bar_notification_peek.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?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.
-*/
--->
-
-<!-- android:background="@drawable/system_bar_closed_default_background" -->
-<com.android.systemui.statusbar.tablet.NotificationPeekPanel
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:background="@*android:drawable/dialog_full_holo_dark"
- android:orientation="vertical"
- >
-
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal|bottom"
- android:animationCache="false"
- android:orientation="vertical"
- android:background="@drawable/system_bar_background"
- android:clickable="true"
- android:focusable="true"
- android:descendantFocusability="afterDescendants"
- >
- </FrameLayout>
-</com.android.systemui.statusbar.tablet.NotificationPeekPanel>
diff --git a/packages/SystemUI/res/layout/system_bar_pocket_panel.xml b/packages/SystemUI/res/layout/system_bar_pocket_panel.xml
deleted file mode 100644
index e4a6da4..0000000
--- a/packages/SystemUI/res/layout/system_bar_pocket_panel.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:background="@*android:drawable/dialog_full_holo_dark"
- >
- <TextView
- android:id="@+id/description"
- android:textAppearance="@android:style/TextAppearance.Small"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textStyle="bold"
- android:maxLines="1"
- android:layout_alignParentBottom="true"
- />
-
- <FrameLayout
- android:id="@+id/preview"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:layout_above="@+id/description"
- android:descendantFocusability="blocksDescendants"
- >
- <ImageView
- android:id="@+id/icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:visibility="gone"
- />
- <TextView
- android:id="@+id/alt"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:textAppearance="@android:style/TextAppearance.Large"
- android:gravity="center"
- android:visibility="gone"
- />
- </FrameLayout>
-</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/system_bar_recent_item.xml b/packages/SystemUI/res/layout/system_bar_recent_item.xml
deleted file mode 100644
index 34f60b2..0000000
--- a/packages/SystemUI/res/layout/system_bar_recent_item.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<?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.
-*/
--->
-
-<RelativeLayout android:id="@+id/recent_item"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content">
-
- <TextView android:id="@+id/app_label"
- android:layout_width="@dimen/status_bar_recents_app_label_width"
- android:layout_height="wrap_content"
- android:textSize="@dimen/status_bar_recents_app_label_text_size"
- android:fadingEdge="horizontal"
- android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
- android:scrollHorizontally="true"
- android:layout_alignParentStart="true"
- android:layout_alignParentTop="true"
- android:layout_marginStart="@dimen/status_bar_recents_app_label_left_margin"
- android:layout_marginTop="32dip"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textColor="@color/status_bar_recents_app_label_color"
- android:importantForAccessibility="no"
- />
-
- <FrameLayout android:id="@+id/app_thumbnail"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toEndOf="@id/app_label"
- android:layout_marginStart="@dimen/status_bar_recents_thumbnail_left_margin"
- android:scaleType="center"
- android:background="@drawable/recents_thumbnail_bg"
- android:foreground="@drawable/recents_thumbnail_fg"
- android:visibility="invisible">
- <ImageView android:id="@+id/app_thumbnail_image"
- android:layout_width="@dimen/status_bar_recents_thumbnail_width"
- android:layout_height="@dimen/status_bar_recents_thumbnail_height"
- />
- </FrameLayout>
-
-
- <ImageView android:id="@+id/app_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toEndOf="@id/app_label"
- android:layout_marginStart="@dimen/status_bar_recents_app_icon_left_margin"
- android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
- android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
- android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
- android:scaleType="centerInside"
- android:adjustViewBounds="true"
- android:visibility="invisible"
- />
-
-
- <View android:id="@+id/recents_callout_line"
- android:layout_width="@dimen/status_bar_recents_app_label_width"
- android:layout_height="1dip"
- android:layout_below="@id/app_label"
- android:layout_marginTop="3dip"
- android:layout_alignParentStart="true"
- android:layout_marginStart="@dimen/status_bar_recents_app_label_left_margin"
- android:layout_toStartOf="@id/app_thumbnail"
- android:layout_marginEnd="3dip"
- android:background="@drawable/recents_callout_line"
- />
-
- <TextView android:id="@+id/app_description"
- android:layout_width="@dimen/status_bar_recents_app_label_width"
- android:layout_height="wrap_content"
- android:textSize="@dimen/status_bar_recents_app_description_text_size"
- android:fadingEdge="horizontal"
- android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
- android:scrollHorizontally="true"
- android:layout_alignParentStart="true"
- android:layout_below="@id/recents_callout_line"
- android:layout_marginStart="@dimen/status_bar_recents_app_label_left_margin"
- android:layout_marginTop="3dip"
- android:singleLine="true"
- android:ellipsize="marquee"
- />
-
-</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/system_bar_recent_panel.xml b/packages/SystemUI/res/layout/system_bar_recent_panel.xml
deleted file mode 100644
index 3d15d9b..0000000
--- a/packages/SystemUI/res/layout/system_bar_recent_panel.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.systemui.recent.RecentsPanelView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
- android:id="@+id/recents_root"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:clipToPadding="false"
- android:clipChildren="false"
- systemui:recentItemLayout="@layout/system_bar_recent_item"
- >
- <FrameLayout
- android:id="@+id/recents_bg_protect"
- android:background="@drawable/recents_bg_protect_tile"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="@*android:dimen/system_bar_height"
- android:clipToPadding="false"
- android:clipChildren="false">
-
- <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/status_bar_recents_right_glow_margin"
- android:divider="@null"
- android:stackFromBottom="true"
- android:fadingEdge="vertical"
- android:scrollbars="none"
- android:fadingEdgeLength="20dip"
- android:layout_gravity="bottom|start"
- android:clipToPadding="false"
- android:clipChildren="false"
- android:fitsSystemWindows="true">
-
- <LinearLayout android:id="@+id/recents_linear_layout"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:clipToPadding="false"
- android:clipChildren="false">
- </LinearLayout>
-
- </com.android.systemui.recent.RecentsVerticalScrollView>
-
- <include layout="@layout/system_bar_no_recent_apps"
- android:id="@+id/recents_no_apps"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginStart="58dip"
- android:layout_marginBottom="36dip"
- android:visibility="invisible" />
-
- </FrameLayout>
-
- <com.android.systemui.recent.StatusBarTouchProxy
- android:id="@+id/status_bar_touch_proxy"
- android:layout_width="match_parent"
- android:layout_height="@*android:dimen/system_bar_height"
- android:layout_alignParentBottom="true"
- android:layout_alignParentStart="true"
- />
-
-
-</com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/layout/system_bar_recent_panel_footer.xml b/packages/SystemUI/res/layout/system_bar_recent_panel_footer.xml
deleted file mode 100644
index 4d14d1f..0000000
--- a/packages/SystemUI/res/layout/system_bar_recent_panel_footer.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/listview_footer_padding"
- android:layout_height="24dip"
- android:layout_width="match_parent">
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/system_bar_settings_view.xml b/packages/SystemUI/res/layout/system_bar_settings_view.xml
deleted file mode 100644
index 4987dd9..0000000
--- a/packages/SystemUI/res/layout/system_bar_settings_view.xml
+++ /dev/null
@@ -1,157 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
--->
-
-<com.android.systemui.statusbar.tablet.SettingsView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- >
-
- <!-- Airplane mode -->
- <LinearLayout
- android:id="@+id/airplane"
- style="@style/SystemBarPanelSettingsRow"
- >
- <ImageView
- android:id="@+id/airplane_icon"
- style="@style/SystemBarPanelSettingsIcon"
- android:src="@drawable/ic_sysbar_airplane_on"
- />
- <TextView
- android:id="@+id/airplane_label"
- style="@style/SystemBarPanelSettingsContents"
- android:text="@string/status_bar_settings_airplane"
- />
- <Switch
- android:id="@+id/airplane_checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="5dp"
- />
- </LinearLayout>
- <View style="@style/SystemBarPanelSettingsPanelSeparator" />
-
- <!-- Network -->
- <LinearLayout
- android:id="@+id/network"
- style="@style/SystemBarPanelSettingsRow"
- >
- <ImageView
- android:id="@+id/network_icon"
- style="@style/SystemBarPanelSettingsIcon"
- android:src="@drawable/ic_sysbar_wifi_on"
- />
- <TextView
- android:id="@+id/network_label"
- style="@style/SystemBarPanelSettingsContents"
- android:text="@string/status_bar_settings_wifi_button"
- />
- </LinearLayout>
- <View style="@style/SystemBarPanelSettingsPanelSeparator" />
-
- <!-- Rotation lock -->
- <LinearLayout
- android:id="@+id/rotate"
- style="@style/SystemBarPanelSettingsRow"
- >
- <ImageView
- android:id="@+id/rotate_icon"
- style="@style/SystemBarPanelSettingsIcon"
- android:src="@drawable/ic_sysbar_rotate_on"
- />
- <TextView
- android:id="@+id/rotate_label"
- style="@style/SystemBarPanelSettingsContents"
- android:text="@string/status_bar_settings_auto_rotation"
- />
- <Switch
- android:id="@+id/rotate_checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="5dp"
- />
- </LinearLayout>
- <View
- android:id="@+id/rotate_separator"
- style="@style/SystemBarPanelSettingsPanelSeparator" />
-
- <!-- Brightness -->
- <LinearLayout style="@style/SystemBarPanelSettingsRow" >
- <ImageView
- android:id="@+id/brightness_icon"
- style="@style/SystemBarPanelSettingsIcon"
- android:src="@drawable/ic_sysbar_brightness"
- />
- <com.android.systemui.settings.ToggleSlider
- android:id="@+id/brightness"
- android:layout_width="0dp"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:layout_marginEnd="2dp"
- systemui:text="@string/status_bar_settings_auto_brightness_label"
- />
- </LinearLayout>
- <View style="@style/SystemBarPanelSettingsPanelSeparator" />
-
- <!-- Notifications / Do not disturb -->
- <LinearLayout
- android:id="@+id/do_not_disturb"
- style="@style/SystemBarPanelSettingsRow"
- >
- <ImageView
- android:id="@+id/do_not_disturb_icon"
- style="@style/SystemBarPanelSettingsIcon"
- android:src="@drawable/ic_notification_open"
- />
- <TextView
- style="@style/SystemBarPanelSettingsContents"
- android:text="@string/status_bar_settings_notifications"
- />
- <Switch
- android:id="@+id/do_not_disturb_checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="5dp"
- />
- </LinearLayout>
- <View style="@style/SystemBarPanelSettingsPanelSeparator" />
-
- <!-- Link to settings -->
- <LinearLayout
- android:id="@+id/settings"
- style="@style/SystemBarPanelSettingsRow"
- >
-
- <ImageView
- android:id="@+id/settings"
- style="@style/SystemBarPanelSettingsIcon"
- android:src="@drawable/ic_sysbar_quicksettings"
- />
- <TextView
- style="@style/SystemBarPanelSettingsContents"
- android:text="@string/status_bar_settings_settings_button"
- />
- </LinearLayout>
- <View style="@style/SystemBarPanelSettingsPanelSeparator" />
-
-</com.android.systemui.statusbar.tablet.SettingsView>
-
diff --git a/packages/SystemUI/res/layout/system_bar_ticker_compat.xml b/packages/SystemUI/res/layout/system_bar_ticker_compat.xml
deleted file mode 100644
index 14cdc40..0000000
--- a/packages/SystemUI/res/layout/system_bar_ticker_compat.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:gravity="bottom"
- >
-
- <ImageView
- android:id="@+id/large_icon"
- android:layout_width="@android:dimen/notification_large_icon_width"
- android:layout_height="@android:dimen/notification_large_icon_height"
- android:scaleType="center"
- android:visibility="gone"
- />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="@*android:dimen/system_bar_height"
- android:layout_weight="1"
- android:background="@drawable/system_bar_ticker_background"
- >
-
- <ImageView android:id="@+id/left_icon"
- android:layout_width="64dp"
- android:layout_height="match_parent"
- android:scaleType="center"
- android:visibility="gone"
- />
-
- <TextView android:id="@+id/text"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="12dp"
- android:gravity="center_vertical"
- android:maxLines="2"
- />
-
- <ImageView android:id="@+id/right_icon"
- android:layout_width="64dp"
- android:layout_height="match_parent"
- android:scaleType="center"
- android:visibility="gone"
- />
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/system_bar_ticker_panel.xml b/packages/SystemUI/res/layout/system_bar_ticker_panel.xml
deleted file mode 100644
index 49d0405..0000000
--- a/packages/SystemUI/res/layout/system_bar_ticker_panel.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
--->
-
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- >
-
- <View
- android:layout_height="@*android:dimen/system_bar_height"
- android:layout_width="match_parent"
- android:background="@drawable/system_bar_ticker_background"
- android:layout_alignParentStart="true"
- android:layout_alignParentBottom="true"
- android:clickable="false"
- />
-
- <ImageView
- android:id="@+id/large_icon"
- android:layout_width="@android:dimen/notification_large_icon_height"
- android:layout_height="@android:dimen/notification_large_icon_width"
- android:scaleType="center"
- android:visibility="gone"
- android:layout_alignParentStart="true"
- android:layout_alignParentBottom="true"
- />
-
- <FrameLayout
- android:id="@+id/ticker_expanded"
- android:layout_weight="1"
- android:layout_height="@*android:dimen/system_bar_height"
- android:layout_width="match_parent"
- android:layout_toEndOf="@id/large_icon"
- android:layout_alignParentBottom="true"
- android:layout_alignWithParentIfMissing="true"
- />
-</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/universe.xml b/packages/SystemUI/res/layout/universe.xml
deleted file mode 100644
index 390c467..0000000
--- a/packages/SystemUI/res/layout/universe.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent" android:layout_height="match_parent">
- <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:layout_centerHorizontal="true" android:layout_alignParentTop="true"
- android:src="@drawable/bugdroid" android:scaleType="center" />
-
- <Button android:id="@+id/close"
- android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:layout_alignParentTop="true" android:layout_alignParentEnd="true"
- android:text="@string/close_universe" />
-
- <TextView android:id="@+id/title"
- android:layout_width="match_parent" android:layout_height="wrap_content"
- android:layout_below="@id/close" android:layout_centerHorizontal="true"
- android:paddingBottom="16dp"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:gravity="center" />
-
- <ImageView android:id="@+id/bottom"
- android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:layout_below="@id/title" android:layout_centerHorizontal="true"
- android:layout_marginTop="16dp" />
-</RelativeLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 60d6edc..aa66811 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Stelsel-UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Maak skoon"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Moenie steur nie"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Wys kennisgewings"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Verwyder uit lys"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Program Info"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Geen onlangse programme nie"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Ligging deur GPS gestel"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Verwyder alle kennisgewings."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Programinligting"</string>
- <string name="close_universe" msgid="3736513750241754348">"Maak toe"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Kennisgewings af"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Tik hier om kennisgewings weer aan te skakel."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Die skerm sal outomaties draai."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skerm is in landskapsoriëntasie gesluit."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skerm is in portretoriëntasie gesluit."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Kennisgewings verskyn hier"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Verkry enige tyd toegang tot hulle deur af te sleep."\n"Sleep weer af vir stelselkontroles."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Sleep rand van skerm om balk te wys"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Sleep van rand van skerm af om stelselbalk te wys"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 600ad6f..a0d2960 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"የስርዓት UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"አጥራ"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"አይረብሹ"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"ማሳወቂያዎች አሳይ"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ከዝርዝር አስወግድ"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"የትግበራ መረጃ"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"ምንም የቅርብ ጊዜ ትግበራዎች የሉም"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"በ GPS የተዘጋጀ ሥፍራ"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"ሁሉንም ማሳወቂያዎች አጽዳ"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"የመተግበሪያ መረጃ"</string>
- <string name="close_universe" msgid="3736513750241754348">"ዝጋ"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"ማሳወቂያዎች ጠፍተዋል"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"ማስታወቅያዎችን መልሶ ለማብራት እዚህ ጋር መታ አድርግ።"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ማያ ገጽ በራስ ሰር ይዞራል።"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ማያ ገጽ በወርድ ገፅ አቀማመጥ ተቆልፏል።"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ማያ ገጽ በቁም ገፅ አቀማመጥ ተቆልፏል።"</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ራስ-ሰር"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"ማሳወቂያዎች እዚህ ላይ ይታያሉ"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"ወደ ታች በማንሸራተት በማንኛውም ጊዜ ይድረሱባቸው።"\n"Swipe የስርዓት መቆጣጠሪያዎችን ለማምጣት እንደገና ወደ ታች ያንሸራትቱ።"</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"አሞሌውን ለማሳየት የማያ ገጹን ጠርዝ ላይ ያንሸራትቱ"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"አሞሌውን ለማሳየት ከማያ ገጹ ጠርዝ ጀምረው ያንሸራትቱ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 44dcac1..1571531 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"واجهة مستخدم النظام"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"محو"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"عدم الإزعاج"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"إظهار التنبيهات"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"إزالة من القائمة"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"معلومات التطبيق"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"ليس هناك تطبيقات حديثة"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"تم تعيين الموقع بواسطة GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"محو جميع الإشعارات."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"معلومات التطبيق"</string>
- <string name="close_universe" msgid="3736513750241754348">"إغلاق"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"التنبيهات معطّلة"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"انقر هنا لإعادة تشغيل الإشعارات."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"سيتم تدوير الشاشة تلقائيًا."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"تم تأمين الشاشة في الاتجاه الأفقي."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"تم تأمين الشاشة في الاتجاه العمودي."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"تلقائي"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"تظهر الإشعارات هنا"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"يمكنك الدخول إليها في أي وقت بالتمرير السريع إلى أسفل."\n"يمكنك التمرير السريع إلى أسفل مرة أخرى للوصول إلى عناصر تحكم النظام."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"مرر سريعًا لحافة الشاشة لإظهار الشريط"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"مرر سريعًا من حافة الشاشة لإظهار شريط النظام"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index d66e946..b7d58e2 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Інтэрфейс карыстальніка сістэмы"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ачысціць"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Не турбаваць"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Паказваць паведамленні"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Выдаліць са спісу"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Звесткі аб прыкладанні"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Апошніх прыкладанняў няма"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Месца задана праз GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Выдалiць усе апавяшчэннi."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Інфармацыя пра прыкладанне"</string>
- <string name="close_universe" msgid="3736513750241754348">"Закрыць"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Паведамленні адключаны"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Націсніце тут, каб зноў уключыць апавяшчэнні."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Экран паварочваецца аўтаматычна."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Экран заблакiраваны ў альбомнай арыентацыі."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Экран заблакiраваны ў партрэтнай арыентацыі."</string>
@@ -206,4 +201,8 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АЎТА"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Апавяшчэнні з\'яўляюцца тут"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Атрымлівайце доступ да іх у любы час, праводзячы пальцам уніз."\n"Правядзіце пальцам уніз яшчэ раз, каб атрымаць доступ да сродкаў кіравання сістэмай."</string>
+ <!-- no translation found for hideybar_confirmation_message (9050869548951044371) -->
+ <skip />
+ <!-- no translation found for hideybar_confirmation_message_long (7117692795163620626) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index d7094af..6cce40b 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Системен ПИ"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Изчистване"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Не ме безпокойте"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Показване на известията"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Премахване от списъка"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Информация за приложението"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Няма скорошни приложения"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Местоположението е зададено от GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Изчистване на всички известия."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Информация за приложението"</string>
- <string name="close_universe" msgid="3736513750241754348">"Затваряне"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Известията са изключени"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Докоснете тук, за да включите отново известията."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екранът ще се завърта автоматично."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екранът е заключен в хоризонтална ориентация."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екранът е заключен във вертикална ориентация."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТ."</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Известията се показват тук"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Осъществявайте достъп до тях по всяко време, като прекарате пръст надолу."\n"Направете го отново за системните контроли."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Прекарайте пръст по ръба на екрана, за да се покаже лентата"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Прекарайте пръст от ръба на екрана, за да се покаже системната лента"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index a8d1e7b..61cb134 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"IU del sistema"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Esborra"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"No molesteu"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostra notificacions"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Elimina de la llista"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informació de l\'aplicació"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"No hi ha aplicacions recents"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"S\'ha establert la ubicació per GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Esborra totes les notificacions."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informació de l\'aplicació"</string>
- <string name="close_universe" msgid="3736513750241754348">"Tanca"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Notificacions desactivades"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Pica aquí per tornar a activar les notificacions."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla girarà automàticament."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla està bloquejada en orientació horitzontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla està bloquejada en orientació vertical."</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÀTICA"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Les notificacions apareixen aquí"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Accedeix-hi en qualsevol moment: només has de fer lliscar el dit cap avall."\n"Torna a fer lliscar el dit cap avall per fer que es mostrin els controls del sistema."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Fes lliscar el dit per la vora de la pantalla perquè es mostri la barra"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Fes lliscar el dit des de la vora de la pantalla perquè es mostri la barra del sistema"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 86f069f..775d081 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"UI systému"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Vymazat"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Nerušit"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Zobrazit upozornění"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Odebrat ze seznamu"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informace o aplikaci"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Žádné nové aplikace"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Poloha nastavena pomocí systému GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Vymazat všechna oznámení."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informace o aplikaci"</string>
- <string name="close_universe" msgid="3736513750241754348">"Zavřít"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Oznámení jsou vypnuta"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Chcete-li oznámení znovu zapnout, klepněte sem."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Obrazovka se automaticky otočí."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Obrazovka je uzamčena v orientaci na šířku."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Obrazovka je uzamčena v orientaci na výšku."</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATICKY"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Zde se zobrazují oznámení"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Můžete je kdykoli zobrazit tím, že přejedete prstem dolů."\n"Přejedete-li prstem dolů ještě jednou, zobrazí se ovládací prvky systému."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Panel zobrazíte přejetím přes okraj obrazovky"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Systémový panel zobrazíte přejetím přes okraj obrazovky"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1bfe7c5..cf34d5a 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"System-UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ryd"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Forstyr ikke"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Vis meddelelser"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Fjern fra listen"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Oplysninger om appen"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Der er ingen seneste apps"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Placeringen er angivet ved hjælp af GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Ryd alle meddelelser."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Oplysninger om appen"</string>
- <string name="close_universe" msgid="3736513750241754348">"Luk"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Underretninger slået fra"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Tryk her for at slå underretninger til igen."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skærmen roterer automatisk."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skærmen er nu låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skærmen er nu låst i stående retning."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Underretninger vises her"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Få adgang til dem når som helst ved at stryge ned."\n"Stryg ned igen for at komme til systemindstillingerne."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Stryg kanten af skærmen for at se bjælken"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Stryg fra skærmens kant for at se systembjælken"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index cb073d2..8e13c57 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"System-UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Löschen"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Bitte nicht stören"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Benachrichtigungen zeigen"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Aus Liste entfernen"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App-Info"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Keine kürzlich geöffneten Apps"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Standort durch GPS festgelegt"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Alle Benachrichtigungen löschen"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"App-Details"</string>
- <string name="close_universe" msgid="3736513750241754348">"Schließen"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Benachrichtigungen aus"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Tippen Sie hier, um die Benachrichtigungen wieder zu aktivieren."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Bildschirm wird automatisch gedreht."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Bildschirm bleibt im Querformat."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Bildschirm bleibt im Hochformat."</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Benachrichtigungen erscheinen hier"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Greifen Sie jederzeit auf sie zu, indem Sie nach unten wischen."\n"Wischen Sie für Systemeinstellungen erneut nach unten."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Zum Einblenden der Leiste vom Rand wischen"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Zum Einblenden der Systemleiste vom Display-Rand weg wischen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 9baeb37..6a04819 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"UI συστήματ."</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Εκκαθάριση"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Μην ενοχλείτε"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Εμφάνιση ειδοποιήσεων"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Κατάργηση από τη λίστα"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Πληροφορίες εφαρμογής"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Δεν υπάρχουν πρόσφατες εφαρμογές"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Ρύθμιση τοποθεσίας με GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Εκκαθάριση όλων των ειδοποιήσεων."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Πληροφορίες εφαρμογής"</string>
- <string name="close_universe" msgid="3736513750241754348">"Κλείσιμο"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Ειδοποιήσεις ανενεργές"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Πατήστε εδώ για να ενεργοποιήσετε ξανά τις ειδοποιήσεις."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Θα γίνεται αυτόματη περιστροφή της οθόνης."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Η οθόνη έχει κλειδωθεί σε οριζόντιο προσανατολισμό."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Η οθόνη έχει κλειδωθεί σε κατακόρυφο προσανατολισμό."</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ΑΥΤΟΜΑΤΗ"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Οι ειδοποιήσεις εμφανίζονται εδώ"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Μεταβείτε σε αυτές ανά πάσα στιγμή σύροντας προς τα κάτω."\n"Σύρετε ξανά προς τα κάτω για τα στοιχεία ελέγχου συστήματος."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Σύρετε από την άκρη της οθόνης για να εμφανίσετε τη γραμμή"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Σύρετε από την άκρη της οθόνης για να εμφανίσετε τη γραμμή συστήματος"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index deb7d09..2f8e0cf 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"System UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Clear"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Do not disturb"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Show notifications"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remove from list"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App info"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"No recent apps"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Location set by GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Clear all notifications."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"App info"</string>
- <string name="close_universe" msgid="3736513750241754348">"Close"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Notifications off"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Tap here to turn notifications back on."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Screen will rotate automatically."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Screen is locked in landscape orientation."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Screen is locked in portrait orientation."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Notifications appear here"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Access them any time by swiping down."\n"Swipe down again for system controls."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Swipe edge of screen to reveal bar"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Swipe from edge of screen to reveal system bar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 7306469..d8ea0d4 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"IU del sistema"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Eliminar"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"No molestar"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificaciones"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Eliminar de la lista"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Información de la aplicación"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Ninguna aplicación reciente"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"La ubicación se estableció por GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Eliminar todas las notificaciones"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Información de la aplicación"</string>
- <string name="close_universe" msgid="3736513750241754348">"Cerrar"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Notificaciones desactivadas"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Toca aquí para volver a activar las notificaciones."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla girará automáticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÁTICO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Las notificaciones aparecen aquí."</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Desliza el dedo hacia abajo para acceder al contenido."\n"Vuelve a deslizar el dedo hacia abajo para acceder a los controles del sistema."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Desliza el dedo desde el borde de la pantalla para mostrar la barra."</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Desliza el dedo desde el borde de la pantalla para mostrar la barra del sistema."</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 6e5be0a..3ae2401 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"IU sistema"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"No molestar"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificaciones"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Eliminar de la lista"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Información de la aplicación"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"No hay aplicaciones recientes."</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Ubicación definida por GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Borrar todas las notificaciones"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Información de la aplicación"</string>
- <string name="close_universe" msgid="3736513750241754348">"Cerrar"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Notificaciones desactivadas"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Toca aquí para volver a activar las notificaciones."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla girará automáticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Las notificaciones aparecen aquí"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Desliza el dedo hacia abajo para acceder al contenido."\n"Vuelve a deslizar el dedo hacia abajo para acceder a los controles del sistema."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Desliza el borde de la pantalla para mostrar la barra"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Desliza el borde de la pantalla para mostrar la barra del sistema"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 5737645..4dcf12c 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Süsteemi UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Kustuta"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Mitte häirida"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Kuva teatised"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Loendist eemaldamine"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Rakenduse teave"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Uusi rakendusi pole"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS-i määratud asukoht"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Kustuta kõik teatised."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Rakenduse teave"</string>
- <string name="close_universe" msgid="3736513750241754348">"Sule"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Teatised väljas"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Teatiste uuesti sisselülitamiseks puudutage siin."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekraani pööramine toimub automaatselt."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekraan on lukustatud horisontaalsuunas."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekraan on lukustatud vertikaalsuunas."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMAATNE"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Märguanded ilmuvad siia"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Juurdepääs igal ajal sõrmega alla pühkides."\n"Süsteemi juhtnuppude jaoks pühkige uuesti alla."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Riba kuvamiseks pühkige ekraani serva"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Süsteemiriba kuvamiseks pühkige ekraani servast"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index b15afcc..62b9bd7 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"رابط کاربر سیستم"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"پاک کردن"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"مزاحم نشوید"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"نمایش اعلانها"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"حذف از لیست"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"اطلاعات برنامه"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"برنامه جدیدی موجود نیست"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"مکان تنظیم شده توسط GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"پاک کردن تمام اعلانها"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"اطلاعات برنامه"</string>
- <string name="close_universe" msgid="3736513750241754348">"بستن"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"اعلانها خاموش"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"برای روشن کردن مجدد اعلانها، اینجا را ضربه بزنید."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"صفحه به صورت خودکار میچرخد."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"صفحه اکنون در جهت افقی قفل است."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"صفحه اکنون در جهت عمودی قفل است."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"خودکار"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"اعلانها در اینجا نمایش داده میشوند"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"با کشیدن انگشت به طرف پایین به آنها دسترسی پیدا کنید."\n"برای کنترلهای سیستم دوباره انگشت خود را به سمت پایین بکشید."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"برای نمایش نوار، انگشت خود را از لبه صفحه به داخل بکشید"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"برای نمایش نوار سیستم، انگشت خود را از لبه صفحه به داخل بکشید"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index c5e0e21..a7915a6 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Käyttöliitt."</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Tyhjennä"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Varattu"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Näytä ilmoitukset"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Poista luettelosta"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Sovelluksen tiedot"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Ei viimeisimpiä sovelluksia"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Sijainti määritetty GPS:n avulla"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Tyhjennä kaikki ilmoitukset."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Sovelluksen tiedot"</string>
- <string name="close_universe" msgid="3736513750241754348">"Sulje"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Ilmoitukset pois käytöstä"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Ota ilmoitukset uudelleen käyttöön napauttamalla tätä."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ruutu kääntyy automaattisesti."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ruutu on lukittu vaakasuuntaan."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ruutu on lukittu pystysuuntaan."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Ilmoitukset näkyvät tässä"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Näet ilmoitukset liu\'uttamalla sormea alas ruudulla."\n"Voit palauttaa järjestelmän ohjaimet näkyviin liu\'uttamalla sormea alas uudelleen."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Tuo palkki näkyviin liu\'uttamalla ruudun reunasta"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Tuo järjestelmäpalkki näkyviin liu\'uttamalla ruudun reunasta"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 616adf5..dd2cee6 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"IU système"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Effacer"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ne pas déranger"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Afficher les notifications"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Supprimer de la liste"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informations sur l\'application"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Aucune application récente."</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Position définie par GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Supprimer toutes les notifications"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informations sur l\'application"</string>
- <string name="close_universe" msgid="3736513750241754348">"Fermer"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Notifications désactivées"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Appuyez ici pour réactiver les notifications."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"L\'écran pivote automatiquement."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"L\'écran est verrouillé en mode paysage."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"L\'écran est verrouillé en mode portrait."</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATIQUE"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Les notifications s’affichent ici"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Accédez-y à tout moment en faisant glisser le doigt vers le bas."\n"Répétez l\'opération pour accéder aux commandes du système."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Faites glisser votre doigt sur le côté de l\'écran pour afficher la barre."</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Faites glisser votre doigt à partir d\'un côté de l\'écran pour afficher la barre système."</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 0342e18..702469e 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"सिस्टम UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"साफ़ करें"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"परेशान न करें"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"सूचनाएं दिखाएं"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"सूची से निकालें"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"एप्लिकेशन जानकारी"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"कोई हाल ही के एप्लिकेशन नहीं"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS द्वारा सेट किया गया स्थान"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"सभी सूचनाएं साफ़ करें."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"एप्लिकेशन जानकारी"</string>
- <string name="close_universe" msgid="3736513750241754348">"बंद करें"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"सूचनाएं बंद"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"सूचनाओं को पुन: चालू करने के लिए यहां टैप करें."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"स्क्रीन स्वचालित रूप से घूमेगी."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"स्क्रीन लैंडस्केप अभिविन्यास में लॉक है."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"स्क्रीन पोर्ट्रेट अभिविन्यास में लॉक है."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"स्वत:"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"सूचनाएं यहां दिखाई देती हैं"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"नीचे स्वाइप करके उन तक कभी भी पहुंचें."\n"सिस्टम नियंत्रणों के लिए पुन: नीचे स्वाइप करें."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"बार दिखाने के लिए स्क्रीन के किनारे को स्वाइप करें"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"सिस्टम बार दिखाने के लिए स्क्रीन के किनारे से स्वाइप करें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index c35b237..ebdc5dd 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"UI sustava"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Očisti"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ne uznemiravaj"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Prikaži obavijesti"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Ukloni s popisa"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informacije o aplikaciji"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Nema nedavnih aplikacija"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokaciju utvrdio GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Brisanje svih obavijesti."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informacije o aplikaciji"</string>
- <string name="close_universe" msgid="3736513750241754348">"Zatvori"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Obavijesti isključene"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Dotaknite ovdje da biste ponovo uključili obavijesti."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Zaslon će se automatski zakrenuti."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaključan u pejzažnoj orijentaciji."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaključan u portretnoj orijentaciji."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATSKI"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Obavijesti se prikazuju ovdje"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Pristupite im u bilo kojem trenutku tako da prstom trznete prema dolje. "\n"Ponovo prstom trznite prema dolje za kontrole sustava."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Prijeđite prstom po rubu zaslona da bi se prikazala traka"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Prijeđite prstom od ruba zaslona da bi se prikazala traka sustava"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index b71cc78..9b05a07 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Rendszer UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Törlés"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ne zavarjanak"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Értesítések megjelenítése"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Eltávolítás a listából"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Alkalmazásinformáció"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Nincs újabb alkalmazás"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"A GPS beállította a helyet"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Minden értesítés törlése"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Alkalmazásinformáció"</string>
- <string name="close_universe" msgid="3736513750241754348">"Bezárás"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Értesítések kikapcsolva"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Itt érintse meg az értesítések bekapcsolásához."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"A képernyő automatikusan forogni fog."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"A képernyő zárolva van fekvő tájolásban."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"A képernyő zárolva van álló tájolásban."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"automatikus"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Az értesítések itt jelennek meg."</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Bármikor elérheti őket, ha lefelé húzza az ujját."\n"Húzza le az ujját még egyszer a rendszerbeállítások eléréséhez."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Csúsztassa ujját a képernyő szélén a sáv megjelenítéséhez"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Csúsztassa ujját a képernyő szélétől a rendszersáv megjelenítéséhez"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 2c26b0d..f8b03c6 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Sistem UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Bersihkan"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Jangan ganggu"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Tampilkan pemberitahuan"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Hapus dari daftar"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Info apl"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Tidak ada apl terbaru"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokasi yang disetel oleh GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Menghapus semua pemberitahuan."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Info aplikasi"</string>
- <string name="close_universe" msgid="3736513750241754348">"Tutup"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Pemberitahuan mati"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Ketuk di sini untuk menyalakan pemberitahuan lagi."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Layar akan diputar secara otomatis."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Layar dikunci dalam orientasi lanskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Layar dikunci dalam orientasi potret."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATIS"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Pemberitahuan muncul di sini"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Akses kapan saja dengan menggesek ke bawah."\n"Gesek ke bawah sekali lagi untuk kontrol sistem."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Gesek tepi layar untuk membuka bilah"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Gesek dari bagian tepi layar untuk membuka bilah sistem"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 0665fb8..5a6b285 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"UI sistema"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Cancella"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Non disturbare"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostra notifiche"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Rimuovi dall\'elenco"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informazioni applicazione"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Nessuna app recente"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Posizione stabilita dal GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Cancella tutte le notifiche."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informazioni applicazione"</string>
- <string name="close_universe" msgid="3736513750241754348">"Chiudi"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Notifiche disattivate"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Tocca qui per riattivare le notifiche."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Lo schermo ruoterà automaticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Lo schermo è bloccato in orientamento orizzontale."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Lo schermo è bloccato in orientamento verticale."</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Le notifiche vengono visualizzate qui"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Puoi accedervi in qualsiasi momento scorrendo verso il basso."\n"Fai scorrere di nuovo verso il basso per visualizzare i controlli del sistema."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Fai scorrere il bordo dello schermo per visualizzare la barra"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Fai scorrere il dito dal bordo dello schermo per visualizzare la barra di sistema"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index c75c039..653bb78 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"ממשק משתמש של המערכת"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"נקה"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"נא לא להפריע"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"הצג התראות"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"הסר מהרשימה"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"פרטי יישום"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"אין יישומים אחרונים"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"מיקום מוגדר על ידי GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"נקה את כל ההתראות."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"פרטי יישום"</string>
- <string name="close_universe" msgid="3736513750241754348">"סגור"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"מצב התראות כבוי"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"הקש כאן כדי להפעיל מחדש את ההתראות."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"המסך יסתובב באופן אוטומטי."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"המסך נעול כעת לרוחב."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"המסך נעול כעת לאורך."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"אוטומטי"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"הודעות מופיעות כאן"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"גש אליהם בכל עת על ידי החלקה למטה."\n"החלק למטה שוב למעבר למרכז הבקרה של המערכת."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"החלק מקצה המסך כדי להציג את הסרגל"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"החלק מקצה המסך כדי להציג את סרגל המערכת"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index fe0b9af..a57938d 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"システムUI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"通知を消去"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"通知を非表示"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"通知を表示"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"リストから削除"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"アプリ情報"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"最近使ったアプリはありません"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPSにより現在地が設定されました"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"通知をすべて消去。"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"アプリ情報"</string>
- <string name="close_universe" msgid="3736513750241754348">"閉じる"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"通知OFF"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"通知を再度ONにするにはここをタップします。"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"画面は自動的に回転します。"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"画面は横向きにロックされています。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"画面は縦向きにロックされています。"</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"ここに通知が表示されます"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"下にスワイプすると、いつでも通知を表示できます。"\n"システムを管理するにはもう一度下にスワイプしてください。"</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"バーを表示するには、画面の端からスワイプします"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"システムバーを表示するには、画面の端からスワイプします"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index eb583e1..ba9bcbf 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"시스템 UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"지우기"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"응답 거부"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"알림 표시"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"목록에서 삭제"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"앱 정보"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"최근에 사용한 앱 없음"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS에서 위치 설정"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"모든 알림 지우기"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"앱 정보"</string>
- <string name="close_universe" msgid="3736513750241754348">"닫기"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"알림 사용 안함"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"알림을 다시 사용하려면 여기를 터치하세요."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"화면이 자동으로 회전됩니다."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"화면이 가로 방향으로 잠겨 있습니다."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"화면이 세로 방향으로 잠겨 있습니다."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"자동"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"알림이 여기에 표시됨"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"아래로 스와이프하여 언제든 액세스하세요."\n"한 번 더 아래로 스와이프하면 시스템 관리로 이동합니다."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"화면 가장자리에서 스와이프하여 표시줄 표시"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"화면 가장자리에서 스와이프하여 시스템 표시줄 표시"</string>
</resources>
diff --git a/packages/SystemUI/res/values-land/refs.xml b/packages/SystemUI/res/values-land/refs.xml
new file mode 100644
index 0000000..f5e79b9
--- /dev/null
+++ b/packages/SystemUI/res/values-land/refs.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <item type="string" name="hideybar_confirmation_message">@string/hideybar_confirmation_message_long</item>
+</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 77d9e9a..4947298 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Sistemos NS"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Išvalyti"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Netrukdyti"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Rodyti pranešimus"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Pašalinti iš sąrašo"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Programos informacija"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Nėra naujausių programų"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS nustatyta vieta"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Išvalyti visus pranešimus."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Programos informacija"</string>
- <string name="close_universe" msgid="3736513750241754348">"Uždaryti"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Pranešimai išjungti"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Jei norite vėl įjungti pranešimus, palieskite čia."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekranas bus sukamas automatiškai."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Užrakintas ekranas yra horizontalios orientacijos."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Užrakintas ekranas yra vertikalios orientacijos."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATINIS"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Pranešimai rodomi čia"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Perbraukę žemyn bet kuriuo metu pasieksite pranešimus."\n"Jei norite naudoti sistemos valdiklius, perbraukite žemyn dar kartą."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Jei norite, kad būtų rodoma juosta, perbraukite ekrano krašte"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Jei norite, kad būtų rodoma sistemos juosta, perbraukite iš ekrano krašto"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index f1a9727..c8a1a1d 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Sistēmas UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Notīrīt"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Netraucēt"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Rādīt paziņojumus"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Noņemšana no saraksta"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Lietotnes informācija"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Nav nesen izmantotu lietotņu."</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS iestatītā atrašanās vieta"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Notīrīt visus paziņojumus"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informācija par lietotni"</string>
- <string name="close_universe" msgid="3736513750241754348">"Aizvērt"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Paziņojumi ir izslēgti"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Pieskarieties šeit, lai atkal ieslēgtu paziņojumus."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekrāns tiks pagriezts automātiski."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekrāns tagad ir bloķēts ainavas orientācijā."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekrāns tagad ir bloķēts portreta orientācijā."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMĀTISKI"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Šeit tiek rādīti paziņojumi"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Piekļūstiet tiem jebkurā laikā, velkot uz leju."\n"Vēlreiz velciet, lai tiktu parādītas sistēmas vadīklas."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Velciet no ekrāna malas, lai piekļūtu joslai."</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Velciet no ekrāna malas, lai piekļūtu sistēmas joslai."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index c99626d..4b71cb4 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Sistem UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Pdm bersih"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Jangan ganggu"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Tunjukkan pemberitahuan"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Alih keluar dari senarai"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Maklumat aplikasi"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Tiada aplikasi terbaharu"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokasi ditetapkan oleh GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Padamkan semua pemberitahuan."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Maklumat apl"</string>
- <string name="close_universe" msgid="3736513750241754348">"Tutup"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Pemberitahuan dimatikan"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Ketik di sini untuk menghidupkan kembali pemberitahuan."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skrin akan berputar secara automatik."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skrin dikunci dalam orientasi landskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skrin dikunci dalam orientasi potret."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Pemberitahuan dipaparkan di sini"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Akses panel pada bila-bila masa dengan meleret ke bawah."\n"Leret ke bawah sekali lagi untuk mendapatkan kawalan sistem."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Leret ke bahagian tepi skrin untuk menampakkan bar"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Leret dari tepi skrin untuk menampakkan bar sistem"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 3d32563..4764da6 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Sys.gr.snitt"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Fjern"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ikke forstyrr"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Vis varslinger"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Fjern fra listen"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Info om app"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Ingen nylige apper"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Posisjon angitt av GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Fjern alle varslinger."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Info om app"</string>
- <string name="close_universe" msgid="3736513750241754348">"Lukk"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Varsler er deaktivert"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Trykk her for å aktivere varsler på nytt."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skjermen roterer automatisk."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skjermen er låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skjermen er låst i stående retning."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Varslene vises her"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Bruk dem når som helst ved å sveipe nedover."\n"Sveip nedover igjen for å gå til systemkontrollene."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Sveip på kanten av skjermen for å få frem feltet"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Sveip fra kanten på skjermen for å få frem systemfeltet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 66f0a35..5e18d0a 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Systeem-UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wissen"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Niet storen"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Meldingen weergeven"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Verwijderen uit lijst"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App-info"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Geen recente apps"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Locatie bepaald met GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Alle meldingen wissen."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"App-info"</string>
- <string name="close_universe" msgid="3736513750241754348">"Sluiten"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Meldingen uit"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Tik hier om meldingen weer in te schakelen."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Scherm wordt automatisch geroteerd."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Het scherm is nu vergrendeld in liggende stand."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Het scherm is nu vergrendeld in staande stand."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATISCH"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Meldingen worden hier weergegeven"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"U kunt de meldingen op elk gewenst moment openen door met uw vinger omlaag te vegen."\n"Veeg nogmaals met uw vinger omlaag om de systeembesturingselementen weer te geven."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Veeg vanaf de rand om balk weer te geven"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Veeg vanaf de rand van het scherm om de systeembalk weer te geven"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 67e54de..13374ae 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Interfejs"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wyczyść"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Nie przeszkadzać"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Pokaż powiadomienia"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Usuń z listy"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informacje o aplikacji"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Brak ostatnio uruchomionych aplikacji."</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokalizacja z GPSa"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Usuń wszystkie powiadomienia."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"O aplikacji"</string>
- <string name="close_universe" msgid="3736513750241754348">"Zamknij"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Powiadomienia wyłączone"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Kliknij tutaj, by przywrócić powiadomienia."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran zostanie obrócony automatycznie."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran jest zablokowany w orientacji poziomej."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran jest zablokowany w orientacji pionowej."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATYCZNA"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Tutaj pokazują się powiadomienia"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Możesz je otworzyć w dowolnej chwili, przesuwając w dół."\n"Przesuń jeszcze raz w dół, by otworzyć ustawienia systemowe."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Przesuń palcem od krawędzi ekranu, by odkryć pasek"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Przesuń palcem od krawędzi ekranu, by odkryć pasek systemu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index ffbfa88..a55aaa4 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"IU do sist."</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Não incomodar"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificações"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remover da lista"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informações da aplicação"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Não existem aplicações recentes"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Localização definida por GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Limpar todas as notificações."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informações da aplicação"</string>
- <string name="close_universe" msgid="3736513750241754348">"Fechar"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Notificações desativadas"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Toque aqui para voltar a ativar as notificações."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"O ecrã será rodado automaticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"O ecrã está bloqueado na orientação horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"O ecrã está bloqueado na orientação vertical."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÁTICO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"As notificações são apresentadas aqui"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Pode aceder em qualquer altura, deslizando rapidamente para baixo com o dedo."\n"Deslize novamente para baixo para aceder aos controlos do sistema."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Deslize da extremidade do ecrã para revelar a barra"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Deslize da extremidade do ecrã para revelar a barra do sistema"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index ffe4027..8811f21 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Interf sist"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Não perturbe"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificações"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remover da lista"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informações do aplicativo"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Nenhum aplicativo recente"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Local definido por GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Limpar todas as notificações."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informações do aplicativo"</string>
- <string name="close_universe" msgid="3736513750241754348">"Fechar"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Notificações desativadas"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Toque aqui para ativar as notificações novamente."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"A tela girará automaticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"A tela está bloqueada na orientação paisagem."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"A tela está bloqueada na orientação retrato."</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"As notificações aparecem aqui"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Acesse a qualquer momento deslizando para baixo."\n"Deslize para baixo novamente para acessar os controles do sistema."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Deslize a borda da tela para ver a barra"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Deslize a partir da borda da tela ver a barra do sistema"</string>
</resources>
diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index 9e5dbe1..d68b80c 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -22,10 +22,6 @@
<!-- no translation found for app_label (7164937344850004466) -->
<skip />
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Stizzar"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
<!-- no translation found for status_bar_recent_remove_item_title (6026395868129852968) -->
<skip />
<!-- no translation found for status_bar_recent_inspect_item_title (7793624864528818569) -->
@@ -306,12 +302,6 @@
<skip />
<!-- no translation found for status_bar_notification_inspect_item_title (1163547729015390250) -->
<skip />
- <!-- no translation found for close_universe (3736513750241754348) -->
- <skip />
- <!-- no translation found for notifications_off_title (8936620513608443224) -->
- <skip />
- <!-- no translation found for notifications_off_text (2529001315769385273) -->
- <skip />
<!-- no translation found for accessibility_rotation_lock_off (4062780228931590069) -->
<skip />
<!-- no translation found for accessibility_rotation_lock_on_landscape (6731197337665366273) -->
@@ -378,4 +368,8 @@
<skip />
<!-- no translation found for status_bar_help_text (7874607155052076323) -->
<skip />
+ <!-- no translation found for hideybar_confirmation_message (9050869548951044371) -->
+ <skip />
+ <!-- no translation found for hideybar_confirmation_message_long (7117692795163620626) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index bea8141..37b1819 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"UI sistem"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ștergeţi"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Nu deranjaţi"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Afişaţi notificări"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Eliminaţi din listă"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informaţii despre aplicaţie"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Nu există aplicaţii recente"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Locaţie setată prin GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Ștergeţi toate notificările."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informaţii despre aplicaţie"</string>
- <string name="close_universe" msgid="3736513750241754348">"Închideţi"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Notificările sunt dezactivate"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Apăsaţi aici pentru a reactiva notificările."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ecranul se va roti în mod automat."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ecranul este blocat în orientarea de tip peisaj."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ecranul este blocat în orientarea de tip portret."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMAT"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Notificările se afişează aici"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Accesaţi-le oricând glisând în jos."\n"Glisaţi în jos din nou pentru comenzile sistemului."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Glisați dinspre marginea ecranului pentru a afișa bara"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Glisați dinspre marginea ecranului pentru a afișa bara de sistem"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 5525ed8..e2abc0c 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Графический интерфейс системы"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Очистить"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Не беспокоить"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Показать уведомления"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Удалить из списка"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"О приложении"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Список недавно использованных приложений пуст."</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Координаты по GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Удалить все уведомления"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"О приложении"</string>
- <string name="close_universe" msgid="3736513750241754348">"Закрыть"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Уведомления отключены"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Нажмите здесь, чтобы снова включить уведомления."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Экран будет поворачиваться автоматически."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Выбрана только альбомная ориентация экрана."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Выбрана только книжная ориентация экрана."</string>
@@ -208,4 +203,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТОНАСТРОЙКА"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Это панель уведомлений"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Ее можно открыть, пролистнув экран вниз."\n"Чтобы открыть настройки, проведите пальцем вниз ещё раз."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Чтобы открыть панель, проведите пальцем от края к центру экрана"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Чтобы открыть панель навигации, проведите пальцем от края к центру экрана"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 79005f1..e9aec22 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"UI systému"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Vymazať"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Nerušiť"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Zobraziť upozornenia"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Odstrániť zo zoznamu"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informácie o aplikácii"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Žiadne nedávne aplikácie"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Poloha nastavená pomocou GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Vymazať všetky upozornenia."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informácie o aplikácii"</string>
- <string name="close_universe" msgid="3736513750241754348">"Zavrieť"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Upozornenia sú vypnuté"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Klepnutím sem upozornenia znova povolíte."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Obrazovka sa automaticky otočí."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Obrazovka je uzamknutá v orientácii na šírku."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Obrazovka je uzamknutá v orientácii na výšku."</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATICKY"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Tu sa zobrazujú upozornenia"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Môžete ich kedykoľvek zobraziť tak, že posuniete prstom nadol."\n"Ak posuniete prstom nadol ešte raz, zobrazia sa ovládacie prvky systému."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Panel zobrazíte posunutím cez okraj obrazovky"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Systémový panel zobrazíte posunutím cez okraj obrazovky"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 742361d..a4ad396 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Sistemski uporabniški vmesnik"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Počisti"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ne moti"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Pokaži obvestila"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Odstrani s seznama"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Podatki o programu"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Ni nedavnih programov"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokacija nastavljena z GPS-om"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Izbriši vsa obvestila."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Podatki o aplikaciji"</string>
- <string name="close_universe" msgid="3736513750241754348">"Zapri"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Obvestila so izklopljena"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Dotaknite se tukaj, da ponovno vklopite obvestila."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Zaslon se bo samodejno zasukal."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaklenjen v ležeči usmerjenosti."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaklenjen v pokončni usmerjenosti."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"SAMODEJNO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Obvestila so prikazana tukaj"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Do njih lahko kadar koli dostopate tako, da povlečete navzdol."\n"Za prikaz sistemskih kontrolnikov znova povlecite navzdol."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Vrstico prikažete tako, da povlečete z roba zaslona"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Sistemsko vrstico prikažete tako, da povlečete z roba zaslona"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 3bf5052..e1a585d 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"UI система"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Обриши"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Не узнемиравај"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Приказуј упозорења"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Уклањање са листе"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Информације о апликацији"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Нема недавних апликација"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Локацију је подесио GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Обриши сва обавештења."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Информације о апликацији"</string>
- <string name="close_universe" msgid="3736513750241754348">"Затвори"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Обавештења су искључена"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Додирните овде да бисте поново укључили обавештења."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екран ће се аутоматски ротирати."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екран је закључан у хоризонталном положају."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екран је закључан у вертикалном положају."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АУТОМАТСКА"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Обавештења се појављују овде"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Приступите им у било ком тренутку листањем надоле."\n"Поново листајте надоле да би се приказале системске контроле."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Превуците по ивици екрана да би се приказала трака"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Превуците од ивице екрана да би се приказала системска трака"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 71eedd8..d39c533 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Gränssnitt"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ta bort"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Stör ej"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Visa aviseringar"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Ta bort från listan"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Info om appen"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Inga nya appar"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Platsen har identifierats av GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Ta bort alla meddelanden."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Info om appen"</string>
- <string name="close_universe" msgid="3736513750241754348">"Stäng"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Meddelanden inaktiverade"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Knacka lätt här om du vill aktivera meddelanden igen."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skärmen roteras automatiskt."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Bildskärmens riktning är nu låst i liggande format."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Bildskärmens riktning är nu låst i stående format."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Meddelanden visas här"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Du kommer åt dem när som helst genom att dra nedåt."\n"Dra nedåt igen om du vill visa systemkontroller."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Dra från kanten av skärmen om du vill visa fältet"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Dra från kanten av skärmen om du vill visa systemfältet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index ccbc51f..4e29d81 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"UI ya Mfumo"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Futa"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Usisumbue"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Onyesha arifa"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Ondoa kwenye orodha"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Taarifa za programu-matumizi"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Hakuna programu za sasa"</string>
@@ -166,9 +164,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Mahali pamewekwa na GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Futa arifa zote."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Taarifa ya programu"</string>
- <string name="close_universe" msgid="3736513750241754348">"Funga"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Arifa zimelemazwa"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Gonga hapa ili kuwezesha tena arifa."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skrini itazunguka kiotomatiki."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skrini imefungwa sasa katika uelekezo wa mandhari."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skrini imefungwa katika uelekeo wa picha."</string>
@@ -202,4 +197,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"KIOTOMATIKI"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Arifa zitaonekana hapa"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Zifikie wakati wowote kwa kutelezesha chini."\n"Telezesha chini tena kupata vidhibiti vya mfumo."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Papasa kwa kasi kutoka ukingo wa skrini ili kuonyesha upau"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Papasa kwa kasi kutoka ukingo wa skrini ili kuonyesha upau wa mfumo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/refs.xml b/packages/SystemUI/res/values-sw600dp-port/refs.xml
new file mode 100644
index 0000000..f5e79b9
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-port/refs.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <item type="string" name="hideybar_confirmation_message">@string/hideybar_confirmation_message_long</item>
+</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index e42855c..b1fc00a 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -16,22 +16,10 @@
*/
-->
<resources>
- <!-- size at which Notification icons will be drawn in the status bar -->
- <dimen name="system_bar_icon_drawing_size">24dip</dimen>
-
- <!-- opacity at which Notification icons will be drawn in the status bar -->
- <item type="dimen" name="system_bar_icon_drawing_alpha">100%</item>
-
- <!-- The width of the view containing non-menu status bar icons -->
- <dimen name="system_bar_navigation_key_width">80dip</dimen>
-
- <!-- The width of the view containing the menu status bar icon -->
- <dimen name="system_bar_navigation_menu_key_width">80dip</dimen>
<!-- ======================================== -->
<!-- The following resources were recently moved from sw600dp; there may
- be situations where they don't sync up perfectly with
- PhoneStatusBar/TabletStatusBar. -->
+ be situations where they don't sync up perfectly with PhoneStatusBar. -->
<!-- ======================================== -->
<!-- The width of the ticker, including the icon -->
diff --git a/packages/SystemUI/res/values-sw720dp/styles.xml b/packages/SystemUI/res/values-sw720dp/styles.xml
deleted file mode 100644
index 5009395..0000000
--- a/packages/SystemUI/res/values-sw720dp/styles.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
- <style name="SystemBarNotificationText">
- <item name="android:textSize">16sp</item>
- <item name="android:textColor">#ff999999</item>
- </style>
-
- <style name="SystemBarPanelSettingsRow">
- <item name="android:paddingRight">16dp</item>
- <item name="android:layout_height">64dp</item>
- <item name="android:layout_width">match_parent</item>
- <item name="android:orientation">horizontal</item>
- <item name="android:background">?android:attr/listChoiceBackgroundIndicator</item>
- </style>
-
- <style name="SystemBarPanelSettingsIcon">
- <item name="android:layout_height">match_parent</item>
- <item name="android:layout_width">64dp</item>
- <item name="android:scaleType">center</item>
- </style>
-
- <style name="SystemBarPanelSettingsContents">
- <item name="android:layout_height">wrap_content</item>
- <item name="android:layout_width">0dp</item>
- <item name="android:layout_weight">1</item>
- <item name="android:layout_gravity">start|center_vertical</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:textSize">18sp</item>
- </style>
-
- <style name="SystemBarPanelSettingsPanelSeparator">
- <item name="android:layout_marginEnd">0dp</item>
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">1dp</item>
- <item name="android:background">@android:drawable/divider_horizontal_dark</item>
- </style>
-
- <style name="TextAppearance.SystemBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
- <item name="android:textSize">30dp</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textColor">@android:color/holo_blue_light</item>
- </style>
-
- <style name="TextAppearance.SystemBar.Expanded.Clock" parent="@style/TextAppearance.StatusBar.Expanded.Clock">
- <item name="android:textSize">48dp</item>
- <item name="android:fontFamily">sans-serif-light</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textColor">#ffffff</item>
- </style>
-
- <style name="TextAppearance.SystemBar.Expanded.Date" parent="@style/TextAppearance.StatusBar.Expanded.Date">
- <item name="android:textSize">14dp</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textColor">#666666</item>
- <item name="android:textAllCaps">true</item>
- </style>
-
-</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 1429a3c..a0cd9a6 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"ส่วนติดต่อผู้ใช้ของระบบ"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"ล้างข้อมูล"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"ห้ามรบกวน"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"แสดงการแจ้งเตือน"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ลบจากรายการ"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ข้อมูลแอปพลิเคชัน"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"ไม่มีแอปพลิเคชันล่าสุด"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"ตำแหน่งที่กำหนดโดย GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"ล้างการแจ้งเตือนทั้งหมด"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"ข้อมูลแอป"</string>
- <string name="close_universe" msgid="3736513750241754348">"ปิด"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"การแจ้งเตือนปิดอยู่"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"แตะที่นี่เพื่อเปิดการแจ้งเตือนอีกครั้ง"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"หน้าจอจะหมุนโดยอัตโนมัติ"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวนอน"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวตั้ง"</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"อัตโนมัติ"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"การแจ้งเตือนจะแสดงขึ้นที่นี่"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"เข้าถึงได้ทุกเมื่อด้วยการกวาดนิ้วลง"\n"กวาดนิ้วลงอีกครั้งสำหรับการควบคุมระบบ"</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"กวาดขอบของหน้าจอเพื่อแสดงแถบ"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"กวาดจากขอบของหน้าจอเพื่อแสดงแถบระบบ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index c48db46..4ba399a 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"UI ng System"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"I-clear"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Huwag gambalain"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Magpakita ng notification"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Alisin mula sa listahan"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Impormasyon ng app"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Walang kamakailang apps"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokasyong itinatakda ng GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"I-clear ang lahat ng notification."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Impormasyon ng app"</string>
- <string name="close_universe" msgid="3736513750241754348">"Isara"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Naka-off ang mga notification"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Tumapik dito upang muling i-on ang mga notification."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Awtomatikong iikot ang screen."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Naka-lock ang screen sa pahigang oryentasyon."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Naka-lock ang screen sa patayong oryentasyon."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Dito lumalabas ang mga notification"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"I-access ang mga ito anumang oras sa pamamagitan ng pag-swipe pababa."\n"Muling mag-swipe pababa para sa mga kontrol ng system."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Mag-swipe sa gilid ng screen upang ipakita ang bar"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Mag-swipe mula sa gilid ng screen upang ipakita ang system bar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d0dd099..6808f30 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Sist Arayüzü"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Temizle"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Rahatsız etmeyin"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Bildirimleri göster"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Listeden kaldır"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Uygulama bilgileri"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Son uygulama yok"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Konum GPS ile belirlendi"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Tüm bildirimleri temizle"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Uygulama bilgileri"</string>
- <string name="close_universe" msgid="3736513750241754348">"Kapat"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Bildirimler kapalı"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Bildirimleri tekrar açmak için buraya hafifçe vurun."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran otomatik olarak dönecektir."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran yatay yönde kilitlendi."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran dikey yönde kilitlendi."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATİK"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Bildirimler burada görünür"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Aşağıya hızlıca kaydırarak bunlara istediğiniz zaman erişebilirsiniz."\n"Sistem denetimleri için tekrar hızlıca aşağı kaydırın."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Çubuğu görüntülemek için ekranın kenarından hızlıca kaydırın"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Sistem çubuğunu görüntülemek için ekranın kenarından hızlıca kaydırın"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 5f059b4..bb507d9 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Інтерфейс системи"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Очист."</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Не турбувати"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Показувати сповіщення"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Видалити зі списку"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Інформація про програму"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Немає останніх програм"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Місцезнаходження встановлено за допомогою GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Очистити всі сповіщення."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Інформація про програму"</string>
- <string name="close_universe" msgid="3736513750241754348">"Закрити"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Сповіщення вимкнено"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Торкніться тут, щоб знову ввімкнути сповіщення."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екран обертатиметься автоматично."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екран заблоковано в альбомній орієнтації."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екран заблоковано в книжковій орієнтації."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТО"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Сповіщення з’являються тут"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Отримуйте до них доступ будь-коли, провівши пальцем униз."\n"Знову проведіть униз, щоб відкрити елементи керування системи."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Гортайте від краю екрана, щоб з’явилась панель"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Проведіть пальцем від краю екрана, щоб з’явилась навігаційна панель"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index b56dd74..59e5d1d 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Giao diện người dùng hệ thống"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Xóa"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Không làm phiền"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Hiển thị thông báo"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Xóa khỏi danh sách"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Thông tin về ứng dụng"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Không có ứng dụng nào gần đây"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Vị trí đặt bởi GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Xóa tất cả thông báo."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Thông tin về ứng dụng"</string>
- <string name="close_universe" msgid="3736513750241754348">"Đóng"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Tắt thông báo"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Chạm vào đây để bật lại thông báo."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Màn hình sẽ xoay tự động."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Màn hình hiện bị khóa theo hướng ngang."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Màn hình hiện bị khóa theo hướng dọc."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"TỰ ĐỘNG"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Thông báo xuất hiện tại đây"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Truy cập vào chúng bất kỳ lúc nào bằng cách vuốt xuống."\n"Vuốt lại xuống để hiển thị các điều khiển hệ thống."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Vuốt cạnh màn hình để hiển thị thanh"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Vuốt từ cạnh màn hình để hiển thị thanh hệ thống"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index d0811d9..af2228b 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"系统用户界面"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"请勿打扰"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"显示通知"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"从列表中删除"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"应用信息"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"最近没有运行任何应用"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"已通过 GPS 确定位置"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"清除所有通知。"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"应用信息"</string>
- <string name="close_universe" msgid="3736513750241754348">"关闭"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"通知功能已停用"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"点按此处可重新启用通知功能。"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"屏幕会自动旋转。"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"屏幕锁定为横向模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"屏幕锁定为纵向模式。"</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自动"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"通知会显示在这里"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"向下滑动可随时查看通知。"\n"再次向下滑动可使用系统控制功能。"</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"从边缘向里滑可显示系统栏"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"从屏幕边缘向里滑动即可显示系统栏"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 170f15a..c0e5bac 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"系統 UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"勿干擾"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"顯示通知"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"從清單中移除"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"應用程式資訊"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"沒有最近使用的應用程式"</string>
@@ -170,9 +168,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS 已定位"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"清除所有通知。"</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"應用程式資訊"</string>
- <string name="close_universe" msgid="3736513750241754348">"關閉"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"關閉通知"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"輕按這裡即可重新開啟通知。"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"螢幕會自動旋轉。"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"螢幕已鎖定為橫向模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"螢幕已鎖定為垂直模式。"</string>
@@ -206,4 +201,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"系統會在這裡顯示通知"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"向下滑動即可隨時存取通知。"\n"再次向下滑動即可使用系統控制項。"</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"滑動螢幕邊緣即可顯示導覽列"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"從螢幕邊緣向內滑動即可顯示導覽列"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 8219ea7..295db6e 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -21,8 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"Uhlelo lwe-UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Sula"</string>
- <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ungaphazamisi"</string>
- <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Bonisa izaziso"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Susa ohlwini"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Ulwazi lwensiza"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Azikho izinhlelo zokusebenza zakamuva"</string>
@@ -168,9 +166,6 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Indawo ihlelwe i-GPS"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Susa zonke izaziso."</string>
<string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Ulwazi lohlelo lokusebenza"</string>
- <string name="close_universe" msgid="3736513750241754348">"Vala"</string>
- <string name="notifications_off_title" msgid="8936620513608443224">"Izaziso zivaliwe"</string>
- <string name="notifications_off_text" msgid="2529001315769385273">"Thepha lapha ukuvula futhi izaziso."</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Isikrini sizophenduka ngokuzenzakalela."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Isikrini sikhiyelwe ngomumo we-landscape."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Isikrini sikhiyelwe ngomumo we-portrait."</string>
@@ -204,4 +199,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OKUZENZAKALELAYO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Izaziso zivela lapha"</string>
<string name="status_bar_help_text" msgid="7874607155052076323">"Kufinyelele noma kunini ngokuswayiphela phansi."\n"Swayiphela phansi futhi ngezilawuli zesistimu."</string>
+ <string name="hideybar_confirmation_message" msgid="9050869548951044371">"Swayipha unqenqema wesikrini ukuze uveze ibha"</string>
+ <string name="hideybar_confirmation_message_long" msgid="7117692795163620626">"Swayipha kusukela kunqenqema ukuze uveze ibha yesistimu"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index acb192d..4a7d090 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -27,12 +27,7 @@
<color name="notification_list_shadow_top">#80000000</color>
<drawable name="recents_callout_line">#99ffffff</drawable>
<drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable>
- <drawable name="intruder_bg_pressed">#ff33B5E5</drawable>
+ <drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
<drawable name="notification_header_bg">#FF000000</drawable>
-
- <!-- ==================== system bar only ==================== -->
- <drawable name="system_bar_background">#ff000000</drawable>
- <!-- the darkening filter applied to notifications -->
- <drawable name="notification_icon_area_smoke">#aa000000</drawable>
<color name="notification_panel_scrim_color">#B0000000</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 92df9b9..b70dee1 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -37,10 +37,6 @@
interface. This name is in the ComponentName flattened format (package/class) -->
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>
- <!-- Component to be used as the system bar service. Must implement the IStatusBar
- interface. This name is in the ComponentName flattened format (package/class) -->
- <string name="config_systemBarComponent" translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string>
-
<!-- Whether or not we show the number in the bar. -->
<bool name="config_statusBarShowNumber">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5767e63..129e0e8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -24,17 +24,6 @@
all of the currently visible notifications. [CHAR LIMIT=10]-->
<string name="status_bar_clear_all_button">Clear</string>
- <!-- The text for the button in the notification window-shade that turns
- on do not disturb mode, where notifications no longer show their ticker,
- no sound plays, and no icons are visible. The windowshade continues to show
- the notifications. [CHAR LIMIT=25]-->
- <string name="status_bar_do_not_disturb_button">Do not disturb</string>
-
- <!-- The text for the button in the notification window-shade that turns
- off do not disturb mode. After clicking this, notifications will be
- shown again. [CHAR LIMIT=25] -->
- <string name="status_bar_please_disturb_button">Show notifications</string>
-
<!-- Title shown in recents popup for removing an application from the list -->
<string name="status_bar_recent_remove_item_title">Remove from list</string>
@@ -436,16 +425,6 @@
application -->
<string name="status_bar_notification_inspect_item_title">App info</string>
- <!-- [CHAR LIMIT=NONE] -->
- <string name="close_universe">Close</string>
-
- <!-- Title for the pseudo-notification shown when notifications are disabled (do-not-disturb
- mode) -->
- <string name="notifications_off_title">Notifications off</string>
-
- <!-- Content text for do-not-disturb mode notification -->
- <string name="notifications_off_text">Tap here to turn notifications back on.</string>
-
<!-- Description of the button in the phone-style notification panel that controls auto-rotation, when auto-rotation is on. [CHAR LIMIT=NONE] -->
<string name="accessibility_rotation_lock_off">Screen will rotate automatically.</string>
@@ -519,4 +498,10 @@
<string name="status_bar_help_title">Notifications appear here</string>
<!-- Body of help text shown when the notification panel is pulled down for the very first time. [CHAR LIMIT=NONE] -->
<string name="status_bar_help_text">Access them anytime by swiping down.\nSwipe down again for system controls.</string>
+
+ <!-- Toast bar message when hiding the navigation bar -->
+ <string name="hideybar_confirmation_message">Swipe edge of screen to reveal bar</string>
+
+ <!-- Longer version of toast bar message when hiding the navigation bar (if room) -->
+ <string name="hideybar_confirmation_message_long">Swipe from edge of screen to reveal system bar</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7ddf261..11940a3 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -34,7 +34,7 @@
<item name="android:wallpaperIntraOpenExitAnimation">@anim/wallpaper_recents_launch_from_launcher_exit</item>
</style>
- <style name="TextAppearance.StatusBar.IntruderAlert"
+ <style name="TextAppearance.StatusBar.HeadsUp"
parent="@*android:style/TextAppearance.StatusBar">
</style>
@@ -154,9 +154,9 @@
<style name="Animation.StatusBar">
</style>
- <style name="Animation.StatusBar.IntruderAlert">
- <item name="android:windowEnterAnimation">@anim/priority_alert_enter</item>
- <item name="android:windowExitAnimation">@anim/priority_alert_exit</item>
+ <style name="Animation.StatusBar.HeadsUp">
+ <item name="android:windowEnterAnimation">@anim/heads_up_enter</item>
+ <item name="android:windowExitAnimation">@anim/heads_up_exit</item>
</style>
<style name="TextAppearance.StatusBar.PhoneTicker"
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBag.java b/packages/SystemUI/src/com/android/systemui/BeanBag.java
index f5a90ca..a396759 100644
--- a/packages/SystemUI/src/com/android/systemui/BeanBag.java
+++ b/packages/SystemUI/src/com/android/systemui/BeanBag.java
@@ -16,44 +16,25 @@
package com.android.systemui;
-import android.animation.AnimatorSet;
-import android.animation.PropertyValuesHolder;
-import android.animation.ObjectAnimator;
import android.animation.TimeAnimator;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.provider.Settings;
+import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Pair;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import java.util.HashMap;
+
import java.util.Random;
public class BeanBag extends Activity {
diff --git a/packages/SystemUI/src/com/android/systemui/BootReceiver.java b/packages/SystemUI/src/com/android/systemui/BootReceiver.java
index d3ce30d..8e24eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/BootReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/BootReceiver.java
@@ -21,7 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
-import android.util.Slog;
+import android.util.Log;
/**
* Performs a number of miscellaneous, non-system-critical actions
@@ -40,7 +40,7 @@
context.startService(loadavg);
}
} catch (Exception e) {
- Slog.e(TAG, "Can't start load average service", e);
+ Log.e(TAG, "Can't start load average service", e);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index edfaf49..fa1e3fe 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -23,15 +23,15 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.os.Vibrator;
-import android.util.Slog;
+import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.View.OnClickListener;
public class ExpandHelper implements Gefingerpoken, OnClickListener {
public interface Callback {
@@ -113,7 +113,7 @@
= new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
- if (DEBUG_SCALE) Slog.v(TAG, "onscalebegin()");
+ if (DEBUG_SCALE) Log.v(TAG, "onscalebegin()");
float focusX = detector.getFocusX();
float focusY = detector.getFocusY();
@@ -126,7 +126,7 @@
@Override
public boolean onScale(ScaleGestureDetector detector) {
- if (DEBUG_SCALE) Slog.v(TAG, "onscale() on " + mCurrView);
+ if (DEBUG_SCALE) Log.v(TAG, "onscale() on " + mCurrView);
return true;
}
@@ -143,7 +143,7 @@
mView = v;
}
public void setHeight(float h) {
- if (DEBUG_SCALE) Slog.v(TAG, "SetHeight: setting to " + h);
+ if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
ViewGroup.LayoutParams lp = mView.getLayoutParams();
lp.height = (int)h;
mView.setLayoutParams(lp);
@@ -158,7 +158,7 @@
}
public int getNaturalHeight(int maximum) {
ViewGroup.LayoutParams lp = mView.getLayoutParams();
- if (DEBUG_SCALE) Slog.v(TAG, "Inspecting a child of type: " +
+ if (DEBUG_SCALE) Log.v(TAG, "Inspecting a child of type: " +
mView.getClass().getName());
int oldHeight = lp.height;
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -230,7 +230,7 @@
}
private void updateExpansion() {
- if (DEBUG_SCALE) Slog.v(TAG, "updateExpansion()");
+ if (DEBUG_SCALE) Log.v(TAG, "updateExpansion()");
// are we scaling or dragging?
float span = mSGD.getCurrentSpan() - mInitialTouchSpan;
span *= USE_SPAN ? 1f : 0f;
@@ -270,10 +270,10 @@
}
private boolean isInside(View v, float x, float y) {
- if (DEBUG) Slog.d(TAG, "isinside (" + x + ", " + y + ")");
+ if (DEBUG) Log.d(TAG, "isinside (" + x + ", " + y + ")");
if (v == null) {
- if (DEBUG) Slog.d(TAG, "isinside null subject");
+ if (DEBUG) Log.d(TAG, "isinside null subject");
return false;
}
if (mEventSource != null) {
@@ -281,14 +281,14 @@
mEventSource.getLocationOnScreen(location);
x += location[0];
y += location[1];
- if (DEBUG) Slog.d(TAG, " to global (" + x + ", " + y + ")");
+ if (DEBUG) Log.d(TAG, " to global (" + x + ", " + y + ")");
}
int[] location = new int[2];
v.getLocationOnScreen(location);
x -= location[0];
y -= location[1];
- if (DEBUG) Slog.d(TAG, " to local (" + x + ", " + y + ")");
- if (DEBUG) Slog.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")");
+ if (DEBUG) Log.d(TAG, " to local (" + x + ", " + y + ")");
+ if (DEBUG) Log.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")");
boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight());
return inside;
}
@@ -307,10 +307,10 @@
private float calculateGlow(float target, float actual) {
// glow if overscale
- if (DEBUG_GLOW) Slog.d(TAG, "target: " + target + " actual: " + actual);
+ if (DEBUG_GLOW) Log.d(TAG, "target: " + target + " actual: " + actual);
float stretch = Math.abs((target - actual) / mMaximumStretch);
float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f)));
- if (DEBUG_GLOW) Slog.d(TAG, "stretch: " + stretch + " strength: " + strength);
+ if (DEBUG_GLOW) Log.d(TAG, "stretch: " + stretch + " strength: " + strength);
return (GLOW_BASE + strength * (1f - GLOW_BASE));
}
@@ -348,7 +348,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
- if (DEBUG_SCALE) Slog.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) +
+ if (DEBUG_SCALE) Log.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) +
" expanding=" + mExpanding +
(0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") +
(0 != (mExpansionStyle & PULL) ? " (pull)" : "") +
@@ -362,7 +362,7 @@
mInitialTouchSpan = mSGD.getCurrentSpan();
mLastFocusY = mInitialTouchFocusY;
mLastSpanY = mInitialTouchSpan;
- if (DEBUG_SCALE) Slog.d(TAG, "set initial span: " + mInitialTouchSpan);
+ if (DEBUG_SCALE) Log.d(TAG, "set initial span: " + mInitialTouchSpan);
if (mExpanding) {
return true;
@@ -376,7 +376,7 @@
xspan > mPullGestureMinXSpan &&
xspan > mSGD.getCurrentSpanY())) {
// detect a vertical pulling gesture with fingers somewhat separated
- if (DEBUG_SCALE) Slog.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
+ if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
final View underFocus = findView(x, y);
if (underFocus != null) {
@@ -393,7 +393,7 @@
if (mWatchingForPull) {
final int yDiff = y - mLastMotionY;
if (yDiff > mTouchSlop) {
- if (DEBUG) Slog.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
+ if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
mLastMotionY = y;
final View underFocus = findView(x, y);
if (underFocus != null) {
@@ -413,7 +413,7 @@
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
- if (DEBUG) Slog.d(TAG, "up/cancel");
+ if (DEBUG) Log.d(TAG, "up/cancel");
finishExpanding(false);
clearView();
break;
@@ -425,7 +425,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getActionMasked();
- if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + MotionEvent.actionToString(action) +
+ if (DEBUG_SCALE) Log.d(TAG, "touch: act=" + MotionEvent.actionToString(action) +
" expanding=" + mExpanding +
(0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") +
(0 != (mExpansionStyle & PULL) ? " (pull)" : "") +
@@ -484,14 +484,14 @@
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
- if (DEBUG) Slog.d(TAG, "pointer change");
+ if (DEBUG) Log.d(TAG, "pointer change");
mInitialTouchY += mSGD.getFocusY() - mLastFocusY;
mInitialTouchSpan += mSGD.getCurrentSpan() - mLastSpanY;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (DEBUG) Slog.d(TAG, "up/cancel");
+ if (DEBUG) Log.d(TAG, "up/cancel");
finishExpanding(false);
clearView();
break;
@@ -505,20 +505,20 @@
return;
}
mExpanding = true;
- if (DEBUG) Slog.d(TAG, "scale type " + expandType + " beginning on view: " + v);
+ if (DEBUG) Log.d(TAG, "scale type " + expandType + " beginning on view: " + v);
mCallback.setUserLockedChild(v, true);
setView(v);
setGlow(GLOW_BASE);
mScaler.setView(v);
mOldHeight = mScaler.getHeight();
if (mCallback.canChildBeExpanded(v)) {
- if (DEBUG) Slog.d(TAG, "working on an expandable child");
+ if (DEBUG) Log.d(TAG, "working on an expandable child");
mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
} else {
- if (DEBUG) Slog.d(TAG, "working on a non-expandable child");
+ if (DEBUG) Log.d(TAG, "working on a non-expandable child");
mNaturalHeight = mOldHeight;
}
- if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight +
+ if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
" mNaturalHeight: " + mNaturalHeight);
v.getParent().requestDisallowInterceptTouchEvent(true);
}
@@ -526,7 +526,7 @@
private void finishExpanding(boolean force) {
if (!mExpanding) return;
- if (DEBUG) Slog.d(TAG, "scale in finishing on view: " + mCurrView);
+ if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mCurrView);
float currentHeight = mScaler.getHeight();
float targetHeight = mSmallSize;
@@ -552,11 +552,11 @@
mExpanding = false;
mExpansionStyle = NONE;
- if (DEBUG) Slog.d(TAG, "wasClosed is: " + wasClosed);
- if (DEBUG) Slog.d(TAG, "currentHeight is: " + currentHeight);
- if (DEBUG) Slog.d(TAG, "mSmallSize is: " + mSmallSize);
- if (DEBUG) Slog.d(TAG, "targetHeight is: " + targetHeight);
- if (DEBUG) Slog.d(TAG, "scale was finished on view: " + mCurrView);
+ if (DEBUG) Log.d(TAG, "wasClosed is: " + wasClosed);
+ if (DEBUG) Log.d(TAG, "currentHeight is: " + currentHeight);
+ if (DEBUG) Log.d(TAG, "mSmallSize is: " + mSmallSize);
+ if (DEBUG) Log.d(TAG, "targetHeight is: " + targetHeight);
+ if (DEBUG) Log.d(TAG, "scale was finished on view: " + mCurrView);
}
private void clearView() {
@@ -575,7 +575,7 @@
String debugLog = "Looking for glows: " +
(mCurrViewTopGlow != null ? "found top " : "didn't find top") +
(mCurrViewBottomGlow != null ? "found bottom " : "didn't find bottom");
- Slog.v(TAG, debugLog);
+ Log.v(TAG, debugLog);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index cdd3d84..ca6a06f 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -16,6 +16,9 @@
package com.android.systemui;
+import static android.opengl.GLES20.*;
+import static javax.microedition.khronos.egl.EGL10.*;
+
import android.app.ActivityManager;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
@@ -35,18 +38,16 @@
import android.view.SurfaceHolder;
import android.view.WindowManager;
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-import javax.microedition.khronos.egl.EGLSurface;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
-import static android.opengl.GLES20.*;
-import static javax.microedition.khronos.egl.EGL10.*;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
/**
* Default built-in wallpaper that simply shows a static image.
diff --git a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
index 2b45ad1..37e974b 100644
--- a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
+++ b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
@@ -16,8 +16,6 @@
package com.android.systemui;
-import com.android.internal.os.ProcessStats;
-
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +29,8 @@
import android.view.View;
import android.view.WindowManager;
+import com.android.internal.os.ProcessStats;
+
public class LoadAverageService extends Service {
private View mView;
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
new file mode 100644
index 0000000..323905f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.view.Display;
+import android.view.View;
+
+public interface RecentsComponent {
+ void toggleRecents(Display display, int layoutDirection, View statusBarView);
+
+ void preloadRecentTasksList();
+
+ void cancelPreloadingRecentTasksList();
+
+ void closeRecents();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index 1f29990..c32f741 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -32,7 +32,7 @@
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.EventLog;
-import android.util.Slog;
+import android.util.Log;
import android.view.IWindowManager;
import android.view.MotionEvent;
import android.view.View;
@@ -43,27 +43,21 @@
import com.android.internal.widget.multiwaveview.GlowPadView;
import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
-
-import com.android.systemui.EventLogTags;
-import com.android.systemui.R;
-import com.android.systemui.recent.StatusBarTouchProxy;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarPanel;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
-import com.android.systemui.statusbar.tablet.StatusBarPanel;
-import com.android.systemui.statusbar.tablet.TabletStatusBar;
public class SearchPanelView extends FrameLayout implements
StatusBarPanel, ActivityOptions.OnAnimationStartedListener {
private static final int SEARCH_PANEL_HOLD_DURATION = 0;
static final String TAG = "SearchPanelView";
- static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
+ static final boolean DEBUG = PhoneStatusBar.DEBUG || false;
public static final boolean DEBUG_GESTURES = true;
private static final String ASSIST_ICON_METADATA_NAME =
"com.android.systemui.action_assist_icon";
private final Context mContext;
private BaseStatusBar mBar;
- private StatusBarTouchProxy mStatusBarTouchProxy;
private boolean mShowing;
private View mSearchTargetsContainer;
@@ -120,7 +114,7 @@
mContext.startActivityAsUser(intent, opts.toBundle(),
new UserHandle(UserHandle.USER_CURRENT));
} catch (ActivityNotFoundException e) {
- Slog.w(TAG, "Activity not found for " + intent.getAction());
+ Log.w(TAG, "Activity not found for " + intent.getAction());
onAnimationStarted();
}
}
@@ -172,7 +166,6 @@
super.onFinishInflate();
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSearchTargetsContainer = findViewById(R.id.search_panel_container);
- mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy);
// TODO: fetch views
mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
mGlowPadView.setOnTriggerListener(mGlowPadViewListener);
@@ -186,7 +179,7 @@
if (component == null || !mGlowPadView.replaceTargetDrawablesIfPresent(component,
ASSIST_ICON_METADATA_NAME,
com.android.internal.R.drawable.ic_action_assist_generic)) {
- if (DEBUG) Slog.v(TAG, "Couldn't grab icon for component " + component);
+ if (DEBUG) Log.v(TAG, "Couldn't grab icon for component " + component);
}
}
}
@@ -200,14 +193,7 @@
}
public boolean isInContentArea(int x, int y) {
- if (pointInside(x, y, mSearchTargetsContainer)) {
- return true;
- } else if (mStatusBarTouchProxy != null &&
- pointInside(x, y, mStatusBarTouchProxy)) {
- return true;
- } else {
- return false;
- }
+ return pointInside(x, y, mSearchTargetsContainer);
}
private final OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
@@ -297,17 +283,6 @@
mBar = bar;
}
- public void setStatusBarView(final View statusBarView) {
- if (mStatusBarTouchProxy != null) {
- mStatusBarTouchProxy.setStatusBar(statusBarView);
-// mGlowPadView.setOnTouchListener(new OnTouchListener() {
-// public boolean onTouch(View v, MotionEvent event) {
-// return statusBarView.onTouchEvent(event);
-// }
-// });
- }
- }
-
@Override
public boolean onTouchEvent(MotionEvent event) {
if (DEBUG_GESTURES) {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index f824a8e..d38d828 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -19,18 +19,17 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.animation.Animator.AnimatorListener;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.graphics.RectF;
import android.os.Handler;
import android.util.Log;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.LinearInterpolator;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.LinearInterpolator;
public class SwipeHelper implements Gefingerpoken {
static final String TAG = "com.android.systemui.SwipeHelper";
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 2110483c..fec7329 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -16,14 +16,16 @@
package com.android.systemui;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
import android.content.Context;
import android.content.res.Configuration;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Map;
+
public abstract class SystemUI {
public Context mContext;
+ public Map<Class<?>, Object> mComponents;
public abstract void start();
@@ -32,4 +34,15 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
}
+
+ @SuppressWarnings("unchecked")
+ public <T> T getComponent(Class<T> interfaceType) {
+ return (T) (mComponents != null ? mComponents.get(interfaceType) : null);
+ }
+
+ public <T, C extends T> void putComponent(Class<T> interfaceType, C component) {
+ if (mComponents != null) {
+ mComponents.put(interfaceType, component);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 1f3e942..ca5f7d1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -16,32 +16,26 @@
package com.android.systemui;
+import android.app.Service;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.util.Log;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
-
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Slog;
-import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
+import java.util.HashMap;
public class SystemUIService extends Service {
- static final String TAG = "SystemUIService";
+ private static final String TAG = "SystemUIService";
/**
- * The class names of the stuff to start.
+ * The classes of the stuff to start.
*/
- final Object[] SERVICES = new Object[] {
- 0, // system bar or status bar, filled in below.
+ private final Class<?>[] SERVICES = new Class[] {
+ com.android.systemui.recent.Recents.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,
@@ -50,44 +44,15 @@
/**
* Hold a reference on the stuff we start.
*/
- SystemUI[] mServices;
-
- private Class chooseClass(Object o) {
- if (o instanceof Integer) {
- final String cl = getString((Integer)o);
- try {
- return getClassLoader().loadClass(cl);
- } catch (ClassNotFoundException ex) {
- throw new RuntimeException(ex);
- }
- } else if (o instanceof Class) {
- return (Class)o;
- } else {
- throw new RuntimeException("Unknown system ui service: " + o);
- }
- }
+ private final SystemUI[] mServices = new SystemUI[SERVICES.length];
@Override
public void onCreate() {
- // Tell the accessibility layer that this process will
- // run as the current user, i.e. run across users.
- AccessibilityManager.createAsSharedAcrossUsers(this);
-
- // Pick status bar or system bar.
- IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
- try {
- SERVICES[0] = wm.hasSystemNavBar()
- ? R.string.config_systemBarComponent
- : R.string.config_statusBarComponent;
- } catch (RemoteException e) {
- Slog.w(TAG, "Failing checking whether status bar can hide", e);
- }
-
+ HashMap<Class<?>, Object> components = new HashMap<Class<?>, Object>();
final int N = SERVICES.length;
- mServices = new SystemUI[N];
for (int i=0; i<N; i++) {
- Class cl = chooseClass(SERVICES[i]);
- Slog.d(TAG, "loading: " + cl);
+ Class<?> cl = SERVICES[i];
+ Log.d(TAG, "loading: " + cl);
try {
mServices[i] = (SystemUI)cl.newInstance();
} catch (IllegalAccessException ex) {
@@ -96,7 +61,8 @@
throw new RuntimeException(ex);
}
mServices[i].mContext = this;
- Slog.d(TAG, "running: " + mServices[i]);
+ mServices[i].mComponents = components;
+ Log.d(TAG, "running: " + mServices[i]);
mServices[i].start();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/UniverseBackground.java b/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
deleted file mode 100644
index f859880..0000000
--- a/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Matrix;
-import android.graphics.PixelFormat;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-import android.view.Choreographer;
-import android.view.Display;
-import android.view.IWindowSession;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewRootImpl;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.animation.Transformation;
-import android.widget.FrameLayout;
-
-public class UniverseBackground extends FrameLayout {
- static final String TAG = "UniverseBackground";
- static final boolean SPEW = false;
- static final boolean CHATTY = false;
-
- final IWindowSession mSession;
- final View mContent;
- final View mBottomAnchor;
-
- final Runnable mAnimationCallback = new Runnable() {
- @Override
- public void run() {
- doAnimation(mChoreographer.getFrameTimeNanos());
- }
- };
-
- // fling gesture tuning parameters, scaled to display density
- private float mSelfExpandVelocityPx; // classic value: 2000px/s
- private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
- private float mFlingExpandMinVelocityPx; // classic value: 200px/s
- private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
- private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
- private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
- private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
-
- private float mExpandAccelPx; // classic value: 2000px/s/s
- private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
-
- static final int STATE_CLOSED = 0;
- static final int STATE_OPENING = 1;
- static final int STATE_OPEN = 2;
- private int mState = STATE_CLOSED;
-
- private float mDragStartX, mDragStartY;
- private float mAverageX, mAverageY;
-
- // position
- private int[] mPositionTmp = new int[2];
- private boolean mExpanded;
- private boolean mExpandedVisible;
-
- private boolean mTracking;
- private VelocityTracker mVelocityTracker;
-
- private Choreographer mChoreographer;
- private boolean mAnimating;
- private boolean mClosing; // only valid when mAnimating; indicates the initial acceleration
- private float mAnimY;
- private float mAnimVel;
- private float mAnimAccel;
- private long mAnimLastTimeNanos;
- private boolean mAnimatingReveal = false;
-
- private int mYDelta = 0;
- private Transformation mUniverseTransform = new Transformation();
- private final float[] mTmpFloats = new float[9];
-
- public UniverseBackground(Context context) {
- super(context);
- setBackgroundColor(0xff000000);
- mSession = WindowManagerGlobal.getWindowSession();
- mContent = View.inflate(context, R.layout.universe, null);
- addView(mContent);
- mContent.findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
- @Override public void onClick(View v) {
- animateCollapse();
- }
- });
- mBottomAnchor = mContent.findViewById(R.id.bottom);
- mChoreographer = Choreographer.getInstance();
- loadDimens();
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- loadDimens();
- }
-
- private void loadDimens() {
- final Resources res = getContext().getResources();
- mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
- mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
- mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
- mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
-
- mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
- mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
-
- mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
- mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
-
- mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
- }
-
- private void computeAveragePos(MotionEvent event) {
- final int num = event.getPointerCount();
- float x = 0, y = 0;
- for (int i=0; i<num; i++) {
- x += event.getX(i);
- y += event.getY(i);
- }
- mAverageX = x / num;
- mAverageY = y / num;
- }
-
- private void sendUniverseTransform() {
- if (getWindowToken() != null) {
- mUniverseTransform.getMatrix().getValues(mTmpFloats);
- try {
- mSession.setUniverseTransform(getWindowToken(), mUniverseTransform.getAlpha(),
- mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y],
- mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
- mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- } catch (RemoteException e) {
- }
- }
- }
-
- public WindowManager.LayoutParams getLayoutParams() {
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND,
- 0
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.OPAQUE);
- // this will allow the window to run in an overlay on devices that support this
- if (ActivityManager.isHighEndGfx()) {
- lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- }
- lp.setTitle("UniverseBackground");
- lp.windowAnimations = 0;
- return lp;
- }
-
- private int getExpandedViewMaxHeight() {
- return mBottomAnchor.getTop();
- }
-
- public void animateCollapse() {
- animateCollapse(1.0f);
- }
-
- public void animateCollapse(float velocityMultiplier) {
- if (SPEW) {
- Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
- + " mExpandedVisible=" + mExpandedVisible
- + " mExpanded=" + mExpanded
- + " mAnimating=" + mAnimating
- + " mAnimY=" + mAnimY
- + " mAnimVel=" + mAnimVel);
- }
-
- mState = STATE_CLOSED;
- if (!mExpandedVisible) {
- return;
- }
-
- int y;
- if (mAnimating) {
- y = (int)mAnimY;
- } else {
- y = getExpandedViewMaxHeight()-1;
- }
- // Let the fling think that we're open so it goes in the right direction
- // and doesn't try to re-open the windowshade.
- mExpanded = true;
- prepareTracking(y, false);
- performFling(y, -mSelfCollapseVelocityPx*velocityMultiplier, true);
- }
-
- private void updateUniverseScale() {
- if (mYDelta > 0) {
- int w = getWidth();
- int h = getHeight();
- float scale = (h-mYDelta+.5f) / (float)h;
- mUniverseTransform.getMatrix().setScale(scale, scale, w/2, h);
- if (CHATTY) Log.i(TAG, "w=" + w + " h=" + h + " scale=" + scale
- + ": " + mUniverseTransform);
- sendUniverseTransform();
- if (getVisibility() != VISIBLE) {
- setVisibility(VISIBLE);
- }
- } else {
- if (CHATTY) Log.i(TAG, "mYDelta=" + mYDelta);
- mUniverseTransform.clear();
- sendUniverseTransform();
- if (getVisibility() == VISIBLE) {
- setVisibility(GONE);
- }
- }
- }
-
- void resetLastAnimTime() {
- mAnimLastTimeNanos = System.nanoTime();
- if (SPEW) {
- Throwable t = new Throwable();
- t.fillInStackTrace();
- Slog.d(TAG, "resetting last anim time=" + mAnimLastTimeNanos, t);
- }
- }
-
- void doAnimation(long frameTimeNanos) {
- if (mAnimating) {
- if (SPEW) Slog.d(TAG, "doAnimation dt=" + (frameTimeNanos - mAnimLastTimeNanos));
- if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
- incrementAnim(frameTimeNanos);
- if (SPEW) {
- Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
- }
-
- if (mAnimY >= getExpandedViewMaxHeight()-1 && !mClosing) {
- if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
- mAnimating = false;
- mYDelta = getExpandedViewMaxHeight();
- updateUniverseScale();
- mExpanded = true;
- mState = STATE_OPEN;
- return;
- }
-
- if (mAnimY <= 0 && mClosing) {
- if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
- mAnimating = false;
- mYDelta = 0;
- updateUniverseScale();
- mExpanded = false;
- mState = STATE_CLOSED;
- return;
- }
-
- mYDelta = (int)mAnimY;
- updateUniverseScale();
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
- mAnimationCallback, null);
- }
- }
-
- void stopTracking() {
- mTracking = false;
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- void incrementAnim(long frameTimeNanos) {
- final long deltaNanos = Math.max(frameTimeNanos - mAnimLastTimeNanos, 0);
- final float t = deltaNanos * 0.000000001f; // ns -> s
- final float y = mAnimY;
- final float v = mAnimVel; // px/s
- final float a = mAnimAccel; // px/s/s
- mAnimY = y + (v*t) + (0.5f*a*t*t); // px
- mAnimVel = v + (a*t); // px/s
- mAnimLastTimeNanos = frameTimeNanos; // ns
- //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
- // + " mAnimAccel=" + mAnimAccel);
- }
-
- void prepareTracking(int y, boolean opening) {
- if (CHATTY) {
- Slog.d(TAG, "panel: beginning to track the user's touch, y=" + y + " opening=" + opening);
- }
-
- mTracking = true;
- mVelocityTracker = VelocityTracker.obtain();
- if (opening) {
- mAnimAccel = mExpandAccelPx;
- mAnimVel = mFlingExpandMinVelocityPx;
- mAnimY = y;
- mAnimating = true;
- mAnimatingReveal = true;
- resetLastAnimTime();
- mExpandedVisible = true;
- }
- if (mAnimating) {
- mAnimating = false;
- mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
- mAnimationCallback, null);
- }
- }
-
- void performFling(int y, float vel, boolean always) {
- if (CHATTY) {
- Slog.d(TAG, "panel: will fling, y=" + y + " vel=" + vel);
- }
-
- mAnimatingReveal = false;
-
- mAnimY = y;
- mAnimVel = vel;
-
- //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
-
- if (mExpanded) {
- if (!always && (
- vel > mFlingCollapseMinVelocityPx
- || (y > (getExpandedViewMaxHeight()*(1f-mCollapseMinDisplayFraction)) &&
- vel > -mFlingExpandMinVelocityPx))) {
- // We are expanded, but they didn't move sufficiently to cause
- // us to retract. Animate back to the expanded position.
- mAnimAccel = mExpandAccelPx;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are expanded and are now going to animate away.
- mAnimAccel = -mCollapseAccelPx;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- } else {
- if (always || (
- vel > mFlingExpandMinVelocityPx
- || (y > (getExpandedViewMaxHeight()*(1f-mExpandMinDisplayFraction)) &&
- vel > -mFlingCollapseMinVelocityPx))) {
- // We are collapsed, and they moved enough to allow us to
- // expand. Animate in the notifications.
- mAnimAccel = mExpandAccelPx;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are collapsed, but they didn't move sufficiently to cause
- // us to retract. Animate back to the collapsed position.
- mAnimAccel = -mCollapseAccelPx;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- }
- //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
- // + " mAnimAccel=" + mAnimAccel);
-
- resetLastAnimTime();
- mAnimating = true;
- mClosing = mAnimAccel < 0;
- mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
- mAnimationCallback, null);
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
- mAnimationCallback, null);
-
- stopTracking();
- }
-
- private void trackMovement(MotionEvent event) {
- mVelocityTracker.addMovement(event);
- }
-
- public boolean consumeEvent(MotionEvent event) {
- if (mState == STATE_CLOSED) {
- if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
- // Second finger down, time to start opening!
- computeAveragePos(event);
- mDragStartX = mAverageX;
- mDragStartY = mAverageY;
- mYDelta = 0;
- mUniverseTransform.clear();
- sendUniverseTransform();
- setVisibility(VISIBLE);
- mState = STATE_OPENING;
- prepareTracking((int)mDragStartY, true);
- mVelocityTracker.clear();
- trackMovement(event);
- return true;
- }
- return false;
- }
-
- if (mState == STATE_OPENING) {
- if (event.getActionMasked() == MotionEvent.ACTION_UP
- || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
- mVelocityTracker.computeCurrentVelocity(1000);
- computeAveragePos(event);
-
- float yVel = mVelocityTracker.getYVelocity();
- boolean negative = yVel < 0;
-
- float xVel = mVelocityTracker.getXVelocity();
- if (xVel < 0) {
- xVel = -xVel;
- }
- if (xVel > mFlingGestureMaxXVelocityPx) {
- xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
- }
-
- float vel = (float)Math.hypot(yVel, xVel);
- if (negative) {
- vel = -vel;
- }
-
- if (CHATTY) {
- Slog.d(TAG, String.format("gesture: vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
- mVelocityTracker.getXVelocity(),
- mVelocityTracker.getYVelocity(),
- xVel, yVel,
- vel));
- }
-
- performFling((int)mAverageY, vel, false);
- mState = STATE_OPEN;
- return true;
- }
-
- computeAveragePos(event);
- mYDelta = (int)(mAverageY - mDragStartY);
- if (mYDelta > getExpandedViewMaxHeight()) {
- mYDelta = getExpandedViewMaxHeight();
- }
- updateUniverseScale();
- return true;
- }
-
- return false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 8979fc2..f8b347c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -26,7 +26,6 @@
import android.os.SystemClock;
import android.util.Log;
-import java.lang.Thread;
import java.util.LinkedList;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 0c6e59c..5b4bb2c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -28,10 +28,9 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.util.Slog;
+import android.util.Log;
import com.android.systemui.SystemUI;
-import com.google.android.collect.Maps;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -50,7 +49,7 @@
private IAudioService mAudioService;
private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);
- private final HashMap<IBinder, Client> mClients = Maps.newHashMap();
+ private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>();
@Override
public void start() {
@@ -61,7 +60,7 @@
try {
mAudioService.setRingtonePlayer(mCallback);
} catch (RemoteException e) {
- Slog.e(TAG, "Problem registering RingtonePlayer: " + e);
+ Log.e(TAG, "Problem registering RingtonePlayer: " + e);
}
}
@@ -82,7 +81,7 @@
@Override
public void binderDied() {
- if (LOGD) Slog.d(TAG, "binderDied() token=" + mToken);
+ if (LOGD) Log.d(TAG, "binderDied() token=" + mToken);
synchronized (mClients) {
mClients.remove(mToken);
}
@@ -94,7 +93,7 @@
@Override
public void play(IBinder token, Uri uri, int streamType) throws RemoteException {
if (LOGD) {
- Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
+ Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
+ Binder.getCallingUid() + ")");
}
Client client;
@@ -112,7 +111,7 @@
@Override
public void stop(IBinder token) {
- if (LOGD) Slog.d(TAG, "stop(token=" + token + ")");
+ if (LOGD) Log.d(TAG, "stop(token=" + token + ")");
Client client;
synchronized (mClients) {
client = mClients.remove(token);
@@ -125,7 +124,7 @@
@Override
public boolean isPlaying(IBinder token) {
- if (LOGD) Slog.d(TAG, "isPlaying(token=" + token + ")");
+ if (LOGD) Log.d(TAG, "isPlaying(token=" + token + ")");
Client client;
synchronized (mClients) {
client = mClients.get(token);
@@ -139,7 +138,7 @@
@Override
public void playAsync(Uri uri, UserHandle user, boolean looping, int streamType) {
- if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
+ if (LOGD) Log.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Async playback only available from system UID.");
}
@@ -149,7 +148,7 @@
@Override
public void stopAsync() {
- if (LOGD) Slog.d(TAG, "stopAsync()");
+ if (LOGD) Log.d(TAG, "stopAsync()");
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Async playback only available from system UID.");
}
diff --git a/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java b/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
index 888b76e..1ac8295 100644
--- a/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
@@ -32,7 +32,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.Slog;
+import android.util.Log;
import android.view.WindowManager;
import com.android.systemui.R;
@@ -79,7 +79,7 @@
try {
policyService.snoozeLimit(template);
} catch (RemoteException e) {
- Slog.w(TAG, "problem snoozing network policy", e);
+ Log.w(TAG, "problem snoozing network policy", e);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index ccb711a..e084dac 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -16,10 +16,6 @@
package com.android.systemui.power;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Arrays;
-
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -27,15 +23,15 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.UserHandle;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
import android.provider.Settings;
-import android.util.Slog;
+import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
@@ -43,6 +39,10 @@
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
@@ -126,19 +126,19 @@
int bucket = findBatteryLevelBucket(mBatteryLevel);
if (DEBUG) {
- Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
+ Log.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
+ " .. " + mLowBatteryReminderLevels[0]
+ " .. " + mLowBatteryReminderLevels[1]);
- Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
- Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
- Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
- Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
- Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
- Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
+ Log.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
+ Log.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
+ Log.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
+ Log.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
+ Log.d(TAG, "bucket " + oldBucket + " --> " + bucket);
+ Log.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
}
if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
- Slog.d(TAG, "showing invalid charger warning");
+ Log.d(TAG, "showing invalid charger warning");
showInvalidChargerDialog();
return;
} else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
@@ -164,20 +164,20 @@
showLowBatteryWarning();
}
} else {
- Slog.w(TAG, "unknown intent: " + intent);
+ Log.w(TAG, "unknown intent: " + intent);
}
}
};
void dismissLowBatteryWarning() {
if (mLowBatteryDialog != null) {
- Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
+ Log.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
mLowBatteryDialog.dismiss();
}
}
void showLowBatteryWarning() {
- Slog.i(TAG,
+ Log.i(TAG,
((mBatteryLevelTextView == null) ? "showing" : "updating")
+ " low battery warning: level=" + mBatteryLevel
+ " [" + findBatteryLevelBucket(mBatteryLevel) + "]");
@@ -234,7 +234,7 @@
void playLowBatterySound() {
if (DEBUG) {
- Slog.i(TAG, "playing low battery sound. WOMP-WOMP!");
+ Log.i(TAG, "playing low battery sound. WOMP-WOMP!");
}
final ContentResolver cr = mContext.getContentResolver();
@@ -261,7 +261,7 @@
}
void showInvalidChargerDialog() {
- Slog.d(TAG, "showing invalid charger dialog");
+ Log.d(TAG, "showing invalid charger dialog");
dismissLowBatteryWarning();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java b/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java
index 2fc7dfc..84d13cf 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java
@@ -19,18 +19,18 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.animation.Animator.AnimatorListener;
import android.util.Log;
-import android.view.ViewTreeObserver;
import android.view.View;
import android.view.ViewPropertyAnimator;
+import android.view.ViewTreeObserver;
/*
* This is a helper class that listens to updates from the corresponding animation.
* For the first two frames, it adjusts the current play time of the animation to
* prevent jank at the beginning of the animation
*/
-public class FirstFrameAnimatorHelper implements ValueAnimator.AnimatorUpdateListener {
+public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
+ implements ValueAnimator.AnimatorUpdateListener {
private static final boolean DEBUG = false;
private static final int MAX_DELAY = 1000;
private static final int IDEAL_FRAME_DURATION = 16;
@@ -50,13 +50,14 @@
public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) {
mTarget = target;
- vpa.setListener(new AnimatorListenerAdapter() {
- public void onAnimationStart (Animator animation) {
- final ValueAnimator va = (ValueAnimator) animation;
- va.addUpdateListener(FirstFrameAnimatorHelper.this);
- onAnimationUpdate(va);
- }
- });
+ vpa.setListener(this);
+ }
+
+ // only used for ViewPropertyAnimators
+ public void onAnimationStart(Animator animation) {
+ final ValueAnimator va = (ValueAnimator) animation;
+ va.addUpdateListener(FirstFrameAnimatorHelper.this);
+ onAnimationUpdate(va);
}
public static void initializeDrawListener(View view) {
@@ -84,7 +85,11 @@
mStartTime = currentTime;
}
- if (!mHandlingOnAnimationUpdate) {
+ if (!mHandlingOnAnimationUpdate &&
+ // If the current play time exceeds the duration, the animation
+ // will get finished, even if we call setCurrentPlayTime -- therefore
+ // don't adjust the animation in that case
+ animation.getCurrentPlayTime() < animation.getDuration()) {
mHandlingOnAnimationUpdate = true;
long frameNum = sGlobalFrameCounter - mStartFrame;
// If we haven't drawn our first frame, reset the time to t = 0
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 02ddb73..8b2cd3f 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -37,7 +37,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
-import com.android.systemui.statusbar.tablet.TabletStatusBar;
import java.util.ArrayList;
import java.util.List;
@@ -46,7 +45,7 @@
public class RecentTasksLoader implements View.OnTouchListener {
static final String TAG = "RecentTasksLoader";
- static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
+ static final boolean DEBUG = PhoneStatusBar.DEBUG || false;
private static final int DISPLAY_TASKS = 20;
private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
new file mode 100644
index 0000000..f51e34b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recent;
+
+import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Paint;
+import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.RecentsComponent;
+import com.android.systemui.SystemUI;
+
+public class Recents extends SystemUI implements RecentsComponent {
+ private static final String TAG = "Recents";
+ private static final boolean DEBUG = false;
+
+ @Override
+ public void start() {
+ putComponent(RecentsComponent.class, this);
+ }
+
+ @Override
+ public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
+ if (DEBUG) Log.d(TAG, "toggle recents panel");
+ try {
+ TaskDescription firstTask = RecentTasksLoader.getInstance(mContext).getFirstTask();
+
+ Intent intent = new Intent(RecentsActivity.TOGGLE_RECENTS_INTENT);
+ intent.setClassName("com.android.systemui",
+ "com.android.systemui.recent.RecentsActivity");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+ if (firstTask == null) {
+ if (RecentsActivity.forceOpaqueBackground(mContext)) {
+ ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_from_launcher_enter,
+ R.anim.recents_launch_from_launcher_exit);
+ mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
+ UserHandle.USER_CURRENT));
+ } else {
+ // The correct window animation will be applied via the activity's style
+ mContext.startActivityAsUser(intent, new UserHandle(
+ UserHandle.USER_CURRENT));
+ }
+
+ } else {
+ Bitmap first = firstTask.getThumbnail();
+ final Resources res = mContext.getResources();
+
+ float thumbWidth = res
+ .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
+ float thumbHeight = res
+ .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height);
+ if (first == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
+ if (first.getWidth() != thumbWidth || first.getHeight() != thumbHeight) {
+ first = Bitmap.createScaledBitmap(first, (int) thumbWidth, (int) thumbHeight,
+ true);
+ if (first == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
+ }
+
+
+ DisplayMetrics dm = new DisplayMetrics();
+ display.getMetrics(dm);
+ // calculate it here, but consider moving it elsewhere
+ // first, determine which orientation you're in.
+ final Configuration config = res.getConfiguration();
+ int x, y;
+
+ if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ float appLabelLeftMargin = res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_app_label_left_margin);
+ float appLabelWidth = res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_app_label_width);
+ float thumbLeftMargin = res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_thumbnail_left_margin);
+ float thumbBgPadding = res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_thumbnail_bg_padding);
+
+ float width = appLabelLeftMargin +
+ +appLabelWidth
+ + thumbLeftMargin
+ + thumbWidth
+ + 2 * thumbBgPadding;
+
+ x = (int) ((dm.widthPixels - width) / 2f + appLabelLeftMargin + appLabelWidth
+ + thumbBgPadding + thumbLeftMargin);
+ y = (int) (dm.heightPixels
+ - res.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height)
+ - thumbBgPadding);
+ if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
+ x = dm.widthPixels - x - res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_thumbnail_width);
+ }
+
+ } else { // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ float thumbTopMargin = res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_thumbnail_top_margin);
+ float thumbBgPadding = res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_thumbnail_bg_padding);
+ float textPadding = res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_text_description_padding);
+ float labelTextSize = res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_app_label_text_size);
+ Paint p = new Paint();
+ p.setTextSize(labelTextSize);
+ float labelTextHeight = p.getFontMetricsInt().bottom
+ - p.getFontMetricsInt().top;
+ float descriptionTextSize = res.getDimensionPixelSize(
+ R.dimen.status_bar_recents_app_description_text_size);
+ p.setTextSize(descriptionTextSize);
+ float descriptionTextHeight = p.getFontMetricsInt().bottom
+ - p.getFontMetricsInt().top;
+
+ float statusBarHeight = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ float recentsItemTopPadding = statusBarHeight;
+
+ float height = thumbTopMargin
+ + thumbHeight
+ + 2 * thumbBgPadding + textPadding + labelTextHeight
+ + recentsItemTopPadding + textPadding + descriptionTextHeight;
+ float recentsItemRightPadding = res
+ .getDimensionPixelSize(R.dimen.status_bar_recents_item_padding);
+ float recentsScrollViewRightPadding = res
+ .getDimensionPixelSize(R.dimen.status_bar_recents_right_glow_margin);
+ x = (int) (dm.widthPixels - res
+ .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width)
+ - thumbBgPadding - recentsItemRightPadding
+ - recentsScrollViewRightPadding);
+ y = (int) ((dm.heightPixels - statusBarHeight - height) / 2f + thumbTopMargin
+ + recentsItemTopPadding + thumbBgPadding + statusBarHeight);
+ }
+
+ ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation(
+ statusBarView,
+ first, x, y,
+ new ActivityOptions.OnAnimationStartedListener() {
+ public void onAnimationStarted() {
+ Intent intent =
+ new Intent(RecentsActivity.WINDOW_ANIMATION_START_INTENT);
+ intent.setPackage("com.android.systemui");
+ mContext.sendBroadcastAsUser(intent,
+ new UserHandle(UserHandle.USER_CURRENT));
+ }
+ });
+ intent.putExtra(RecentsActivity.WAITING_FOR_WINDOW_ANIMATION_PARAM, true);
+ mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
+ UserHandle.USER_CURRENT));
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Failed to launch RecentAppsIntent", e);
+ }
+ }
+
+ @Override
+ public void preloadRecentTasksList() {
+ if (DEBUG) Log.d(TAG, "preloading recents");
+ Intent intent = new Intent(RecentsActivity.PRELOAD_INTENT);
+ intent.setClassName("com.android.systemui",
+ "com.android.systemui.recent.RecentsPreloadReceiver");
+ mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+
+ RecentTasksLoader.getInstance(mContext).preloadFirstTask();
+ }
+
+ @Override
+ public void cancelPreloadingRecentTasksList() {
+ if (DEBUG) Log.d(TAG, "cancel preloading recents");
+ Intent intent = new Intent(RecentsActivity.CANCEL_PRELOAD_INTENT);
+ intent.setClassName("com.android.systemui",
+ "com.android.systemui.recent.RecentsPreloadReceiver");
+ mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+
+ RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
+ }
+
+ @Override
+ public void closeRecents() {
+ if (DEBUG) Log.d(TAG, "closing recents panel");
+ Intent intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT);
+ intent.setPackage("com.android.systemui");
+ mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
index 62030ad..818c2980 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
@@ -30,7 +30,7 @@
import android.view.WindowManager;
import com.android.systemui.R;
-import com.android.systemui.statusbar.tablet.StatusBarPanel;
+import com.android.systemui.statusbar.StatusBarPanel;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index c64b954..ada30ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -61,21 +61,19 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.StatusBarPanel;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
-import com.android.systemui.statusbar.tablet.StatusBarPanel;
-import com.android.systemui.statusbar.tablet.TabletStatusBar;
import java.util.ArrayList;
public class RecentsPanelView extends FrameLayout implements OnItemClickListener, RecentsCallback,
StatusBarPanel, Animator.AnimatorListener {
static final String TAG = "RecentsPanelView";
- static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
+ static final boolean DEBUG = PhoneStatusBar.DEBUG || false;
private PopupMenu mPopup;
private View mRecentsScrim;
private View mRecentsNoApps;
private ViewGroup mRecentsContainer;
- private StatusBarTouchProxy mStatusBarTouchProxy;
private boolean mShowing;
private boolean mWaitingToShow;
@@ -289,14 +287,7 @@
}
public boolean isInContentArea(int x, int y) {
- if (pointInside(x, y, mRecentsContainer)) {
- return true;
- } else if (mStatusBarTouchProxy != null &&
- pointInside(x, y, mStatusBarTouchProxy)) {
- return true;
- } else {
- return false;
- }
+ return pointInside(x, y, mRecentsContainer);
}
public void show(boolean show) {
@@ -430,12 +421,6 @@
return mShowing;
}
- public void setStatusBarView(View statusBarView) {
- if (mStatusBarTouchProxy != null) {
- mStatusBarTouchProxy.setStatusBar(statusBarView);
- }
- }
-
public void setRecentTasksLoader(RecentTasksLoader loader) {
mRecentTasksLoader = loader;
}
@@ -451,7 +436,6 @@
super.onFinishInflate();
mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container);
- mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy);
mListAdapter = new TaskDescriptionAdapter(mContext);
if (mRecentsContainer instanceof RecentsScrollView){
RecentsScrollView scrollView
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 403c643f..ee076d9 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -21,7 +21,6 @@
import android.content.res.Configuration;
import android.database.DataSetObserver;
import android.graphics.Canvas;
-import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.FloatMath;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/StatusBarTouchProxy.java b/packages/SystemUI/src/com/android/systemui/recent/StatusBarTouchProxy.java
deleted file mode 100644
index ded114f..0000000
--- a/packages/SystemUI/src/com/android/systemui/recent/StatusBarTouchProxy.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recent;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.FrameLayout;
-
-public class StatusBarTouchProxy extends FrameLayout {
-
- private View mStatusBar;
-
- public StatusBarTouchProxy(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setStatusBar(View statusBar) {
- mStatusBar = statusBar;
- }
-
- public boolean onTouchEvent (MotionEvent event) {
- return mStatusBar.dispatchTouchEvent(event);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 5041617..5617520 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -45,7 +45,6 @@
import android.os.Process;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -197,15 +196,18 @@
// Create screenshot directory if it doesn't exist
mScreenshotDir.mkdirs();
+ // media provider uses seconds, not milliseconds
+ long dateSeconds = mImageTime / 1000;
+
// Save the screenshot to the MediaStore
ContentValues values = new ContentValues();
ContentResolver resolver = context.getContentResolver();
values.put(MediaStore.Images.ImageColumns.DATA, mImageFilePath);
values.put(MediaStore.Images.ImageColumns.TITLE, mImageFileName);
values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, mImageFileName);
- values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, mImageTime);
- values.put(MediaStore.Images.ImageColumns.DATE_ADDED, mImageTime);
- values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, mImageTime);
+ values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, dateSeconds);
+ values.put(MediaStore.Images.ImageColumns.DATE_ADDED, dateSeconds);
+ values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, dateSeconds);
values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");
values.put(MediaStore.Images.ImageColumns.WIDTH, mImageWidth);
values.put(MediaStore.Images.ImageColumns.HEIGHT, mImageHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 6a0fe47..456b5fa 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -23,7 +23,6 @@
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.util.Log;
public class TakeScreenshotService extends Service {
private static final String TAG = "TakeScreenshotService";
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index fdeead1..327e715 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -18,7 +18,6 @@
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncTask;
@@ -30,9 +29,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
-import android.util.Slog;
-import android.view.IWindowManager;
-import android.widget.CompoundButton;
import android.widget.ImageView;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index 1b05084..ff79f04 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -16,19 +16,16 @@
package com.android.systemui.settings;
-import com.android.systemui.R;
-
import android.app.Dialog;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
-import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
-import java.lang.Runnable;
+import com.android.systemui.R;
/** A dialog that provides controls for adjusting the screen brightness. */
public class BrightnessDialog extends Dialog implements
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index 122f81e..036bd4f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.UserHandle;
public abstract class CurrentUserTracker extends BroadcastReceiver {
@@ -54,4 +55,8 @@
}
public abstract void onUserSwitched(int newUserId);
+
+ public boolean isCurrentUserOwner() {
+ return mCurrentUserId == UserHandle.USER_OWNER;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java b/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java
index 1075a73..8bc72c9 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java
@@ -16,9 +16,6 @@
package com.android.systemui.settings;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -26,10 +23,13 @@
import android.content.IntentFilter;
import android.os.Handler;
import android.os.UserHandle;
-import android.util.Slog;
+import android.util.Log;
import com.android.systemui.SystemUI;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public class SettingsUI extends SystemUI {
private static final String TAG = "SettingsUI";
private static final boolean DEBUG = false;
@@ -42,7 +42,7 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) {
- if (DEBUG) Slog.d(TAG, "showing brightness dialog");
+ if (DEBUG) Log.d(TAG, "showing brightness dialog");
if (mBrightnessDialog == null) {
mBrightnessDialog = new BrightnessDialog(mContext);
@@ -59,7 +59,7 @@
}
} else {
- Slog.w(TAG, "unknown intent: " + intent);
+ Log.w(TAG, "unknown intent: " + intent);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
index c7c361c..d584043 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
@@ -21,10 +21,8 @@
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.util.Slog;
import android.view.View;
import android.widget.CompoundButton;
-import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
index 78226c5..9839fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
@@ -20,7 +20,6 @@
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.util.Slog;
import android.view.View;
import android.widget.ImageView;
import android.widget.RemoteViews.RemoteView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 683824b..69fc3cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1,4 +1,3 @@
-
/*
* Copyright (C) 2010 The Android Open Source Project
*
@@ -17,52 +16,35 @@
package com.android.systemui.statusbar;
-import android.service.notification.StatusBarNotification;
-import android.content.res.Configuration;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.widget.SizeAdaptiveLayout;
-import com.android.systemui.R;
-import com.android.systemui.SearchPanelView;
-import com.android.systemui.SystemUI;
-import com.android.systemui.recent.RecentTasksLoader;
-import com.android.systemui.recent.RecentsActivity;
-import com.android.systemui.recent.TaskDescription;
-import com.android.systemui.statusbar.policy.NotificationRowLayout;
-import com.android.systemui.statusbar.tablet.StatusBarPanel;
-
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
-import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
+import android.content.res.Configuration;
import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.Paint;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Slog;
import android.view.Display;
import android.view.IWindowManager;
import android.view.LayoutInflater;
@@ -79,6 +61,16 @@
import android.widget.RemoteViews;
import android.widget.TextView;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.widget.SizeAdaptiveLayout;
+import com.android.systemui.R;
+import com.android.systemui.RecentsComponent;
+import com.android.systemui.SearchPanelView;
+import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.policy.NotificationRowLayout;
+
import java.util.ArrayList;
import java.util.Locale;
@@ -94,16 +86,19 @@
protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
protected static final int MSG_OPEN_SEARCH_PANEL = 1024;
protected static final int MSG_CLOSE_SEARCH_PANEL = 1025;
- protected static final int MSG_SHOW_INTRUDER = 1026;
- protected static final int MSG_HIDE_INTRUDER = 1027;
+ protected static final int MSG_SHOW_HEADS_UP = 1026;
+ protected static final int MSG_HIDE_HEADS_UP = 1027;
- protected static final boolean ENABLE_INTRUDERS = false;
+ protected static final boolean ENABLE_HEADS_UP = true;
+ // scores above this threshold should be displayed in heads up mode.
+ private static final int INTERRUPTION_THRESHOLD = 10;
// Should match the value in PhoneWindowManager
public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
public static final int EXPANDED_LEAVE_ALONE = -10000;
public static final int EXPANDED_FULL_OPEN = -10001;
+ private static final String SETTING_HEADS_UP = "heads_up_enabled";
protected CommandQueue mCommandQueue;
protected IStatusBarService mBarService;
@@ -113,7 +108,7 @@
protected NotificationData mNotificationData = new NotificationData();
protected NotificationRowLayout mPile;
- protected StatusBarNotification mCurrentlyIntrudingNotification;
+ protected StatusBarNotification mCurrentlyInterruptingNotification;
// used to notify status bar for suppressing notification LED
protected boolean mPanelSlightlyVisible;
@@ -127,6 +122,11 @@
protected int mLayoutDirection;
private Locale mLocale;
+ protected boolean mUseHeadsUp = true;
+
+ protected IDreamManager mDreamManager;
+ KeyguardManager mKeyguardManager;
+ PowerManager mPowerManager;
// UI-specific methods
@@ -144,6 +144,8 @@
private boolean mDeviceProvisioned = false;
+ private RecentsComponent mRecents;
+
public IStatusBarService getStatusBarService() {
return mBarService;
}
@@ -164,11 +166,24 @@
}
};
+ final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mUseHeadsUp = ENABLE_HEADS_UP && 0 != Settings.Global.getInt(
+ mContext.getContentResolver(), SETTING_HEADS_UP, 0);
+ Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+ if (!mUseHeadsUp) {
+ Log.d(TAG, "dismissing any existing heads up notification on disable event");
+ mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
+ }
+ }
+ };
+
private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
@Override
public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
if (DEBUG) {
- Slog.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
+ Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
}
final boolean isActivity = pendingIntent.isActivity();
if (isActivity) {
@@ -196,19 +211,43 @@
}
};
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+ userSwitched(mCurrentUserId);
+ }
+ }
+ };
+
public void start() {
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
mDisplay = mWindowManager.getDefaultDisplay();
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
+ mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+
mProvisioningObserver.onChange(false); // set up
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
mProvisioningObserver);
+ mHeadsUpObserver.onChange(false); // set up
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(SETTING_HEADS_UP), true,
+ mHeadsUpObserver);
+
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mRecents = getComponent(RecentsComponent.class);
+
mLocale = mContext.getResources().getConfiguration().locale;
mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
@@ -259,7 +298,7 @@
}
if (DEBUG) {
- Slog.d(TAG, String.format(
+ Log.d(TAG, String.format(
"init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
iconList.size(),
switches[0],
@@ -273,16 +312,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (true) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house");
- userSwitched(mCurrentUserId);
- }
- }}, filter);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
mLocale = mContext.getResources().getConfiguration().locale;
}
@@ -295,7 +325,7 @@
final int thisUserId = mCurrentUserId;
final int notificationUserId = n.getUserId();
if (DEBUG && MULTIUSER_DEBUG) {
- Slog.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
+ Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
n, thisUserId, notificationUserId));
}
return notificationUserId == UserHandle.USER_ALL
@@ -348,7 +378,7 @@
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0);
version = info.targetSdkVersion;
} catch (NameNotFoundException ex) {
- Slog.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
+ Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
}
if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
content.setBackgroundResource(R.drawable.notification_row_legacy_bg);
@@ -402,7 +432,7 @@
}
}
- public void dismissIntruder() {
+ public void dismissHeadsUp() {
// pass
}
@@ -441,9 +471,6 @@
mHandler.sendEmptyMessage(msg);
}
- protected abstract WindowManager.LayoutParams getRecentsLayoutParams(
- LayoutParams layoutParams);
-
protected abstract WindowManager.LayoutParams getSearchLayoutParams(
LayoutParams layoutParams);
@@ -487,141 +514,6 @@
protected abstract View getStatusBarView();
- protected void toggleRecentsActivity() {
- try {
-
- TaskDescription firstTask = RecentTasksLoader.getInstance(mContext).getFirstTask();
-
- Intent intent = new Intent(RecentsActivity.TOGGLE_RECENTS_INTENT);
- intent.setClassName("com.android.systemui",
- "com.android.systemui.recent.RecentsActivity");
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-
- if (firstTask == null) {
- if (RecentsActivity.forceOpaqueBackground(mContext)) {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_from_launcher_enter,
- R.anim.recents_launch_from_launcher_exit);
- mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
- UserHandle.USER_CURRENT));
- } else {
- // The correct window animation will be applied via the activity's style
- mContext.startActivityAsUser(intent, new UserHandle(
- UserHandle.USER_CURRENT));
- }
-
- } else {
- Bitmap first = firstTask.getThumbnail();
- final Resources res = mContext.getResources();
-
- float thumbWidth = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
- float thumbHeight = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height);
- if (first == null) {
- throw new RuntimeException("Recents thumbnail is null");
- }
- if (first.getWidth() != thumbWidth || first.getHeight() != thumbHeight) {
- first = Bitmap.createScaledBitmap(first, (int) thumbWidth, (int) thumbHeight,
- true);
- if (first == null) {
- throw new RuntimeException("Recents thumbnail is null");
- }
- }
-
-
- DisplayMetrics dm = new DisplayMetrics();
- mDisplay.getMetrics(dm);
- // calculate it here, but consider moving it elsewhere
- // first, determine which orientation you're in.
- // todo: move the system_bar layouts to sw600dp ?
- final Configuration config = res.getConfiguration();
- int x, y;
-
- if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
- float appLabelLeftMargin = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_app_label_left_margin);
- float appLabelWidth = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_app_label_width);
- float thumbLeftMargin = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_left_margin);
- float thumbBgPadding = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_bg_padding);
-
- float width = appLabelLeftMargin +
- +appLabelWidth
- + thumbLeftMargin
- + thumbWidth
- + 2 * thumbBgPadding;
-
- x = (int) ((dm.widthPixels - width) / 2f + appLabelLeftMargin + appLabelWidth
- + thumbBgPadding + thumbLeftMargin);
- y = (int) (dm.heightPixels
- - res.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height) - thumbBgPadding);
- if (mLayoutDirection == View.LAYOUT_DIRECTION_RTL) {
- x = dm.widthPixels - x - res
- .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
- }
-
- } else { // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- float thumbTopMargin = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_top_margin);
- float thumbBgPadding = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_bg_padding);
- float textPadding = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_text_description_padding);
- float labelTextSize = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_app_label_text_size);
- Paint p = new Paint();
- p.setTextSize(labelTextSize);
- float labelTextHeight = p.getFontMetricsInt().bottom
- - p.getFontMetricsInt().top;
- float descriptionTextSize = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_app_description_text_size);
- p.setTextSize(descriptionTextSize);
- float descriptionTextHeight = p.getFontMetricsInt().bottom
- - p.getFontMetricsInt().top;
-
- float statusBarHeight = res
- .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
- float recentsItemTopPadding = statusBarHeight;
-
- float height = thumbTopMargin
- + thumbHeight
- + 2 * thumbBgPadding + textPadding + labelTextHeight
- + recentsItemTopPadding + textPadding + descriptionTextHeight;
- float recentsItemRightPadding = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_item_padding);
- float recentsScrollViewRightPadding = res
- .getDimensionPixelSize(R.dimen.status_bar_recents_right_glow_margin);
- x = (int) (dm.widthPixels - res
- .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width)
- - thumbBgPadding - recentsItemRightPadding - recentsScrollViewRightPadding);
- y = (int) ((dm.heightPixels - statusBarHeight - height) / 2f + thumbTopMargin
- + recentsItemTopPadding + thumbBgPadding + statusBarHeight);
- }
-
- ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation(
- getStatusBarView(),
- first, x, y,
- new ActivityOptions.OnAnimationStartedListener() {
- public void onAnimationStarted() {
- Intent intent = new Intent(RecentsActivity.WINDOW_ANIMATION_START_INTENT);
- intent.setPackage("com.android.systemui");
- mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
- }
- });
- intent.putExtra(RecentsActivity.WAITING_FOR_WINDOW_ANIMATION_PARAM, true);
- mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
- UserHandle.USER_CURRENT));
- }
- return;
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "Failed to launch RecentAppsIntent", e);
- }
- }
-
protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
// additional optimization when we have software system buttons - start loading the recent
// tasks on touch down
@@ -642,24 +534,28 @@
}
};
- protected void preloadRecentTasksList() {
- if (DEBUG) Slog.d(TAG, "preloading recents");
- Intent intent = new Intent(RecentsActivity.PRELOAD_INTENT);
- intent.setClassName("com.android.systemui",
- "com.android.systemui.recent.RecentsPreloadReceiver");
- mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ protected void toggleRecentsActivity() {
+ if (mRecents != null) {
+ mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
+ }
+ }
- RecentTasksLoader.getInstance(mContext).preloadFirstTask();
+ protected void preloadRecentTasksList() {
+ if (mRecents != null) {
+ mRecents.preloadRecentTasksList();
+ }
}
protected void cancelPreloadingRecentTasksList() {
- if (DEBUG) Slog.d(TAG, "cancel preloading recents");
- Intent intent = new Intent(RecentsActivity.CANCEL_PRELOAD_INTENT);
- intent.setClassName("com.android.systemui",
- "com.android.systemui.recent.RecentsPreloadReceiver");
- mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ if (mRecents != null) {
+ mRecents.cancelPreloadingRecentTasksList();
+ }
+ }
- RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
+ protected void closeRecents() {
+ if (mRecents != null) {
+ mRecents.closeRecents();
+ }
}
protected class H extends Handler {
@@ -667,14 +563,10 @@
Intent intent;
switch (m.what) {
case MSG_TOGGLE_RECENTS_PANEL:
- if (DEBUG) Slog.d(TAG, "toggle recents panel");
toggleRecentsActivity();
break;
case MSG_CLOSE_RECENTS_PANEL:
- if (DEBUG) Slog.d(TAG, "closing recents panel");
- intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT);
- intent.setPackage("com.android.systemui");
- mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ closeRecents();
break;
case MSG_PRELOAD_RECENT_APPS:
preloadRecentTasksList();
@@ -683,13 +575,13 @@
cancelPreloadingRecentTasksList();
break;
case MSG_OPEN_SEARCH_PANEL:
- if (DEBUG) Slog.d(TAG, "opening search panel");
+ if (DEBUG) Log.d(TAG, "opening search panel");
if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
mSearchPanelView.show(true, true);
}
break;
case MSG_CLOSE_SEARCH_PANEL:
- if (DEBUG) Slog.d(TAG, "closing search panel");
+ if (DEBUG) Log.d(TAG, "closing search panel");
if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
mSearchPanelView.show(false, true);
}
@@ -776,7 +668,7 @@
}
catch (RuntimeException e) {
final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
- Slog.e(TAG, "couldn't inflate view for notification " + ident, e);
+ Log.e(TAG, "couldn't inflate view for notification " + ident, e);
return false;
}
@@ -819,13 +711,13 @@
return new NotificationClicker(intent, pkg, tag, id);
}
- private class NotificationClicker implements View.OnClickListener {
+ protected class NotificationClicker implements View.OnClickListener {
private PendingIntent mIntent;
private String mPkg;
private String mTag;
private int mId;
- NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
+ public NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
mIntent = intent;
mPkg = pkg;
mTag = tag;
@@ -855,7 +747,7 @@
mIntent.send(mContext, 0, overlay);
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here. Just log the exception message.
- Slog.w(TAG, "Sending contentIntent failed: " + e);
+ Log.w(TAG, "Sending contentIntent failed: " + e);
}
KeyguardManager kgm =
@@ -872,9 +764,6 @@
// close the shade if it was open
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
visibilityChanged(false);
-
- // If this click was on the intruder alert, hide that instead
-// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
}
}
/**
@@ -913,7 +802,7 @@
protected StatusBarNotification removeNotificationViews(IBinder key) {
NotificationData.Entry entry = mNotificationData.remove(key);
if (entry == null) {
- Slog.w(TAG, "removeNotification for unknown key: " + key);
+ Log.w(TAG, "removeNotification for unknown key: " + key);
return null;
}
// Remove the expanded view.
@@ -928,7 +817,7 @@
protected StatusBarIconView addNotificationViews(IBinder key,
StatusBarNotification notification) {
if (DEBUG) {
- Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
+ Log.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
}
// Construct the icon.
final StatusBarIconView iconView = new StatusBarIconView(mContext,
@@ -957,7 +846,7 @@
// Add the expanded view and icon.
int pos = mNotificationData.add(entry);
if (DEBUG) {
- Slog.d(TAG, "addNotificationViews: added at " + pos);
+ Log.d(TAG, "addNotificationViews: added at " + pos);
}
updateExpansionStates();
updateNotificationIcons();
@@ -970,10 +859,10 @@
mContext.getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
ViewGroup.LayoutParams lp = entry.row.getLayoutParams();
if (entry.expandable() && expand) {
- if (DEBUG) Slog.d(TAG, "setting expanded row height to WRAP_CONTENT");
+ if (DEBUG) Log.d(TAG, "setting expanded row height to WRAP_CONTENT");
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
} else {
- if (DEBUG) Slog.d(TAG, "setting collapsed row height to " + rowHeight);
+ if (DEBUG) Log.d(TAG, "setting collapsed row height to " + rowHeight);
lp.height = rowHeight;
}
entry.row.setLayoutParams(lp);
@@ -986,18 +875,18 @@
NotificationData.Entry entry = mNotificationData.get(i);
if (!entry.userLocked()) {
if (i == (N-1)) {
- if (DEBUG) Slog.d(TAG, "expanding top notification at " + i);
+ if (DEBUG) Log.d(TAG, "expanding top notification at " + i);
expandView(entry, true);
} else {
if (!entry.userExpanded()) {
- if (DEBUG) Slog.d(TAG, "collapsing notification at " + i);
+ if (DEBUG) Log.d(TAG, "collapsing notification at " + i);
expandView(entry, false);
} else {
- if (DEBUG) Slog.d(TAG, "ignoring user-modified notification at " + i);
+ if (DEBUG) Log.d(TAG, "ignoring user-modified notification at " + i);
}
}
} else {
- if (DEBUG) Slog.d(TAG, "ignoring notification being held by user at " + i);
+ if (DEBUG) Log.d(TAG, "ignoring notification being held by user at " + i);
}
}
}
@@ -1015,11 +904,11 @@
}
public void updateNotification(IBinder key, StatusBarNotification notification) {
- if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
+ if (DEBUG) Log.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
if (oldEntry == null) {
- Slog.w(TAG, "updateNotification for unknown key: " + key);
+ Log.w(TAG, "updateNotification for unknown key: " + key);
return;
}
@@ -1032,13 +921,13 @@
final RemoteViews bigContentView = notification.getNotification().bigContentView;
if (DEBUG) {
- Slog.d(TAG, "old notification: when=" + oldNotification.getNotification().when
+ Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
+ " ongoing=" + oldNotification.isOngoing()
+ " expanded=" + oldEntry.expanded
+ " contentView=" + oldContentView
+ " bigContentView=" + oldBigContentView
+ " rowParent=" + oldEntry.row.getParent());
- Slog.d(TAG, "new notification: when=" + notification.getNotification().when
+ Log.d(TAG, "new notification: when=" + notification.getNotification().when
+ " ongoing=" + oldNotification.isOngoing()
+ " contentView=" + contentView
+ " bigContentView=" + bigContentView);
@@ -1071,7 +960,7 @@
oldEntry.notification.getNotification().tickerText);
boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
if (contentsUnchanged && bigContentsUnchanged && (orderUnchanged || isTopAnyway)) {
- if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
+ if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
oldEntry.notification = notification;
try {
// Reapply the RemoteViews
@@ -1102,15 +991,15 @@
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
- Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+ Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
removeNotificationViews(key);
addNotificationViews(key, notification);
}
} else {
- if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key);
- if (DEBUG) Slog.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
- if (DEBUG) Slog.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
- if (DEBUG) Slog.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
+ if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
+ if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
+ if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
+ if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
final boolean wasExpanded = oldEntry.userExpanded();
removeNotificationViews(key);
addNotificationViews(key, notification);
@@ -1127,7 +1016,7 @@
// Is this for you?
boolean isForCurrentUser = notificationIsForCurrentUser(notification);
- if (DEBUG) Slog.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
+ if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
// Restart the ticker if it's still running
if (updateTicker && isForCurrentUser) {
@@ -1139,18 +1028,30 @@
setAreThereNotifications();
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- // See if we need to update the intruder.
- if (ENABLE_INTRUDERS && oldNotification == mCurrentlyIntrudingNotification) {
- if (DEBUG) Slog.d(TAG, "updating the current intruder:" + notification);
+ // See if we need to update the heads up.
+ if (ENABLE_HEADS_UP && oldNotification == mCurrentlyInterruptingNotification) {
+ if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification);
// XXX: this is a hack for Alarms. The real implementation will need to *update*
- // the intruder.
- if (notification.getNotification().fullScreenIntent == null) { // TODO(dsandler): consistent logic with add()
- if (DEBUG) Slog.d(TAG, "no longer intrudes!");
- mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+ // the heads up.
+ if (!shouldInterrupt(notification)) {
+ if (DEBUG) Log.d(TAG, "no longer interrupts!");
+ mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
}
}
}
+ protected boolean shouldInterrupt(StatusBarNotification notification) {
+ boolean interrupt = notification.getNotification().fullScreenIntent == null
+ && notification.getScore() >= INTERRUPTION_THRESHOLD
+ && mPowerManager.isScreenOn() && !mKeyguardManager.isKeyguardLocked();
+ try {
+ interrupt = interrupt && !mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.d(TAG, "failed to query dream manager", e);
+ }
+ return interrupt;
+ }
+
// Q: What kinds of notifications should show during setup?
// A: Almost none! Only things coming from the system (package is "android") that also
// have special "kind" tags marking them as relevant for setup (see below).
@@ -1172,4 +1073,19 @@
KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
return km.inKeyguardRestrictedInputMode();
}
+
+ public void suspendAutohide() {
+ // hook for subclasses
+ }
+
+ public void resumeAutohide() {
+ // hook for subclasses
+ }
+
+ public void destroy() {
+ if (mSearchPanelView != null) {
+ mWindowManager.removeViewImmediate(mSearchPanelView);
+ }
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index cbbaab3..43992c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -19,8 +19,8 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
-
import android.service.notification.StatusBarNotification;
+
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 3ac1bcf..9648b11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -57,8 +57,8 @@
final float sourceX = mTempPoint[0];
final float sourceY = mTempPoint[1];
-
- switch (event.getAction()) {
+ final int action = event.getAction();
+ switch (action) {
case MotionEvent.ACTION_DOWN:
mPanelShowing = mDelegateView.getVisibility() == View.VISIBLE;
mDownPoint[0] = event.getX();
@@ -71,7 +71,7 @@
return false;
}
- if (!mPanelShowing && event.getAction() == MotionEvent.ACTION_MOVE) {
+ if (!mPanelShowing && action == MotionEvent.ACTION_MOVE) {
final int historySize = event.getHistorySize();
for (int k = 0; k < historySize + 1; k++) {
float x = k < historySize ? event.getHistoricalX(k) : event.getX();
@@ -85,6 +85,12 @@
}
}
+ if (action == MotionEvent.ACTION_DOWN) {
+ mBar.suspendAutohide();
+ } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ mBar.resumeAutohide();
+ }
+
mDelegateView.getLocationOnScreen(mTempPoint);
final float delegateX = mTempPoint[0];
final float delegateY = mTempPoint[1];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DoNotDisturb.java b/packages/SystemUI/src/com/android/systemui/statusbar/DoNotDisturb.java
deleted file mode 100644
index 9e44e71..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DoNotDisturb.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import android.app.StatusBarManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Slog;
-
-import com.android.systemui.statusbar.policy.Prefs;
-
-public class DoNotDisturb implements SharedPreferences.OnSharedPreferenceChangeListener {
- private Context mContext;
- private StatusBarManager mStatusBar;
- SharedPreferences mPrefs;
- private boolean mDoNotDisturb;
-
- public DoNotDisturb(Context context) {
- mContext = context;
- mStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
- mPrefs = Prefs.read(context);
- mPrefs.registerOnSharedPreferenceChangeListener(this);
- mDoNotDisturb = mPrefs.getBoolean(Prefs.DO_NOT_DISTURB_PREF, Prefs.DO_NOT_DISTURB_DEFAULT);
- updateDisableRecord();
- }
-
- public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
- final boolean val = prefs.getBoolean(Prefs.DO_NOT_DISTURB_PREF,
- Prefs.DO_NOT_DISTURB_DEFAULT);
- if (val != mDoNotDisturb) {
- mDoNotDisturb = val;
- updateDisableRecord();
- }
- }
-
- private void updateDisableRecord() {
- final int disabled = StatusBarManager.DISABLE_NOTIFICATION_ICONS
- | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
- | StatusBarManager.DISABLE_NOTIFICATION_TICKER;
- mStatusBar.disable(mDoNotDisturb ? disabled : 0);
- }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
index 0f894a1..f2adaf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
@@ -16,6 +16,12 @@
package com.android.systemui.statusbar;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.MotionEvent;
+
import java.io.BufferedWriter;
import java.io.FileDescriptor;
import java.io.FileWriter;
@@ -24,12 +30,6 @@
import java.util.HashSet;
import java.util.LinkedList;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.view.MotionEvent;
-
/**
* Convenience class for capturing gestures for later analysis.
*/
@@ -46,7 +46,7 @@
public MotionEvent event;
public MotionEventRecord(long when, MotionEvent event) {
this.time = when;
- this.event = event.copy();
+ this.event = MotionEvent.obtain(event);
}
String actionName(int action) {
switch (action) {
@@ -101,7 +101,7 @@
mDownTime = ev.getDownTime();
} else {
if (mDownTime != ev.getDownTime()) {
- Slog.w(TAG, "Assertion failure in GestureRecorder: event downTime ("
+ Log.w(TAG, "Assertion failure in GestureRecorder: event downTime ("
+ev.getDownTime()+") does not match gesture downTime ("+mDownTime+")");
}
}
@@ -237,10 +237,10 @@
mGestures.add(mCurrentGesture);
}
if (DEBUG) {
- Slog.v(TAG, String.format("Wrote %d complete gestures to %s", mLastSaveLen, mLogfile));
+ Log.v(TAG, String.format("Wrote %d complete gestures to %s", mLastSaveLen, mLogfile));
}
} catch (IOException e) {
- Slog.e(TAG, String.format("Couldn't write gestures to %s", mLogfile), e);
+ Log.e(TAG, String.format("Couldn't write gestures to %s", mLogfile), e);
mLastSaveLen = -1;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 2c7a2a8..673ca6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -16,15 +16,15 @@
package com.android.systemui.statusbar;
-import android.service.notification.StatusBarNotification;
import android.os.IBinder;
+import android.service.notification.StatusBarNotification;
import android.view.View;
import android.widget.ImageView;
import com.android.systemui.R;
-import java.util.Comparator;
import java.util.ArrayList;
+import java.util.Comparator;
/**
* The list of currently displaying notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java b/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java
deleted file mode 100644
index 735ee25..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.CompoundButton;
-
-import com.android.systemui.statusbar.policy.AutoRotateController;
-
-public class RotationToggle extends CompoundButton
- implements AutoRotateController.RotationLockCallbacks {
- private AutoRotateController mRotater;
-
- public RotationToggle(Context context) {
- super(context);
- }
-
- public RotationToggle(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public RotationToggle(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mRotater = new AutoRotateController(getContext(), this, this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mRotater != null) {
- mRotater.release();
- mRotater = null;
- }
- }
-
- @Override
- public void setRotationLockControlVisibility(boolean show) {
- setVisibility(show ? VISIBLE : GONE);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java
new file mode 100644
index 0000000..aea9ec6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * Manages a persistent connection to a service component defined in a secure setting.
+ *
+ * <p>If a valid service component is specified in the secure setting, starts it up and keeps it
+ * running; handling setting changes, package updates, component disabling, and unexpected
+ * process termination.
+ *
+ * <p>Clients can listen for important events using the supplied {@link Callbacks}.
+ */
+public class ServiceMonitor {
+ private static final int RECHECK_DELAY = 2000;
+ private static final int WAIT_FOR_STOP = 500;
+
+ public interface Callbacks {
+ /** The service does not exist or failed to bind */
+ void onNoService();
+ /** The service is about to start, this is a chance to perform cleanup and
+ * delay the start if necessary */
+ long onServiceStartAttempt();
+ }
+
+ // internal handler + messages used to serialize access to internal state
+ public static final int MSG_START_SERVICE = 1;
+ public static final int MSG_CONTINUE_START_SERVICE = 2;
+ public static final int MSG_STOP_SERVICE = 3;
+ public static final int MSG_PACKAGE_INTENT = 4;
+ public static final int MSG_CHECK_BOUND = 5;
+ public static final int MSG_SERVICE_DISCONNECTED = 6;
+
+ private final Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_START_SERVICE:
+ startService();
+ break;
+ case MSG_CONTINUE_START_SERVICE:
+ continueStartService();
+ break;
+ case MSG_STOP_SERVICE:
+ stopService();
+ break;
+ case MSG_PACKAGE_INTENT:
+ packageIntent((Intent)msg.obj);
+ break;
+ case MSG_CHECK_BOUND:
+ checkBound();
+ break;
+ case MSG_SERVICE_DISCONNECTED:
+ serviceDisconnected((ComponentName)msg.obj);
+ break;
+ }
+ }
+ };
+
+ private final ContentObserver mSettingObserver = new ContentObserver(mHandler) {
+ public void onChange(boolean selfChange) {
+ onChange(selfChange, null);
+ }
+
+ public void onChange(boolean selfChange, Uri uri) {
+ if (mDebug) Log.d(mTag, "onChange selfChange=" + selfChange + " uri=" + uri);
+ ComponentName cn = getComponentNameFromSetting();
+ if (cn == null && mServiceName == null || cn != null && cn.equals(mServiceName)) {
+ if (mDebug) Log.d(mTag, "skipping no-op restart");
+ return;
+ }
+ if (mBound) {
+ mHandler.sendEmptyMessage(MSG_STOP_SERVICE);
+ }
+ mHandler.sendEmptyMessageDelayed(MSG_START_SERVICE, WAIT_FOR_STOP);
+ }
+ };
+
+ private final class SC implements ServiceConnection, IBinder.DeathRecipient {
+ private ComponentName mName;
+ private IBinder mService;
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mDebug) Log.d(mTag, "onServiceConnected name=" + name + " service=" + service);
+ mName = name;
+ mService = service;
+ try {
+ service.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Log.w(mTag, "Error linking to death", e);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (mDebug) Log.d(mTag, "onServiceDisconnected name=" + name);
+ boolean unlinked = mService.unlinkToDeath(this, 0);
+ if (mDebug) Log.d(mTag, " unlinked=" + unlinked);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_DISCONNECTED, mName));
+ }
+
+ public void binderDied() {
+ if (mDebug) Log.d(mTag, "binderDied");
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_DISCONNECTED, mName));
+ }
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String pkg = intent.getData().getSchemeSpecificPart();
+ if (mServiceName != null && mServiceName.getPackageName().equals(pkg)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_PACKAGE_INTENT, intent));
+ }
+ }
+ };
+
+ private final String mTag;
+ private final boolean mDebug;
+
+ private final Context mContext;
+ private final String mSettingKey;
+ private final Callbacks mCallbacks;
+
+ private ComponentName mServiceName;
+ private SC mServiceConnection;
+ private boolean mBound;
+
+ public ServiceMonitor(String ownerTag, boolean debug,
+ Context context, String settingKey, Callbacks callbacks) {
+ mTag = ownerTag + ".ServiceMonitor";
+ mDebug = debug;
+ mContext = context;
+ mSettingKey = settingKey;
+ mCallbacks = callbacks;
+ }
+
+ public void start() {
+ // listen for setting changes
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey),
+ false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL);
+
+ // listen for package/component changes
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ mHandler.sendEmptyMessage(MSG_START_SERVICE);
+ }
+
+ private ComponentName getComponentNameFromSetting() {
+ String cn = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ mSettingKey, UserHandle.USER_CURRENT);
+ return cn == null ? null : ComponentName.unflattenFromString(cn);
+ }
+
+ // everything below is called on the handler
+
+ private void packageIntent(Intent intent) {
+ if (mDebug) Log.d(mTag, "packageIntent intent=" + intent
+ + " extras=" + bundleToString(intent.getExtras()));
+ if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
+ mHandler.sendEmptyMessage(MSG_START_SERVICE);
+ } else if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
+ PackageManager pm = mContext.getPackageManager();
+ boolean serviceEnabled =
+ pm.getApplicationEnabledSetting(mServiceName.getPackageName())
+ != PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ && pm.getComponentEnabledSetting(mServiceName)
+ != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ if (mBound && !serviceEnabled) {
+ stopService();
+ scheduleCheckBound();
+ } else if (!mBound && serviceEnabled) {
+ startService();
+ }
+ }
+ }
+
+ private void stopService() {
+ if (mDebug) Log.d(mTag, "stopService");
+ boolean stopped = mContext.stopService(new Intent().setComponent(mServiceName));
+ if (mDebug) Log.d(mTag, " stopped=" + stopped);
+ mContext.unbindService(mServiceConnection);
+ mBound = false;
+ }
+
+ private void startService() {
+ mServiceName = getComponentNameFromSetting();
+ if (mDebug) Log.d(mTag, "startService mServiceName=" + mServiceName);
+ if (mServiceName == null) {
+ mBound = false;
+ mCallbacks.onNoService();
+ } else {
+ long delay = mCallbacks.onServiceStartAttempt();
+ mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay);
+ }
+ }
+
+ private void continueStartService() {
+ if (mDebug) Log.d(mTag, "continueStartService");
+ Intent intent = new Intent().setComponent(mServiceName);
+ try {
+ mServiceConnection = new SC();
+ mBound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ if (mDebug) Log.d(mTag, "mBound: " + mBound);
+ } catch (Throwable t) {
+ Log.w(mTag, "Error binding to service: " + mServiceName, t);
+ }
+ if (!mBound) {
+ mCallbacks.onNoService();
+ }
+ }
+
+ private void serviceDisconnected(ComponentName serviceName) {
+ if (mDebug) Log.d(mTag, "serviceDisconnected serviceName=" + serviceName
+ + " mServiceName=" + mServiceName);
+ if (serviceName.equals(mServiceName)) {
+ mBound = false;
+ scheduleCheckBound();
+ }
+ }
+
+ private void checkBound() {
+ if (mDebug) Log.d(mTag, "checkBound mBound=" + mBound);
+ if (!mBound) {
+ startService();
+ }
+ }
+
+ private void scheduleCheckBound() {
+ mHandler.removeMessages(MSG_CHECK_BOUND);
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_BOUND, RECHECK_DELAY);
+ }
+
+ private static String bundleToString(Bundle bundle) {
+ if (bundle == null) return null;
+ StringBuilder sb = new StringBuilder('{');
+ for (String key : bundle.keySet()) {
+ if (sb.length() > 1) sb.append(',');
+ Object v = bundle.get(key);
+ v = (v instanceof String[]) ? Arrays.asList((String[]) v) : v;
+ sb.append(key).append('=').append(v);
+ }
+ return sb.append('}').toString();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 46916f7..f17b143e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -18,16 +18,15 @@
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Slog;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import com.android.systemui.statusbar.policy.NetworkController;
-
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.NetworkController;
// Intimately tied to the design of res/layout/signal_cluster_view.xml
public class SignalClusterView
@@ -64,7 +63,7 @@
}
public void setNetworkController(NetworkController nc) {
- if (DEBUG) Slog.d(TAG, "NetworkController=" + nc);
+ if (DEBUG) Log.d(TAG, "NetworkController=" + nc);
mNC = nc;
}
@@ -185,7 +184,7 @@
mWifiGroup.setVisibility(View.GONE);
}
- if (DEBUG) Slog.d(TAG,
+ if (DEBUG) Log.d(TAG,
String.format("wifi: %s sig=%d act=%d",
(mWifiVisible ? "VISIBLE" : "GONE"),
mWifiStrengthId, mWifiActivityId));
@@ -214,7 +213,7 @@
mSpacer.setVisibility(View.GONE);
}
- if (DEBUG) Slog.d(TAG,
+ if (DEBUG) Log.d(TAG,
String.format("mobile: %s sig=%d act=%d typ=%d",
(mMobileVisible ? "VISIBLE" : "GONE"),
mMobileStrengthId, mMobileActivityId, mMobileTypeId));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 39d56a4..8b50a38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -20,25 +20,23 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Slog;
import android.util.Log;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
-import java.text.NumberFormat;
-
import com.android.internal.statusbar.StatusBarIcon;
-
import com.android.systemui.R;
+import java.text.NumberFormat;
+
public class StatusBarIconView extends AnimatedImageView {
private static final String TAG = "StatusBarIconView";
@@ -151,7 +149,7 @@
private boolean updateDrawable(boolean withClear) {
Drawable drawable = getIcon(mIcon);
if (drawable == null) {
- Slog.w(TAG, "No icon for slot " + mSlot);
+ Log.w(TAG, "No icon for slot " + mSlot);
return false;
}
if (withClear) {
@@ -185,7 +183,7 @@
r = context.getPackageManager()
.getResourcesForApplicationAsUser(icon.iconPackage, userId);
} catch (PackageManager.NameNotFoundException ex) {
- Slog.e(TAG, "Icon package not found: " + icon.iconPackage);
+ Log.e(TAG, "Icon package not found: " + icon.iconPackage);
return null;
}
} else {
@@ -199,7 +197,7 @@
try {
return r.getDrawable(icon.iconId);
} catch (RuntimeException e) {
- Slog.w(TAG, "Icon not found in "
+ Log.w(TAG, "Icon not found in "
+ (icon.iconPackage != null ? icon.iconId : "<system>")
+ ": " + Integer.toHexString(icon.iconId));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPanel.java
new file mode 100644
index 0000000..272c321
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPanel.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+public interface StatusBarPanel {
+ public boolean isInContentArea(int x, int y);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
new file mode 100644
index 0000000..05282fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+/**
+ * Ensure a single status bar service implementation is running at all times.
+ *
+ * <p>The implementation either comes from a service component running in a remote process (defined
+ * using a secure setting), else falls back to using the in-process implementation according
+ * to the product config.
+ */
+public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {
+ private static final String TAG = "SystemBars";
+ private static final boolean DEBUG = true;
+ private static final int WAIT_FOR_BARS_TO_DIE = 500;
+
+ // manages the implementation coming from the remote process
+ private ServiceMonitor mServiceMonitor;
+
+ // in-process fallback implementation, per the product config
+ private BaseStatusBar mStatusBar;
+
+ @Override
+ public void start() {
+ if (DEBUG) Log.d(TAG, "start");
+ mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
+ mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
+ mServiceMonitor.start(); // will call onNoService if no remote service is found
+ }
+
+ @Override
+ public void onNoService() {
+ if (DEBUG) Log.d(TAG, "onNoService");
+ createStatusBarFromConfig(); // fallback to using an in-process implementation
+ }
+
+ @Override
+ public long onServiceStartAttempt() {
+ if (DEBUG) Log.d(TAG, "onServiceStartAttempt mStatusBar="+mStatusBar);
+ if (mStatusBar != null) {
+ // tear down the in-process version, we'll recreate it again if needed
+ mStatusBar.destroy();
+ mStatusBar = null;
+ return WAIT_FOR_BARS_TO_DIE;
+ }
+ return 0;
+ }
+
+ private void createStatusBarFromConfig() {
+ if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
+ final String clsName = mContext.getString(R.string.config_statusBarComponent);
+ if (clsName == null || clsName.length() == 0) {
+ throw andLog("No status bar component configured", null);
+ }
+ Class<?> cls = null;
+ try {
+ cls = mContext.getClassLoader().loadClass(clsName);
+ } catch (Throwable t) {
+ throw andLog("Error loading status bar component: " + clsName, t);
+ }
+ try {
+ mStatusBar = (BaseStatusBar) cls.newInstance();
+ } catch (Throwable t) {
+ throw andLog("Error creating status bar component: " + clsName, t);
+ }
+ mStatusBar.mContext = mContext;
+ mStatusBar.mComponents = mComponents;
+ mStatusBar.start();
+ if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
+ }
+
+ private RuntimeException andLog(String msg, Throwable t) {
+ Log.w(TAG, msg, t);
+ throw new RuntimeException(msg, t);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CarrierLabel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CarrierLabel.java
deleted file mode 100644
index 66494e4..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CarrierLabel.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.internal.telephony.TelephonyIntents;
-
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.TimeZone;
-
-import com.android.internal.R;
-
-/**
- * This widget display an analogic clock with two hands for hours and
- * minutes.
- */
-public class CarrierLabel extends TextView {
- private boolean mAttached;
-
- public CarrierLabel(Context context) {
- this(context, null);
- }
-
- public CarrierLabel(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public CarrierLabel(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- updateNetworkName(false, null, false, null);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (!mAttached) {
- mAttached = true;
- IntentFilter filter = new IntentFilter();
- filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
- getContext().registerReceiver(mIntentReceiver, filter, null, getHandler());
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAttached) {
- getContext().unregisterReceiver(mIntentReceiver);
- mAttached = false;
- }
- }
-
- private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (TelephonyIntents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
- updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
- intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
- intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
- intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
- }
- }
- };
-
- void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
- if (false) {
- Slog.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
- + " showPlmn=" + showPlmn + " plmn=" + plmn);
- }
- final String str;
- // match logic in KeyguardStatusViewManager
- final boolean plmnValid = showPlmn && !TextUtils.isEmpty(plmn);
- final boolean spnValid = showSpn && !TextUtils.isEmpty(spn);
- if (plmnValid && spnValid) {
- str = plmn + "|" + spn;
- } else if (plmnValid) {
- str = plmn;
- } else if (spnValid) {
- str = spn;
- } else {
- str = "";
- }
- setText(str);
- }
-
-
-}
-
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CloseDragHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CloseDragHandle.java
deleted file mode 100644
index ee01489..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CloseDragHandle.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.LinearLayout;
-
-
-public class CloseDragHandle extends LinearLayout {
- PhoneStatusBar mService;
-
- public CloseDragHandle(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /**
- * Ensure that, if there is no target under us to receive the touch,
- * that we process it ourself. This makes sure that onInterceptTouchEvent()
- * is always called for the entire gesture.
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- setPressed(true);
- } else {
- mService.interceptTouchEvent(event);
- }
- return true;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return mService.interceptTouchEvent(event)
- ? true : super.onInterceptTouchEvent(event);
- }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java
index 0640282..50ead3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java
@@ -17,16 +17,11 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
-import android.os.Handler;
import android.util.AttributeSet;
-import android.util.Slog;
import android.view.View;
import android.widget.LinearLayout;
-import com.android.internal.statusbar.StatusBarIcon;
-
import com.android.systemui.R;
-import com.android.systemui.statusbar.StatusBarIconView;
public class IconMerger extends LinearLayout {
private static final String TAG = "IconMerger";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 54c4666..9eaee30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -29,26 +29,26 @@
import android.os.Message;
import android.os.ServiceManager;
import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.animation.AccelerateInterpolator;
+import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
-import android.view.View;
import android.view.Surface;
+import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.animation.AccelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DelegateViewHelper;
import com.android.systemui.statusbar.policy.DeadZone;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public class NavigationBarView extends LinearLayout {
final static boolean DEBUG = false;
final static String TAG = "PhoneStatusBar/NavigationBarView";
@@ -95,7 +95,7 @@
final int vh = mCurrentView.getHeight();
if (h != vh || w != vw) {
- Slog.w(TAG, String.format(
+ Log.w(TAG, String.format(
"*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)",
how, w, h, vw, vh));
if (WORKAROUND_INVALID_LAYOUT) {
@@ -318,7 +318,7 @@
mLowProfile = lightsOut;
- if (DEBUG) Slog.d(TAG, "setting lights " + (lightsOut?"out":"on"));
+ if (DEBUG) Log.d(TAG, "setting lights " + (lightsOut?"out":"on"));
final View navButtons = mCurrentView.findViewById(R.id.nav_buttons);
final View lowLights = mCurrentView.findViewById(R.id.lights_out);
@@ -361,7 +361,7 @@
if (hide == mHidden) return;
mHidden = hide;
- Slog.d(TAG,
+ Log.d(TAG,
(hide ? "HIDING" : "SHOWING") + " navigation bar");
// bring up the lights no matter what
@@ -382,6 +382,10 @@
mCurrentView = mRotatedViews[Surface.ROTATION_0];
}
+ public boolean isVertical() {
+ return mVertical;
+ }
+
public void reorient() {
final int rot = mDisplay.getRotation();
for (int i=0; i<4; i++) {
@@ -398,7 +402,7 @@
setMenuVisibility(mShowMenu, true /* force */);
if (DEBUG) {
- Slog.d(TAG, "reorient(): rot=" + mDisplay.getRotation());
+ Log.d(TAG, "reorient(): rot=" + mDisplay.getRotation());
}
setNavigationIconHints(mNavigationIconHints, true);
@@ -412,13 +416,13 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- if (DEBUG) Slog.d(TAG, String.format(
+ if (DEBUG) Log.d(TAG, String.format(
"onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));
final boolean newVertical = w > 0 && h > w;
if (newVertical != mVertical) {
mVertical = newVertical;
- //Slog.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
+ //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
reorient();
}
@@ -429,7 +433,7 @@
/*
@Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
- if (DEBUG) Slog.d(TAG, String.format(
+ if (DEBUG) Log.d(TAG, String.format(
"onLayout: %s (%d,%d,%d,%d)",
changed?"changed":"notchanged", left, top, right, bottom));
super.onLayout(changed, left, top, right, bottom);
@@ -439,7 +443,7 @@
// fails, any touch on the display will fix the layout.
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (DEBUG) Slog.d(TAG, "onInterceptTouchEvent: " + ev.toString());
+ if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString());
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
postCheckForInvalidLayout("touch");
}
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 f33dc20..6be6d4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -22,7 +22,6 @@
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.EventLog;
-import android.util.Slog;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 565a3f2..751d944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -16,21 +16,21 @@
package com.android.systemui.statusbar.phone;
-import java.util.ArrayList;
-
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Slog;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
+import java.util.ArrayList;
+
public class PanelBar extends FrameLayout {
public static final boolean DEBUG = false;
public static final String TAG = PanelBar.class.getSimpleName();
public static final void LOG(String fmt, Object... args) {
if (!DEBUG) return;
- Slog.v(TAG, String.format(fmt, args));
+ Log.v(TAG, String.format(fmt, args));
}
public static final int STATE_CLOSED = 0;
@@ -66,7 +66,7 @@
public void setPanelHolder(PanelHolder ph) {
if (ph == null) {
- Slog.e(TAG, "setPanelHolder: null PanelHolder", new Throwable());
+ Log.e(TAG, "setPanelHolder: null PanelHolder", new Throwable());
return;
}
ph.setBar(this);
@@ -98,7 +98,7 @@
// Allow subclasses to implement enable/disable semantics
if (!panelsEnabled()) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
- Slog.v(TAG, String.format("onTouch: all panels disabled, ignoring touch at (%d,%d)",
+ Log.v(TAG, String.format("onTouch: all panels disabled, ignoring touch at (%d,%d)",
(int) event.getX(), (int) event.getY()));
}
return false;
@@ -109,7 +109,7 @@
final PanelView panel = selectPanelForTouch(event);
if (panel == null) {
// panel is not there, so we'll eat the gesture
- Slog.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
+ Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
(int) event.getX(), (int) event.getY()));
mTouchingPanel = null;
return true;
@@ -119,7 +119,7 @@
(enabled ? "" : " (disabled)"));
if (!enabled) {
// panel is disabled, so we'll eat the gesture
- Slog.v(TAG, String.format(
+ Log.v(TAG, String.format(
"onTouch: panel (%s) is disabled, ignoring touch at (%d,%d)",
panel, (int) event.getX(), (int) event.getY()));
mTouchingPanel = 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 e351429..cf7f9c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -16,24 +16,24 @@
package com.android.systemui.statusbar.phone;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayDeque;
-import java.util.Iterator;
-
import android.animation.ObjectAnimator;
import android.animation.TimeAnimator;
import android.animation.TimeAnimator.TimeListener;
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
-import android.util.Slog;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import com.android.systemui.R;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayDeque;
+import java.util.Iterator;
+
public class PanelView extends FrameLayout {
public static final boolean DEBUG = PanelBar.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
@@ -42,7 +42,7 @@
public final void LOG(String fmt, Object... args) {
if (!DEBUG) return;
- Slog.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
+ Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
public static final boolean BRAKES = false;
@@ -112,7 +112,7 @@
}
public void computeCurrentVelocity(long timebase) {
if (FlingTracker.DEBUG) {
- Slog.v("FlingTracker", "computing velocities for " + mEventBuf.size() + " events");
+ Log.v("FlingTracker", "computing velocities for " + mEventBuf.size() + " events");
}
mVX = mVY = 0;
MotionEventCopy last = null;
@@ -127,7 +127,7 @@
final float dx = (event.x - last.x);
final float dy = (event.y - last.y);
if (FlingTracker.DEBUG) {
- Slog.v("FlingTracker", String.format(" [%d] dx=%.1f dy=%.1f dt=%.0f vx=%.1f vy=%.1f",
+ Log.v("FlingTracker", String.format(" [%d] dx=%.1f dy=%.1f dt=%.0f vx=%.1f vy=%.1f",
i,
dx, dy, dt,
(dx/dt),
@@ -147,7 +147,7 @@
mVY /= totalweight;
} else {
if (DEBUG_NAN) {
- Slog.v("FlingTracker", "computeCurrentVelocity warning: totalweight=0",
+ Log.v("FlingTracker", "computeCurrentVelocity warning: totalweight=0",
new Throwable());
}
// so as not to contaminate the velocities with NaN
@@ -155,13 +155,13 @@
}
if (FlingTracker.DEBUG) {
- Slog.v("FlingTracker", "computed: vx=" + mVX + " vy=" + mVY);
+ Log.v("FlingTracker", "computed: vx=" + mVX + " vy=" + mVY);
}
}
public float getXVelocity() {
if (Float.isNaN(mVX)) {
if (DEBUG_NAN) {
- Slog.v("FlingTracker", "warning: vx=NaN");
+ Log.v("FlingTracker", "warning: vx=NaN");
}
mVX = 0;
}
@@ -170,7 +170,7 @@
public float getYVelocity() {
if (Float.isNaN(mVY)) {
if (DEBUG_NAN) {
- Slog.v("FlingTracker", "warning: vx=NaN");
+ Log.v("FlingTracker", "warning: vx=NaN");
}
mVY = 0;
}
@@ -307,7 +307,7 @@
post(mStopAnimator);
}
} else {
- Slog.v(TAG, "animationTick called with dtms=" + dtms + "; nothing to do (h="
+ Log.v(TAG, "animationTick called with dtms=" + dtms + "; nothing to do (h="
+ mExpandedHeight + " v=" + mVel + ")");
}
}
@@ -547,7 +547,7 @@
if (Float.isNaN(h)) {
// If a NaN gets in here, it will freeze the Animators.
if (DEBUG_NAN) {
- Slog.v(TAG, "setExpandedHeightInternal: warning: h=NaN, using 0 instead",
+ Log.v(TAG, "setExpandedHeightInternal: warning: h=NaN, using 0 instead",
new Throwable());
}
h = 0;
@@ -586,7 +586,7 @@
if (Float.isNaN(frac)) {
// If a NaN gets in here, it will freeze the Animators.
if (DEBUG_NAN) {
- Slog.v(TAG, "setExpandedFraction: frac=NaN, using 0 instead",
+ Log.v(TAG, "setExpandedFraction: frac=NaN, using 0 instead",
new Throwable());
}
frac = 0;
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 7d23e89..fd99f5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -26,7 +26,6 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
-import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -46,21 +45,19 @@
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
+import android.service.notification.StatusBarNotification;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
-import android.util.Slog;
import android.view.Display;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewPropertyAnimator;
@@ -75,7 +72,7 @@
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
-
+import android.widget.Toast;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
@@ -89,12 +86,13 @@
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.DateView;
-import com.android.systemui.statusbar.policy.IntruderAlertView;
+import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NotificationRowLayout;
import com.android.systemui.statusbar.policy.OnSizeChangedListener;
import com.android.systemui.statusbar.policy.Prefs;
+import com.android.systemui.statusbar.policy.RotationLockController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -125,13 +123,18 @@
// 1020-1030 reserved for BaseStatusBar
// will likely move to a resource or other tunable param at some point
- private static final int INTRUDER_ALERT_DECAY_MS = 0; // disabled, was 10000;
+ private static final int HEADS_UP_DECAY_MS = 0; // disabled, was 10000;
private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
+ private static final int STATUS_OR_NAV_OVERLAY =
+ View.STATUS_BAR_OVERLAY | View.NAVIGATION_BAR_OVERLAY;
+ private static final long AUTOHIDE_TIMEOUT_MS = 3000;
+ private static final float TRANSPARENT_ALPHA = 0.7f;
+
// fling gesture tuning parameters, scaled to display density
private float mSelfExpandVelocityPx; // classic value: 2000px/s
private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
@@ -154,6 +157,7 @@
BatteryController mBatteryController;
LocationController mLocationController;
NetworkController mNetworkController;
+ RotationLockController mRotationLockController;
int mNaturalBarHeight = -1;
int mIconSize = -1;
@@ -161,8 +165,6 @@
Display mDisplay;
Point mCurrentDisplaySize = new Point();
- IDreamManager mDreamManager;
-
StatusBarWindowView mStatusBarWindow;
PhoneStatusBarView mStatusBarView;
@@ -222,8 +224,8 @@
// the date view
DateView mDateView;
- // for immersive activities
- private IntruderAlertView mIntruderAlertView;
+ // for heads up notifications
+ private HeadsUpNotificationView mHeadsUpNotificationView;
// on-screen navigation buttons
private NavigationBarView mNavigationBarView = null;
@@ -271,7 +273,7 @@
public void onAnimationEnd(Animator animation) {
// double-check to avoid races
if (mStatusBarContents.getAlpha() == 0) {
- if (DEBUG) Slog.d(TAG, "makeIconsInvisible");
+ if (DEBUG) Log.d(TAG, "makeIconsInvisible");
mStatusBarContents.setVisibility(View.INVISIBLE);
}
}
@@ -287,7 +289,7 @@
Settings.Secure.USER_SETUP_COMPLETE,
0 /*default */,
mCurrentUserId);
- if (MULTIUSER_DEBUG) Slog.d(TAG, String.format("User setup changed: " +
+ if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
"selfChange=%s userSetup=%s mUserSetup=%s",
selfChange, userSetup, mUserSetup));
if (mSettingsButton != null && mHasFlipSettings) {
@@ -304,19 +306,57 @@
}
};
+ private Toast mHideybarConfirmation;
+ private boolean mHideybarConfirmationDismissed;
+
+ private final View.OnTouchListener mDismissHideybarConfirmationOnTouchOutside =
+ new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
+ dismissHideybarConfirmation();
+ }
+ return false;
+ }
+ };
+
+ private final Runnable mHideybarConfirmationAction = new Runnable() {
+ @Override
+ public void run() {
+ if (mHideybarConfirmation != null) {
+ final boolean isGloballyConfirmed = Prefs.read(mContext)
+ .getBoolean(Prefs.HIDEYBAR_CONFIRMED, false);
+ if (!isGloballyConfirmed) {
+ // user pressed button, consider this a confirmation
+ Prefs.edit(mContext)
+ .putBoolean(Prefs.HIDEYBAR_CONFIRMED, true)
+ .apply();
+ }
+ dismissHideybarConfirmation();
+ }
+ }
+ };
+
+ private boolean mAutohideSuspended;
+
+ private final Runnable mAutohide = new Runnable() {
+ @Override
+ public void run() {
+ int requested = mSystemUiVisibility & ~STATUS_OR_NAV_OVERLAY;
+ notifyUiVisibilityChanged(requested);
+ }};
+
@Override
public void start() {
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
-
- mDreamManager = IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE));
+ mDisplay.getSize(mCurrentDisplaySize);
super.start(); // calls createAndAddWindows()
addNavigationBar();
- if (ENABLE_INTRUDERS) addIntruderView();
+ if (ENABLE_HEADS_UP) addHeadsUpView();
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext);
@@ -375,10 +415,11 @@
mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
R.color.notification_panel_solid_background)));
}
- if (ENABLE_INTRUDERS) {
- mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
- mIntruderAlertView.setVisibility(View.GONE);
- mIntruderAlertView.setBar(this);
+ if (ENABLE_HEADS_UP) {
+ mHeadsUpNotificationView =
+ (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
+ mHeadsUpNotificationView.setVisibility(View.GONE);
+ mHeadsUpNotificationView.setBar(this);
}
if (MULTIUSER_DEBUG) {
mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);
@@ -389,7 +430,7 @@
try {
boolean showNav = mWindowManagerService.hasNavigationBar();
- if (DEBUG) Slog.v(TAG, "hasNavigationBar=" + showNav);
+ if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
mNavigationBarView =
(NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
@@ -489,6 +530,7 @@
mBatteryController.addIconView((ImageView)mStatusBarView.findViewById(R.id.battery));
mNetworkController = new NetworkController(mContext);
mBluetoothController = new BluetoothController(mContext);
+ mRotationLockController = new RotationLockController(mContext);
final SignalClusterView signalCluster =
(SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
@@ -515,7 +557,7 @@
mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
mShowCarrierInPanel = (mCarrierLabel != null);
- if (DEBUG) Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
+ if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
if (mShowCarrierInPanel) {
mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
@@ -581,7 +623,7 @@
mQS.setService(this);
mQS.setBar(mStatusBarView);
mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
- mLocationController);
+ mLocationController, mRotationLockController);
} else {
mQS = null; // fly away, be free
}
@@ -620,31 +662,6 @@
}
@Override
- protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
- boolean opaque = false;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- layoutParams.width,
- layoutParams.height,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
- if (ActivityManager.isHighEndGfx()) {
- lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- } else {
- lp.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
- lp.dimAmount = 0.75f;
- }
- lp.gravity = Gravity.BOTTOM | Gravity.START;
- lp.setTitle("RecentsPanel");
- lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
- lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
- | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
- return lp;
- }
-
- @Override
protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
boolean opaque = false;
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -670,7 +687,6 @@
@Override
protected void updateSearchPanel() {
super.updateSearchPanel();
- mSearchPanelView.setStatusBarView(mNavigationBarView);
mNavigationBarView.setDelegateView(mSearchPanelView);
}
@@ -767,7 +783,7 @@
// For small-screen devices (read: phones) that lack hardware navigation buttons
private void addNavigationBar() {
- if (DEBUG) Slog.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
+ if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
if (mNavigationBarView == null) return;
prepareNavigationBarView();
@@ -798,7 +814,7 @@
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.OPAQUE);
+ PixelFormat.TRANSLUCENT);
// this will allow the navbar to run in an overlay on devices that support this
if (ActivityManager.isHighEndGfx()) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
@@ -809,7 +825,7 @@
return lp;
}
- private void addIntruderView() {
+ private void addHeadsUpView() {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
@@ -823,11 +839,11 @@
PixelFormat.TRANSLUCENT);
lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
//lp.y += height * 1.5; // FIXME
- lp.setTitle("IntruderAlert");
+ lp.setTitle("Heads Up");
lp.packageName = mContext.getPackageName();
- lp.windowAnimations = R.style.Animation_StatusBar_IntruderAlert;
+ lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
- mWindowManager.addView(mIntruderAlertView, lp);
+ mWindowManager.addView(mHeadsUpNotificationView, lp);
}
public void refreshAllStatusBarIcons() {
@@ -846,7 +862,7 @@
}
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
- if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ " icon=" + icon);
StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
view.set(icon);
@@ -855,74 +871,53 @@
public void updateIcon(String slot, int index, int viewIndex,
StatusBarIcon old, StatusBarIcon icon) {
- if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ " old=" + old + " icon=" + icon);
StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
view.set(icon);
}
public void removeIcon(String slot, int index, int viewIndex) {
- if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
+ if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
mStatusIcons.removeViewAt(viewIndex);
}
public void addNotification(IBinder key, StatusBarNotification notification) {
- if (DEBUG) Slog.d(TAG, "addNotification score=" + notification.getScore());
+ if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
StatusBarIconView iconView = addNotificationViews(key, notification);
if (iconView == null) return;
- boolean immersive = false;
- try {
- immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
- if (DEBUG) {
- Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
- }
- } catch (RemoteException ex) {
- }
-
- /*
- * DISABLED due to missing API
- if (ENABLE_INTRUDERS && (
- // TODO(dsandler): Only if the screen is on
- notification.notification.intruderView != null)) {
- Slog.d(TAG, "Presenting high-priority notification");
- // special new transient ticker mode
- // 1. Populate mIntruderAlertView
-
- if (notification.notification.intruderView == null) {
- Slog.e(TAG, notification.notification.toString() + " wanted to intrude but intruderView was null");
- return;
- }
+ if (mUseHeadsUp && shouldInterrupt(notification)) {
+ if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
+ // 1. Populate mHeadsUpNotificationView
// bind the click event to the content area
- PendingIntent contentIntent = notification.notification.contentIntent;
+ PendingIntent contentIntent = notification.getNotification().contentIntent;
final View.OnClickListener listener = (contentIntent != null)
? new NotificationClicker(contentIntent,
- notification.pkg, notification.tag, notification.id)
+ notification.getPackageName(), notification.getTag(), notification.getId())
: null;
- mIntruderAlertView.applyIntruderContent(notification.notification.intruderView, listener);
+ if (mHeadsUpNotificationView.applyContent(notification.getNotification(), listener)) {
- mCurrentlyIntrudingNotification = notification;
+ mCurrentlyInterruptingNotification = notification;
- // 2. Animate mIntruderAlertView in
- mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
+ // 2. Animate mHeadsUpNotificationView in
+ mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
- // 3. Set alarm to age the notification off (TODO)
- mHandler.removeMessages(MSG_HIDE_INTRUDER);
- if (INTRUDER_ALERT_DECAY_MS > 0) {
- mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
+ // 3. Set alarm to age the notification off (TODO)
+ mHandler.removeMessages(MSG_HIDE_HEADS_UP);
+ if (HEADS_UP_DECAY_MS > 0) {
+ mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, HEADS_UP_DECAY_MS);
+ }
}
- } else
- */
-
- if (notification.getNotification().fullScreenIntent != null) {
+ } else if (notification.getNotification().fullScreenIntent != null) {
// Stop screensaver if the notification has a full-screen intent.
// (like an incoming phone call)
awakenDreams();
// not immersive & a full-screen alert should be shown
- if (DEBUG) Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
+ if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
notification.getNotification().fullScreenIntent.send();
} catch (PendingIntent.CanceledException e) {
@@ -930,8 +925,8 @@
} else {
// usual case: status bar visible & not immersive
- // show the ticker if there isn't an intruder too
- if (mCurrentlyIntrudingNotification == null) {
+ // show the ticker if there isn't already a heads up
+ if (mCurrentlyInterruptingNotification == null) {
tick(null, notification, true);
}
}
@@ -943,7 +938,7 @@
public void removeNotification(IBinder key) {
StatusBarNotification old = removeNotificationViews(key);
- if (SPEW) Slog.d(TAG, "removeNotification key=" + key + " old=" + old);
+ if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
if (old != null) {
// Cancel the ticker if it's still running
@@ -952,8 +947,8 @@
// Recalculate the position of the sliding windows and the titles.
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- if (ENABLE_INTRUDERS && old == mCurrentlyIntrudingNotification) {
- mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+ if (ENABLE_HEADS_UP && old == mCurrentlyInterruptingNotification) {
+ mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
}
if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0) {
@@ -1048,7 +1043,7 @@
int N = mNotificationData.size();
if (DEBUG) {
- Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
+ Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
}
ArrayList<View> toShow = new ArrayList<View>();
@@ -1088,7 +1083,7 @@
// The idea here is to only show the carrier label when there is enough room to see it,
// i.e. when there aren't enough notifications to fill the panel.
if (DEBUG) {
- Slog.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
+ Log.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
mPile.getHeight(), mScrollView.getHeight(), mCarrierLabelHeight));
}
@@ -1101,7 +1096,7 @@
if (force || mCarrierLabelVisible != makeVisible) {
mCarrierLabelVisible = makeVisible;
if (DEBUG) {
- Slog.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
+ Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
}
mCarrierLabel.animate().cancel();
if (makeVisible) {
@@ -1132,7 +1127,7 @@
final boolean clearable = any && mNotificationData.hasClearableItems();
if (DEBUG) {
- Slog.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
+ Log.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
+ " any=" + any + " clearable=" + clearable);
}
@@ -1209,7 +1204,7 @@
mDisabled = state;
if (DEBUG) {
- Slog.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
+ Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
old, state, diff));
}
@@ -1236,7 +1231,7 @@
flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
flagdbg.append(((diff & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
flagdbg.append(">");
- Slog.d(TAG, flagdbg.toString());
+ Log.d(TAG, flagdbg.toString());
if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
mSystemIconArea.animate().cancel();
@@ -1335,12 +1330,12 @@
case MSG_CLOSE_PANELS:
animateCollapsePanels();
break;
- case MSG_SHOW_INTRUDER:
- setIntruderAlertVisibility(true);
+ case MSG_SHOW_HEADS_UP:
+ setHeadsUpVisibility(true);
break;
- case MSG_HIDE_INTRUDER:
- setIntruderAlertVisibility(false);
- mCurrentlyIntrudingNotification = null;
+ case MSG_HIDE_HEADS_UP:
+ setHeadsUpVisibility(false);
+ mCurrentlyInterruptingNotification = null;
break;
}
}
@@ -1359,7 +1354,7 @@
};
void makeExpandedVisible(boolean revealAfterDraw) {
- if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+ if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
if (mExpandedVisible) {
return;
}
@@ -1388,6 +1383,8 @@
}
visibilityChanged(true);
+
+ suspendAutohide();
}
public void animateCollapsePanels() {
@@ -1396,7 +1393,7 @@
public void animateCollapsePanels(int flags) {
if (SPEW) {
- Slog.d(TAG, "animateCollapse():"
+ Log.d(TAG, "animateCollapse():"
+ " mExpandedVisible=" + mExpandedVisible
+ " flags=" + flags);
}
@@ -1464,7 +1461,7 @@
@Override
public void animateExpandNotificationsPanel() {
- if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
+ if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
return ;
}
@@ -1519,7 +1516,7 @@
@Override
public void animateExpandSettingsPanel() {
- if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
+ if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
return;
}
@@ -1618,7 +1615,7 @@
}
void makeExpandedInvisible() {
- if (SPEW) Slog.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
+ if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
+ " mExpandedVisible=" + mExpandedVisible);
if (!mExpandedVisible) {
@@ -1670,6 +1667,9 @@
mPostCollapseCleanup.run();
mPostCollapseCleanup = null;
}
+
+ // Reschedule suspended auto-hide if necessary
+ resumeAutohide();
}
/**
@@ -1786,11 +1786,11 @@
}
if (SPEW) {
- Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+ Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+ mDisabled + " mTracking=" + mTracking);
} else if (CHATTY) {
if (event.getAction() != MotionEvent.ACTION_MOVE) {
- Slog.d(TAG, String.format(
+ Log.d(TAG, String.format(
"panel: %s at (%f, %f) mDisabled=0x%08x",
MotionEvent.actionToString(event.getAction()),
event.getRawX(), event.getRawY(), mDisabled));
@@ -1816,6 +1816,7 @@
hideCling();
}
+ suspendAutohide();
return false;
}
@@ -1839,7 +1840,11 @@
final int oldVal = mSystemUiVisibility;
final int newVal = (oldVal&~mask) | (vis&mask);
final int diff = newVal ^ oldVal;
-
+ if (DEBUG) Log.d(TAG, String.format(
+ "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
+ Integer.toHexString(vis), Integer.toHexString(mask),
+ Integer.toHexString(oldVal), Integer.toHexString(newVal),
+ Integer.toHexString(diff)));
if (diff != 0) {
mSystemUiVisibility = newVal;
@@ -1859,10 +1864,103 @@
setStatusBarLowProfile(lightsOut);
}
- notifyUiVisibilityChanged();
+ boolean sbOverlayChanged = 0 != (diff & View.STATUS_BAR_OVERLAY);
+ boolean nbOverlayChanged = 0 != (diff & View.NAVIGATION_BAR_OVERLAY);
+ if (sbOverlayChanged || nbOverlayChanged) {
+ boolean sbOverlay = 0 != (vis & View.STATUS_BAR_OVERLAY);
+ boolean nbOverlay = 0 != (vis & View.NAVIGATION_BAR_OVERLAY);
+ if (sbOverlayChanged) {
+ setTransparent(mStatusBarView, sbOverlay);
+ }
+ if (nbOverlayChanged) {
+ setTransparent(mNavigationBarView, nbOverlay);
+ }
+ if (sbOverlayChanged && sbOverlay || nbOverlayChanged && nbOverlay) {
+ scheduleAutohide();
+ } else {
+ cancelAutohide();
+ }
+ }
+ if (mNavigationBarView != null) {
+ int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_ALLOW_OVERLAY;
+ boolean oldVisible = (oldVal & flags) == flags;
+ boolean newVisible = (newVal & flags) == flags;
+ if (!oldVisible && newVisible) {
+ mHideybarConfirmationDismissed = false;
+ }
+ setHideybarConfirmationVisible(newVisible);
+ }
+ notifyUiVisibilityChanged(mSystemUiVisibility);
}
}
+ private void dismissHideybarConfirmation() {
+ if (mHideybarConfirmation != null) {
+ mHideybarConfirmationDismissed = true;
+ mHideybarConfirmation.cancel();
+ mHideybarConfirmation = null;
+ }
+ }
+
+ private void setHideybarConfirmationVisible(boolean visible) {
+ if (DEBUG) Log.d(TAG, "setHideybarConfirmationVisible " + visible);
+ if (visible && mHideybarConfirmation == null && !mHideybarConfirmationDismissed) {
+ // create the confirmation toast bar
+ int msg = R.string.hideybar_confirmation_message;
+ mHideybarConfirmation = Toast.makeBar(mContext, msg, Toast.LENGTH_INFINITE)
+ .setAction(com.android.internal.R.string.ok, mHideybarConfirmationAction);
+ View v = mHideybarConfirmation.getView();
+ v.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+ boolean isGloballyConfirmed = Prefs.read(mContext)
+ .getBoolean(Prefs.HIDEYBAR_CONFIRMED, false);
+ if (isGloballyConfirmed) {
+ // dismiss on outside touch if globally confirmed
+ v.setOnTouchListener(mDismissHideybarConfirmationOnTouchOutside);
+ }
+ // position at the bottom like normal toasts, but use top gravity
+ // to avoid jumping around when showing/hiding the nav bar
+ v.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ int offsetY = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.toast_y_offset);
+ mHideybarConfirmation.setGravity(Gravity.TOP,
+ 0, mCurrentDisplaySize.y - v.getMeasuredHeight() / 2 - offsetY);
+ // show the confirmation
+ mHideybarConfirmation.show();
+ } else if (!visible) {
+ dismissHideybarConfirmation();
+ }
+ }
+
+ @Override
+ public void resumeAutohide() {
+ if (mAutohideSuspended) {
+ scheduleAutohide();
+ }
+ }
+
+ @Override
+ public void suspendAutohide() {
+ mHandler.removeCallbacks(mAutohide);
+ mAutohideSuspended = 0 != (mSystemUiVisibility & STATUS_OR_NAV_OVERLAY);
+ }
+
+ private void cancelAutohide() {
+ mAutohideSuspended = false;
+ mHandler.removeCallbacks(mAutohide);
+ }
+
+ private void scheduleAutohide() {
+ cancelAutohide();
+ mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
+ }
+
+ private void setTransparent(View view, boolean transparent) {
+ float alpha = transparent ? TRANSPARENT_ALPHA : 1;
+ if (DEBUG) Log.d(TAG, "Setting " + (view == mStatusBarView ? "status bar" :
+ view == mNavigationBarView ? "navigation bar" : "view") + " alpha to " + alpha);
+ view.setAlpha(alpha);
+ }
+
private void setStatusBarLowProfile(boolean lightsOut) {
if (mLightsOutAnimation == null) {
final View notifications = mStatusBarView.findViewById(R.id.notification_icon_area);
@@ -1917,16 +2015,16 @@
}
}
- private void notifyUiVisibilityChanged() {
+ private void notifyUiVisibilityChanged(int vis) {
try {
- mWindowManagerService.statusBarVisibilityChanged(mSystemUiVisibility);
+ mWindowManagerService.statusBarVisibilityChanged(vis);
} catch (RemoteException ex) {
}
}
public void topAppWindowChanged(boolean showMenu) {
if (DEBUG) {
- Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
+ Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
}
if (mNavigationBarView != null) {
mNavigationBarView.setMenuVisibility(showMenu);
@@ -2089,7 +2187,7 @@
mHandler.post(new Runnable() {
public void run() {
mStatusBarView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ ") " + mStatusBarView.getWidth() + "x"
+ getStatusBarHeight());
mStatusBarView.debug();
@@ -2161,7 +2259,7 @@
@Override
public void updateExpandedViewPos(int thingy) {
- if (DEBUG) Slog.v(TAG, "updateExpandedViewPos");
+ if (DEBUG) Log.v(TAG, "updateExpandedViewPos");
// on larger devices, the notification panel is propped open a bit
mNotificationPanel.setMinimumHeight(
@@ -2229,7 +2327,7 @@
@Override
public void run() {
if (DEBUG) {
- Slog.v(TAG, "running post-collapse cleanup");
+ Log.v(TAG, "running post-collapse cleanup");
}
try {
mPile.setViewRemoval(true);
@@ -2307,7 +2405,7 @@
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
- if (DEBUG) Slog.v(TAG, "onReceive: " + intent);
+ if (DEBUG) Log.v(TAG, "onReceive: " + intent);
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
@@ -2326,7 +2424,7 @@
}
else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
if (DEBUG) {
- Slog.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
+ Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
mDisplay.getSize(mCurrentDisplaySize);
@@ -2360,22 +2458,20 @@
mCurrentUserId);
}
- private void setIntruderAlertVisibility(boolean vis) {
- if (!ENABLE_INTRUDERS) return;
- if (DEBUG) {
- Slog.v(TAG, (vis ? "showing" : "hiding") + " intruder alert window");
- }
- mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
+ private void setHeadsUpVisibility(boolean vis) {
+ if (!ENABLE_HEADS_UP) return;
+ if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
+ mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
}
- public void dismissIntruder() {
- if (mCurrentlyIntrudingNotification == null) return;
+ public void dismissHeadsUp() {
+ if (mCurrentlyInterruptingNotification == null) return;
try {
mBarService.onNotificationClear(
- mCurrentlyIntrudingNotification.getPackageName(),
- mCurrentlyIntrudingNotification.getTag(),
- mCurrentlyIntrudingNotification.getId());
+ mCurrentlyInterruptingNotification.getPackageName(),
+ mCurrentlyInterruptingNotification.getTag(),
+ mCurrentlyInterruptingNotification.getId());
} catch (android.os.RemoteException ex) {
// oh well
}
@@ -2414,7 +2510,7 @@
R.dimen.status_bar_icon_padding);
if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
-// Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
+// Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
mIconHPadding = newIconHPadding;
mIconSize = newIconSize;
//reloadAllNotificationIcons(); // reload the tray
@@ -2459,7 +2555,7 @@
mNotificationPanelMinHeightFrac = 0f;
}
- if (false) Slog.v(TAG, "updateResources");
+ if (false) Log.v(TAG, "updateResources");
}
//
@@ -2480,7 +2576,7 @@
public void run() {
vibrate();
SystemClock.sleep(250);
- Slog.d(TAG, "startTracing");
+ Log.d(TAG, "startTracing");
android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
mHandler.postDelayed(mStopTracing, 10000);
}
@@ -2489,7 +2585,7 @@
Runnable mStopTracing = new Runnable() {
public void run() {
android.os.Debug.stopMethodTracing();
- Slog.d(TAG, "stopTracing");
+ Log.d(TAG, "stopTracing");
vibrate();
}
};
@@ -2539,4 +2635,16 @@
public void setBounds(Rect bounds) {
}
}
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ if (mStatusBarWindow != null) {
+ mWindowManager.removeViewImmediate(mStatusBarWindow);
+ }
+ if (mNavigationBarView != null) {
+ mWindowManager.removeViewImmediate(mNavigationBarView);
+ }
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 9b8bd22..4ee2a4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -22,28 +22,13 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.location.LocationManager;
import android.media.AudioManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiManager;
-import android.os.Binder;
import android.os.Handler;
-import android.os.RemoteException;
-import android.os.storage.StorageManager;
-import android.provider.Settings;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.TelephonyManager;
-import android.util.Slog;
+import android.util.Log;
-import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.cdma.EriInfo;
import com.android.internal.telephony.cdma.TtyIntent;
-import com.android.server.am.BatteryStatsService;
import com.android.systemui.R;
/**
@@ -71,10 +56,6 @@
private final StatusBarManager mService;
private final Handler mHandler = new Handler();
- // storage
- private StorageManager mStorageManager;
-
-
// Assume it's all good unless we hear otherwise. We don't always seem
// to get broadcasts that it *is* there.
IccCardConstants.State mSimState = IccCardConstants.State.READY;
@@ -150,11 +131,6 @@
filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
- // storage
- mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
- mStorageManager.registerListener(
- new com.android.systemui.usb.StorageNotification(context));
-
// TTY status
mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0, null);
mService.setIconVisibility("tty", false);
@@ -284,17 +260,17 @@
final String action = intent.getAction();
final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
- if (false) Slog.v(TAG, "updateTTY: enabled: " + enabled);
+ if (false) Log.v(TAG, "updateTTY: enabled: " + enabled);
if (enabled) {
// TTY is on
- if (false) Slog.v(TAG, "updateTTY: set TTY on");
+ if (false) Log.v(TAG, "updateTTY: set TTY on");
mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0,
mContext.getString(R.string.accessibility_tty_enabled));
mService.setIconVisibility("tty", true);
} else {
// TTY is off
- if (false) Slog.v(TAG, "updateTTY: set TTY off");
+ if (false) Log.v(TAG, "updateTTY: set TTY off");
mService.setIconVisibility("tty", false);
}
}
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 de9f750..2a65381 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -23,7 +23,7 @@
import android.content.res.Resources.NotFoundException;
import android.util.AttributeSet;
import android.util.EventLog;
-import android.util.Slog;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
@@ -128,7 +128,7 @@
float region = (w * mSettingsPanelDragzoneFrac);
if (DEBUG) {
- Slog.v(TAG, String.format(
+ Log.v(TAG, String.format(
"w=%.1f frac=%.3f region=%.1f min=%.1f x=%.1f w-x=%.1f",
w, mSettingsPanelDragzoneFrac, region, mSettingsPanelDragzoneMin, x, (w-x)));
}
@@ -152,7 +152,7 @@
// which is kind of tricky to determine
mShouldFade = (mFadingPanel == null || mFadingPanel.isFullyExpanded());
if (DEBUG) {
- Slog.v(TAG, "start opening: " + panel + " shouldfade=" + mShouldFade);
+ Log.v(TAG, "start opening: " + panel + " shouldfade=" + mShouldFade);
}
mFadingPanel = panel;
}
@@ -202,7 +202,7 @@
super.panelExpansionChanged(panel, frac);
if (DEBUG) {
- Slog.v(TAG, "panelExpansionChanged: f=" + frac);
+ Log.v(TAG, "panelExpansionChanged: f=" + frac);
}
if (panel == mFadingPanel && mScrimColor != 0 && ActivityManager.isHighEndGfx()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 85bcd8b..9ee6065 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -16,19 +16,6 @@
package com.android.systemui.statusbar.phone;
-import com.android.internal.view.RotationPolicy;
-import com.android.systemui.R;
-
-import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.RSSIState;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.State;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.UserState;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.WifiState;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -71,8 +58,19 @@
import android.widget.ImageView;
import android.widget.TextView;
-import java.util.ArrayList;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState;
+import com.android.systemui.statusbar.phone.QuickSettingsModel.RSSIState;
+import com.android.systemui.statusbar.phone.QuickSettingsModel.State;
+import com.android.systemui.statusbar.phone.QuickSettingsModel.UserState;
+import com.android.systemui.statusbar.phone.QuickSettingsModel.WifiState;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import java.util.ArrayList;
/**
*
@@ -97,6 +95,7 @@
private WifiManager mWifiManager;
private BluetoothController mBluetoothController;
+ private RotationLockController mRotationLockController;
private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
@@ -113,14 +112,6 @@
private final ArrayList<QuickSettingsTileView> mDynamicSpannedTiles =
new ArrayList<QuickSettingsTileView>();
- private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
- new RotationPolicy.RotationPolicyListener() {
- @Override
- public void onChange() {
- mModel.onRotationLockChanged();
- }
- };
-
public QuickSettings(Context context, QuickSettingsContainerView container) {
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mContext = context;
@@ -170,8 +161,10 @@
}
void setup(NetworkController networkController, BluetoothController bluetoothController,
- BatteryController batteryController, LocationController locationController) {
+ BatteryController batteryController, LocationController locationController,
+ RotationLockController rotationLockController) {
mBluetoothController = bluetoothController;
+ mRotationLockController = rotationLockController;
setupQuickSettings();
updateWifiDisplayStatus();
@@ -181,8 +174,7 @@
bluetoothController.addStateChangedCallback(mModel);
batteryController.addStateChangedCallback(mModel);
locationController.addStateChangedCallback(mModel);
- RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener,
- UserHandle.USER_ALL);
+ rotationLockController.addRotationLockControllerCallback(mModel);
}
private void queryForUserInformation() {
@@ -471,13 +463,29 @@
= new QuickSettingsBasicTile(mContext);
rotationLockTile.setOnClickListener(new View.OnClickListener() {
@Override
- public void onClick(View v) {
- boolean locked = RotationPolicy.isRotationLocked(mContext);
- RotationPolicy.setRotationLock(mContext, !locked);
+ public void onClick(View view) {
+ final boolean locked = mRotationLockController.isRotationLocked();
+ mRotationLockController.setRotationLocked(!locked);
}
});
- mModel.addRotationLockTile(rotationLockTile,
- new QuickSettingsModel.BasicRefreshCallback(rotationLockTile));
+ mModel.addRotationLockTile(rotationLockTile, mRotationLockController,
+ new QuickSettingsModel.RefreshCallback() {
+ @Override
+ public void refreshView(QuickSettingsTileView view, State state) {
+ QuickSettingsModel.RotationLockState rotationLockState =
+ (QuickSettingsModel.RotationLockState) state;
+ view.setVisibility(rotationLockState.visible
+ ? View.VISIBLE : View.GONE);
+ if (state.iconId != 0) {
+ // needed to flush any cached IDs
+ rotationLockTile.setImageDrawable(null);
+ rotationLockTile.setImageResource(state.iconId);
+ }
+ if (state.label != null) {
+ rotationLockTile.setText(state.label);
+ }
+ }
+ });
parent.addView(rotationLockTile);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java
index 94b2fc7..ef850d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import com.android.systemui.R;
-
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
@@ -26,6 +24,8 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.systemui.R;
+
class QuickSettingsBasicTile extends QuickSettingsTileView {
private final TextView mTextView;
private final ImageView mImageView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 38c46c4..79495a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -39,22 +39,23 @@
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.view.RotationPolicy;
import com.android.systemui.R;
-import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.settings.BrightnessController.BrightnessStateChangeCallback;
+import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.LocationController.LocationGpsStateChangeCallback;
import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
import java.util.List;
-
class QuickSettingsModel implements BluetoothStateChangeCallback,
NetworkSignalChangedCallback,
BatteryStateChangeCallback,
LocationGpsStateChangeCallback,
- BrightnessStateChangeCallback {
+ BrightnessStateChangeCallback,
+ RotationLockControllerCallback {
// Sett InputMethoManagerService
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
@@ -89,6 +90,9 @@
boolean connected = false;
String stateContentDescription;
}
+ public static class RotationLockState extends State {
+ boolean visible = false;
+ }
/** The callback to update a given tile. */
interface RefreshCallback {
@@ -245,7 +249,7 @@
private QuickSettingsTileView mRotationLockTile;
private RefreshCallback mRotationLockCallback;
- private State mRotationLockState = new State();
+ private RotationLockState mRotationLockState = new RotationLockState();
private QuickSettingsTileView mBrightnessTile;
private RefreshCallback mBrightnessCallback;
@@ -259,6 +263,8 @@
private RefreshCallback mSettingsCallback;
private State mSettingsState = new State();
+ private RotationLockController mRotationLockController;
+
public QuickSettingsModel(Context context) {
mContext = context;
mHandler = new Handler();
@@ -574,7 +580,7 @@
} catch (SettingNotFoundException e) {
}
- mBugreportState.enabled = enabled;
+ mBugreportState.enabled = enabled && mUserTracker.isCurrentUserOwner();
mBugreportCallback.refreshView(mBugreportTile, mBugreportState);
}
@@ -681,25 +687,29 @@
}
// Rotation lock
- void addRotationLockTile(QuickSettingsTileView view, RefreshCallback cb) {
+ void addRotationLockTile(QuickSettingsTileView view,
+ RotationLockController rotationLockController,
+ RefreshCallback cb) {
mRotationLockTile = view;
mRotationLockCallback = cb;
+ mRotationLockController = rotationLockController;
onRotationLockChanged();
}
void onRotationLockChanged() {
- boolean locked = RotationPolicy.isRotationLocked(mContext);
- mRotationLockState.enabled = locked;
- mRotationLockState.iconId = locked
+ onRotationLockStateChanged(mRotationLockController.isRotationLocked(),
+ mRotationLockController.isRotationLockAffordanceVisible());
+ }
+ @Override
+ public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
+ mRotationLockState.visible = affordanceVisible;
+ mRotationLockState.enabled = rotationLocked;
+ mRotationLockState.iconId = rotationLocked
? R.drawable.ic_qs_rotation_locked
: R.drawable.ic_qs_auto_rotate;
- mRotationLockState.label = locked
+ mRotationLockState.label = rotationLocked
? mContext.getString(R.string.quick_settings_rotation_locked_label)
: mContext.getString(R.string.quick_settings_rotation_unlocked_label);
-
- // may be called before addRotationLockTile due to RotationPolicyListener in QuickSettings
- if (mRotationLockTile != null && mRotationLockCallback != null) {
- mRotationLockCallback.refreshView(mRotationLockTile, mRotationLockState);
- }
+ mRotationLockCallback.refreshView(mRotationLockTile, mRotationLockState);
}
void refreshRotationLockTile() {
if (mRotationLockTile != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
index 33335631..c10a0d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
@@ -16,28 +16,24 @@
package com.android.systemui.statusbar.phone;
-import android.animation.LayoutTransition;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.EventLog;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
-import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.RotationLockController;
public class SettingsPanelView extends PanelView {
public static final boolean DEBUG_GESTURES = true;
@@ -85,10 +81,11 @@
}
public void setup(NetworkController networkController, BluetoothController bluetoothController,
- BatteryController batteryController, LocationController locationController) {
+ BatteryController batteryController, LocationController locationController,
+ RotationLockController rotationLockController) {
if (mQS != null) {
mQS.setup(networkController, bluetoothController, batteryController,
- locationController);
+ locationController, rotationLockController);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 5620e1b..a600aae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -20,14 +20,12 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewRootImpl;
import android.widget.FrameLayout;
import android.widget.ScrollView;
-import android.widget.TextSwitcher;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
index f3f6a80..a784f37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
@@ -16,28 +16,26 @@
package com.android.systemui.statusbar.phone;
-import android.service.notification.StatusBarNotification;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.text.StaticLayout;
+import android.service.notification.StatusBarNotification;
import android.text.Layout.Alignment;
+import android.text.StaticLayout;
import android.text.TextPaint;
import android.view.View;
import android.view.animation.AnimationUtils;
+import android.widget.ImageSwitcher;
import android.widget.TextSwitcher;
import android.widget.TextView;
-import android.widget.ImageSwitcher;
-
-import java.util.ArrayList;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.CharSequences;
-
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarIconView;
+import java.util.ArrayList;
+
public abstract class Ticker {
private static final int TICKER_SEGMENT_DELAY = 3000;
@@ -192,7 +190,7 @@
if (n.getPackageName().equals(seg.notification.getPackageName())
&& n.getNotification().icon == seg.notification.getNotification().icon
&& n.getNotification().iconLevel == seg.notification.getNotification().iconLevel
- && CharSequences.equals(seg.notification.getNotification().tickerText,
+ && charSequencesEqual(seg.notification.getNotification().tickerText,
n.getNotification().tickerText)) {
return;
}
@@ -232,6 +230,20 @@
}
}
+ private static boolean charSequencesEqual(CharSequence a, CharSequence b) {
+ if (a.length() != b.length()) {
+ return false;
+ }
+
+ int length = a.length();
+ for (int i = 0; i < length; i++) {
+ if (a.charAt(i) != b.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public void removeEntry(StatusBarNotification n) {
for (int i=mSegments.size()-1; i>=0; i--) {
Segment seg = mSegments.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingPatternView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingPatternView.java
deleted file mode 100644
index d2ed5ff..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingPatternView.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
-import android.graphics.BitmapFactory;
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-import android.graphics.Canvas;
-
-public class TrackingPatternView extends View {
- private Bitmap mTexture;
- private Paint mPaint;
- private int mTextureWidth;
- private int mTextureHeight;
-
- public TrackingPatternView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mTexture = BitmapFactory.decodeResource(getResources(),
- com.android.internal.R.drawable.status_bar_background);
- mTextureWidth = mTexture.getWidth();
- mTextureHeight = mTexture.getHeight();
-
- mPaint = new Paint();
- mPaint.setDither(false);
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- final Bitmap texture = mTexture;
- final Paint paint = mPaint;
-
- final int width = getWidth();
- final int height = getHeight();
-
- final int textureWidth = mTextureWidth;
- final int textureHeight = mTextureHeight;
-
- int x = 0;
- int y;
-
- while (x < width) {
- y = 0;
- while (y < height) {
- canvas.drawBitmap(texture, x, y, paint);
- y += textureHeight;
- }
- x += textureWidth;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AirplaneModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AirplaneModeController.java
deleted file mode 100644
index 3c8276d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AirplaneModeController.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Slog;
-import android.widget.CompoundButton;
-
-public class AirplaneModeController extends BroadcastReceiver
- implements CompoundButton.OnCheckedChangeListener {
- private static final String TAG = "StatusBar.AirplaneModeController";
-
- private Context mContext;
- private CompoundButton mCheckBox;
-
- private boolean mAirplaneMode;
-
- public AirplaneModeController(Context context, CompoundButton checkbox) {
- mContext = context;
- mAirplaneMode = getAirplaneMode();
- mCheckBox = checkbox;
- checkbox.setChecked(mAirplaneMode);
- checkbox.setOnCheckedChangeListener(this);
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- context.registerReceiver(this, filter);
-
- }
-
- public void release() {
- mContext.unregisterReceiver(this);
- }
-
- public void onCheckedChanged(CompoundButton view, boolean checked) {
- if (checked != mAirplaneMode) {
- mAirplaneMode = checked;
- unsafe(checked);
- }
- }
-
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
- final boolean enabled = intent.getBooleanExtra("state", false);
- if (enabled != mAirplaneMode) {
- mAirplaneMode = enabled;
- mCheckBox.setChecked(enabled);
- }
- }
- }
-
- private boolean getAirplaneMode() {
- ContentResolver cr = mContext.getContentResolver();
- return 0 != Settings.Global.getInt(cr, Settings.Global.AIRPLANE_MODE_ON, 0);
- }
-
- // TODO: Fix this racy API by adding something better to TelephonyManager or
- // ConnectivityService.
- private void unsafe(final boolean enabled) {
- AsyncTask.execute(new Runnable() {
- public void run() {
- Settings.Global.putInt(
- mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON,
- enabled ? 1 : 0);
- Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra("state", enabled);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
- });
- }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java
deleted file mode 100644
index 7d58032..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import com.android.internal.view.RotationPolicy;
-
-import android.content.Context;
-import android.os.UserHandle;
-import android.widget.CompoundButton;
-
-public final class AutoRotateController implements CompoundButton.OnCheckedChangeListener {
- private final Context mContext;
- private final CompoundButton mCheckbox;
- private final RotationLockCallbacks mCallbacks;
-
- private boolean mAutoRotation;
-
- private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
- new RotationPolicy.RotationPolicyListener() {
- @Override
- public void onChange() {
- updateState();
- }
- };
-
- public AutoRotateController(Context context, CompoundButton checkbox,
- RotationLockCallbacks callbacks) {
- mContext = context;
- mCheckbox = checkbox;
- mCallbacks = callbacks;
-
- mCheckbox.setOnCheckedChangeListener(this);
-
- RotationPolicy.registerRotationPolicyListener(context, mRotationPolicyListener,
- UserHandle.USER_ALL);
- updateState();
- }
-
- public void onCheckedChanged(CompoundButton view, boolean checked) {
- if (checked != mAutoRotation) {
- mAutoRotation = checked;
- RotationPolicy.setRotationLock(mContext, !checked);
- }
- }
-
- public void release() {
- RotationPolicy.unregisterRotationPolicyListener(mContext,
- mRotationPolicyListener);
- }
-
- private void updateState() {
- mAutoRotation = !RotationPolicy.isRotationLocked(mContext);
- mCheckbox.setChecked(mAutoRotation);
-
- boolean visible = RotationPolicy.isRotationLockToggleVisible(mContext);
- mCallbacks.setRotationLockControlVisibility(visible);
- mCheckbox.setEnabled(visible);
- }
-
- public interface RotationLockCallbacks {
- void setRotationLockControlVisibility(boolean show);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 716341f..452ff489 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -16,20 +16,18 @@
package com.android.systemui.statusbar.policy;
-import java.util.ArrayList;
-
-import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
-import android.util.Slog;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.systemui.R;
+import java.util.ArrayList;
+
public class BatteryController extends BroadcastReceiver {
private static final String TAG = "StatusBar.BatteryController";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index bff6cda..e5816cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -20,22 +20,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.format.DateFormat;
import android.text.style.CharacterStyle;
-import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
-import android.text.style.RelativeSizeSpan;
-import android.text.style.StyleSpan;
import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
import android.widget.TextView;
import java.text.SimpleDateFormat;
@@ -45,8 +35,6 @@
import libcore.icu.LocaleData;
-import com.android.internal.R;
-
/**
* Digital clock for the status bar.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
deleted file mode 100644
index 2d951c2..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.RemoteException;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-public class CompatModeButton extends ImageView {
- private static final boolean DEBUG = false;
- private static final String TAG = "StatusBar.CompatModeButton";
-
- private ActivityManager mAM;
-
- public CompatModeButton(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public CompatModeButton(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs);
-
- setClickable(true);
-
- mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-
- refresh();
- }
-
- public void refresh() {
- int mode = mAM.getFrontActivityScreenCompatMode();
- if (mode == ActivityManager.COMPAT_MODE_UNKNOWN) {
- // If in an unknown state, don't change.
- return;
- }
- final boolean vis = (mode != ActivityManager.COMPAT_MODE_NEVER
- && mode != ActivityManager.COMPAT_MODE_ALWAYS);
- if (DEBUG) Slog.d(TAG, "compat mode is " + mode + "; icon will " + (vis ? "show" : "hide"));
- setVisibility(vis ? View.VISIBLE : View.GONE);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java
deleted file mode 100644
index 225ebc1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-
-public class CurrentUserTracker extends BroadcastReceiver {
-
- private int mCurrentUserId;
-
- public CurrentUserTracker(Context context) {
- IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- context.registerReceiver(this, filter);
- mCurrentUserId = ActivityManager.getCurrentUser();
- }
-
- public int getCurrentUserId() {
- return mCurrentUserId;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index 6eb88be..dca5e41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -22,7 +22,7 @@
import android.graphics.Canvas;
import android.os.SystemClock;
import android.util.AttributeSet;
-import android.util.Slog;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -75,7 +75,7 @@
mVertical = (index == VERTICAL);
if (DEBUG)
- Slog.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
+ Log.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
+ (mVertical ? " vertical" : " horizontal"));
setFlashOnTouchCapture(context.getResources().getBoolean(R.bool.config_dead_zone_flash));
@@ -106,7 +106,7 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
if (DEBUG) {
- Slog.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
+ Log.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
}
final int action = event.getAction();
@@ -114,12 +114,12 @@
poke(event);
} else if (action == MotionEvent.ACTION_DOWN) {
if (DEBUG) {
- Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
+ Log.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
}
int size = (int) getSize(event.getEventTime());
if ((mVertical && event.getX() < size) || event.getY() < size) {
if (CHATTY) {
- Slog.v(TAG, "consuming errant click: (" + event.getX() + "," + event.getY() + ")");
+ Log.v(TAG, "consuming errant click: (" + event.getX() + "," + event.getY() + ")");
}
if (mShouldFlash) {
post(mDebugFlash);
@@ -134,7 +134,7 @@
public void poke(MotionEvent event) {
mLastPokeTime = event.getEventTime();
if (DEBUG)
- Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime));
+ Log.v(TAG, "poked! size=" + getSize(mLastPokeTime));
if (mShouldFlash) postInvalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DoNotDisturbController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DoNotDisturbController.java
deleted file mode 100644
index 94c8aa5..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DoNotDisturbController.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Settings;
-import android.util.Slog;
-import android.view.IWindowManager;
-import android.widget.CompoundButton;
-
-public class DoNotDisturbController implements CompoundButton.OnCheckedChangeListener,
- SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = "StatusBar.DoNotDisturbController";
-
- SharedPreferences mPrefs;
- private Context mContext;
- private CompoundButton mCheckBox;
-
- private boolean mDoNotDisturb;
-
- public DoNotDisturbController(Context context, CompoundButton checkbox) {
- mContext = context;
-
- mPrefs = Prefs.read(context);
- mPrefs.registerOnSharedPreferenceChangeListener(this);
- mDoNotDisturb = mPrefs.getBoolean(Prefs.DO_NOT_DISTURB_PREF, Prefs.DO_NOT_DISTURB_DEFAULT);
-
- mCheckBox = checkbox;
- checkbox.setOnCheckedChangeListener(this);
-
- checkbox.setChecked(!mDoNotDisturb);
- }
-
- // The checkbox is ON for notifications coming in and OFF for Do not disturb, so we
- // don't have a double negative.
- public void onCheckedChanged(CompoundButton view, boolean checked) {
- //Slog.d(TAG, "onCheckedChanged checked=" + checked + " mDoNotDisturb=" + mDoNotDisturb);
- final boolean value = !checked;
- if (value != mDoNotDisturb) {
- SharedPreferences.Editor editor = Prefs.edit(mContext);
- editor.putBoolean(Prefs.DO_NOT_DISTURB_PREF, value);
- editor.apply();
- }
- }
-
- public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
- final boolean val = prefs.getBoolean(Prefs.DO_NOT_DISTURB_PREF,
- Prefs.DO_NOT_DISTURB_DEFAULT);
- if (val != mDoNotDisturb) {
- mDoNotDisturb = val;
- mCheckBox.setChecked(!val);
- }
- }
-
- public void release() {
- mPrefs.unregisterOnSharedPreferenceChangeListener(this);
- }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EventHole.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EventHole.java
deleted file mode 100644
index 47e758c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EventHole.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Region;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.ServiceManager;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.HapticFeedbackConstants;
-import android.view.IWindowManager;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewTreeObserver;
-import android.widget.RemoteViews.RemoteView;
-
-import com.android.systemui.R;
-
-public class EventHole extends View implements ViewTreeObserver.OnComputeInternalInsetsListener {
- private static final String TAG = "StatusBar.EventHole";
-
- private boolean mWindowVis;
- private int[] mLoc = new int[2];
-
- public EventHole(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public EventHole(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs);
- }
-
- @Override
- protected void onWindowVisibilityChanged(int visibility) {
- super.onWindowVisibilityChanged(visibility);
- mWindowVis = visibility == View.VISIBLE;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- }
-
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- final boolean visible = isShown() && mWindowVis && getWidth() > 0 && getHeight() > 0;
- final int[] loc = mLoc;
- getLocationInWindow(loc);
- final int l = loc[0];
- final int r = l + getWidth();
- final int t = loc[1];
- final int b = t + getHeight();
-
- View top = this;
- while (top.getParent() instanceof View) {
- top = (View)top.getParent();
- }
-
- if (visible) {
- info.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(0, 0, top.getWidth(), top.getHeight());
- info.touchableRegion.op(l, t, r, b, Region.Op.DIFFERENCE);
- } else {
- info.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
- }
- }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FixedSizeDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FixedSizeDrawable.java
deleted file mode 100644
index 8f2f5f9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FixedSizeDrawable.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.graphics.drawable.Drawable;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.util.Slog;
-
-public class FixedSizeDrawable extends Drawable {
- Drawable mDrawable;
- int mLeft;
- int mTop;
- int mRight;
- int mBottom;
-
- public FixedSizeDrawable(Drawable that) {
- mDrawable = that;
- }
-
- public void setFixedBounds(int l, int t, int r, int b) {
- mLeft = l;
- mTop = t;
- mRight = r;
- mBottom = b;
- }
-
- public void setBounds(Rect bounds) {
- mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
- }
-
- public void setBounds(int l, int t, int r, int b) {
- mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
- }
-
- public void draw(Canvas canvas) {
- mDrawable.draw(canvas);
- }
-
- public int getOpacity() {
- return mDrawable.getOpacity();
- }
-
- public void setAlpha(int alpha) {
- mDrawable.setAlpha(alpha);
- }
-
- public void setColorFilter(ColorFilter cf) {
- mDrawable.setColorFilter(cf);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
new file mode 100644
index 0000000..dbf9957
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.Notification;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.SwipeHelper;
+import com.android.systemui.statusbar.BaseStatusBar;
+
+public class HeadsUpNotificationView extends LinearLayout implements SwipeHelper.Callback {
+ private static final String TAG = "HeadsUpNotificationView";
+ private static final boolean DEBUG = false;
+
+ Rect mTmpRect = new Rect();
+
+ private SwipeHelper mSwipeHelper;
+
+ BaseStatusBar mBar;
+ private ViewGroup mContentHolder;
+
+ private Notification mHeadsUp;
+ private OnClickListener mOnClickListener;
+
+ public HeadsUpNotificationView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public HeadsUpNotificationView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ setOrientation(LinearLayout.VERTICAL);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ float densityScale = getResources().getDisplayMetrics().density;
+ float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
+ mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
+
+ mContentHolder = (ViewGroup) findViewById(R.id.contentHolder);
+ if (mHeadsUp != null) {
+ // whoops, we're on already!
+ applyContent(mHeadsUp, mOnClickListener);
+ }
+ }
+
+ public void setBar(BaseStatusBar bar) {
+ mBar = bar;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
+ return mSwipeHelper.onInterceptTouchEvent(ev) ||
+ super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mSwipeHelper.onTouchEvent(ev) ||
+ super.onTouchEvent(ev);
+ }
+
+ public boolean canChildBeDismissed(View v) {
+ return true;
+ }
+
+ public void onChildDismissed(View v) {
+ Log.v(TAG, "User swiped heads up to dismiss");
+ mBar.dismissHeadsUp();
+ }
+
+ public void onBeginDrag(View v) {
+ }
+
+ public void onDragCancelled(View v) {
+ mContentHolder.setAlpha(1f); // sometimes this isn't quite reset
+ }
+
+ public View getChildAtPosition(MotionEvent ev) {
+ return mContentHolder;
+ }
+
+ public View getChildContentView(View v) {
+ return v;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ float densityScale = getResources().getDisplayMetrics().density;
+ mSwipeHelper.setDensityScale(densityScale);
+ float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
+ mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
+ }
+
+ @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();
+ }
+ }
+
+ public boolean applyContent(Notification headsUp, OnClickListener listener) {
+ mHeadsUp = headsUp;
+ mOnClickListener = listener;
+ if (mContentHolder == null) {
+ // too soon!
+ return false;
+ }
+ if (headsUp.contentView == null) {
+ // bad data
+ return false;
+ }
+ mContentHolder.setX(0);
+ mContentHolder.setVisibility(View.VISIBLE);
+ mContentHolder.setAlpha(1f);
+ mContentHolder.removeAllViews();
+ final View content = headsUp.contentView.apply(getContext(), mContentHolder);
+ if (listener != null) {
+ content.setOnClickListener(listener);
+
+ Drawable bg = getResources().getDrawable(R.drawable.heads_up_notification_row_bg);
+ if (bg == null) {
+ Log.e(TAG, String.format("Can't find background drawable id=0x%08x",
+ R.drawable.heads_up_notification_row_bg));
+ } else {
+ content.setBackgroundDrawable(bg);
+ }
+ }
+ mContentHolder.addView(content);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IntruderAlertView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IntruderAlertView.java
deleted file mode 100644
index ee5c863..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IntruderAlertView.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.LayoutTransition;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Slog;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.RemoteViews;
-
-import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.statusbar.BaseStatusBar;
-
-import java.util.HashMap;
-
-public class IntruderAlertView extends LinearLayout implements SwipeHelper.Callback {
- private static final String TAG = "IntruderAlertView";
- private static final boolean DEBUG = false;
-
- Rect mTmpRect = new Rect();
-
- private SwipeHelper mSwipeHelper;
-
- BaseStatusBar mBar;
- private ViewGroup mContentHolder;
-
- private RemoteViews mIntruderRemoteViews;
- private OnClickListener mOnClickListener;
-
- public IntruderAlertView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public IntruderAlertView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- setOrientation(LinearLayout.VERTICAL);
- }
-
- @Override
- public void onAttachedToWindow() {
- float densityScale = getResources().getDisplayMetrics().density;
- float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
- mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
-
- mContentHolder = (ViewGroup) findViewById(R.id.contentHolder);
- if (mIntruderRemoteViews != null) {
- // whoops, we're on already!
- applyIntruderContent(mIntruderRemoteViews, mOnClickListener);
- }
- }
-
- public void setBar(BaseStatusBar bar) {
- mBar = bar;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
- return mSwipeHelper.onInterceptTouchEvent(ev) ||
- super.onInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return mSwipeHelper.onTouchEvent(ev) ||
- super.onTouchEvent(ev);
- }
-
- public boolean canChildBeDismissed(View v) {
- return true;
- }
-
- public void onChildDismissed(View v) {
- Slog.v(TAG, "User swiped intruder to dismiss");
- mBar.dismissIntruder();
- }
-
- public void onBeginDrag(View v) {
- }
-
- public void onDragCancelled(View v) {
- mContentHolder.setAlpha(1f); // sometimes this isn't quite reset
- }
-
- public View getChildAtPosition(MotionEvent ev) {
- return mContentHolder;
- }
-
- public View getChildContentView(View v) {
- return v;
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- float densityScale = getResources().getDisplayMetrics().density;
- mSwipeHelper.setDensityScale(densityScale);
- float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
- mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
- }
-
- @Override
- public void onDraw(android.graphics.Canvas c) {
- super.onDraw(c);
- if (DEBUG) {
- //Slog.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();
- }
- }
-
- public void applyIntruderContent(RemoteViews intruderView, OnClickListener listener) {
- if (DEBUG) {
- Slog.v(TAG, "applyIntruderContent: view=" + intruderView + " listener=" + listener);
- }
- mIntruderRemoteViews = intruderView;
- mOnClickListener = listener;
- if (mContentHolder == null) {
- // too soon!
- return;
- }
- mContentHolder.setX(0);
- mContentHolder.setVisibility(View.VISIBLE);
- mContentHolder.setAlpha(1f);
- mContentHolder.removeAllViews();
- final View content = intruderView.apply(getContext(), mContentHolder);
- if (listener != null) {
- content.setOnClickListener(listener);
-
- //content.setBackgroundResource(R.drawable.intruder_row_bg);
- Drawable bg = getResources().getDrawable(R.drawable.intruder_row_bg);
- if (bg == null) {
- Log.e(TAG, String.format("Can't find background drawable id=0x%08x", R.drawable.intruder_row_bg));
- } else {
- content.setBackgroundDrawable(bg);
- }
- }
- mContentHolder.addView(content);
-
- }
-}
\ No newline at end of file
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 82d6a99..fc77be1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -20,17 +20,13 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.graphics.Canvas;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.hardware.input.InputManager;
-import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.ServiceManager;
import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityEvent;
import android.view.HapticFeedbackConstants;
-import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -38,6 +34,7 @@
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import com.android.systemui.R;
@@ -61,7 +58,7 @@
Runnable mCheckLongPress = new Runnable() {
public void run() {
if (isPressed()) {
- // Slog.d("KeyButtonView", "longpressed: " + this);
+ // Log.d("KeyButtonView", "longpressed: " + this);
if (mCode != 0) {
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
@@ -213,7 +210,7 @@
switch (action) {
case MotionEvent.ACTION_DOWN:
- //Slog.d("KeyButtonView", "press");
+ //Log.d("KeyButtonView", "press");
mDownTime = SystemClock.uptimeMillis();
setPressed(true);
if (mCode != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index 68d048d..63f1254 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -16,8 +16,7 @@
package com.android.systemui.statusbar.policy;
-import java.util.ArrayList;
-
+import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -29,11 +28,10 @@
import android.os.UserHandle;
import android.provider.Settings;
-// private NM API
-import android.app.INotificationManager;
-
import com.android.systemui.R;
+import java.util.ArrayList;
+
public class LocationController extends BroadcastReceiver {
private static final String TAG = "StatusBar.LocationController";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 73752e5..fb2348e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -27,27 +27,23 @@
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wimax.WimaxManagerConstants;
-import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
-import android.os.RemoteException;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
-import android.util.Slog;
+import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.cdma.EriInfo;
import com.android.internal.util.AsyncChannel;
-import com.android.server.am.BatteryStatsService;
import com.android.systemui.R;
import java.io.FileDescriptor;
@@ -166,9 +162,6 @@
boolean mDataAndWifiStacked = false;
- // yuck -- stop doing this here and put it in the framework
- IBatteryStats mBatteryStats;
-
public interface SignalCluster {
void setWifiIndicators(boolean visible, int strengthIcon, int activityIcon,
String contentDescription);
@@ -253,9 +246,6 @@
// AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
updateAirplaneMode();
- // yuck
- mBatteryStats = BatteryStatsService.getService();
-
mLastLocale = mContext.getResources().getConfiguration().locale;
}
@@ -429,7 +419,7 @@
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
if (DEBUG) {
- Slog.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength +
+ Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength +
((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
}
mSignalStrength = signalStrength;
@@ -440,7 +430,7 @@
@Override
public void onServiceStateChanged(ServiceState state) {
if (DEBUG) {
- Slog.d(TAG, "onServiceStateChanged state=" + state.getState());
+ Log.d(TAG, "onServiceStateChanged state=" + state.getState());
}
mServiceState = state;
updateTelephonySignalStrength();
@@ -452,7 +442,7 @@
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (DEBUG) {
- Slog.d(TAG, "onCallStateChanged state=" + state);
+ Log.d(TAG, "onCallStateChanged state=" + state);
}
// In cdma, if a voice call is made, RSSI should switch to 1x.
if (isCdma()) {
@@ -464,7 +454,7 @@
@Override
public void onDataConnectionStateChanged(int state, int networkType) {
if (DEBUG) {
- Slog.d(TAG, "onDataConnectionStateChanged: state=" + state
+ Log.d(TAG, "onDataConnectionStateChanged: state=" + state
+ " type=" + networkType);
}
mDataState = state;
@@ -477,7 +467,7 @@
@Override
public void onDataActivity(int direction) {
if (DEBUG) {
- Slog.d(TAG, "onDataActivity: direction=" + direction);
+ Log.d(TAG, "onDataActivity: direction=" + direction);
}
mDataActivity = direction;
updateDataIcon();
@@ -539,13 +529,13 @@
private final void updateTelephonySignalStrength() {
if (!hasService()) {
- if (CHATTY) Slog.d(TAG, "updateTelephonySignalStrength: !hasService()");
+ if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: !hasService()");
mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal;
mDataSignalIconId = R.drawable.stat_sys_signal_null;
} else {
if (mSignalStrength == null) {
- if (CHATTY) Slog.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null");
+ if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null");
mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal;
mDataSignalIconId = R.drawable.stat_sys_signal_null;
@@ -556,7 +546,7 @@
int[] iconList;
if (isCdma() && mAlwaysShowCdmaRssi) {
mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel();
- if(DEBUG) Slog.d(TAG, "mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi
+ if(DEBUG) Log.d(TAG, "mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi
+ " set to cdmaLevel=" + mSignalStrength.getCdmaLevel()
+ " instead of level=" + mSignalStrength.getLevel());
} else {
@@ -792,22 +782,13 @@
}
}
- // yuck - this should NOT be done by the status bar
- long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
mDataDirectionIconId = iconId;
mDataConnected = visible;
}
void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
if (false) {
- Slog.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+ Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+ " showPlmn=" + showPlmn + " plmn=" + plmn);
}
StringBuilder str = new StringBuilder();
@@ -841,7 +822,7 @@
mWifiChannel.sendMessage(Message.obtain(this,
AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
} else {
- Slog.e(TAG, "Failed to connect to wifi");
+ Log.e(TAG, "Failed to connect to wifi");
}
break;
case WifiManager.DATA_ACTIVITY_NOTIFICATION:
@@ -973,7 +954,7 @@
private void updateConnectivity(Intent intent) {
if (CHATTY) {
- Slog.d(TAG, "updateConnectivity: intent=" + intent);
+ Log.d(TAG, "updateConnectivity: intent=" + intent);
}
final ConnectivityManager connManager = (ConnectivityManager) mContext
@@ -993,8 +974,8 @@
int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
if (CHATTY) {
- Slog.d(TAG, "updateConnectivity: networkInfo=" + info);
- Slog.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus);
+ Log.d(TAG, "updateConnectivity: networkInfo=" + info);
+ Log.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus);
}
mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
@@ -1182,7 +1163,7 @@
}
if (DEBUG) {
- Slog.d(TAG, "refreshViews connected={"
+ Log.d(TAG, "refreshViews connected={"
+ (mWifiConnected?" wifi":"")
+ (mDataConnected?" data":"")
+ " } level="
@@ -1321,7 +1302,7 @@
// the data direction overlay
if (mLastDataDirectionOverlayIconId != combinedActivityIconId) {
if (DEBUG) {
- Slog.d(TAG, "changing data overlay icon id to " + combinedActivityIconId);
+ Log.d(TAG, "changing data overlay icon id to " + combinedActivityIconId);
}
mLastDataDirectionOverlayIconId = combinedActivityIconId;
N = mDataDirectionOverlayIconViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 89eed1b..cd8445c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -16,23 +16,17 @@
package com.android.systemui.statusbar.policy;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.LayoutTransition;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.Slog;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
import com.android.systemui.ExpandHelper;
@@ -87,11 +81,11 @@
setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@Override
public void onChildViewAdded(View parent, View child) {
- Slog.d(TAG, "view added: " + child + "; new count: " + getChildCount());
+ Log.d(TAG, "view added: " + child + "; new count: " + getChildCount());
}
@Override
public void onChildViewRemoved(View parent, View child) {
- Slog.d(TAG, "view removed: " + child + "; new count: " + (getChildCount() - 1));
+ Log.d(TAG, "view removed: " + child + "; new count: " + (getChildCount() - 1));
}
});
@@ -167,7 +161,7 @@
}
public void onChildDismissed(View v) {
- if (DEBUG) Slog.v(TAG, "onChildDismissed: " + v + " mRemoveViews=" + mRemoveViews);
+ if (DEBUG) Log.v(TAG, "onChildDismissed: " + v + " mRemoveViews=" + mRemoveViews);
final View veto = v.findViewById(R.id.veto);
if (veto != null && veto.getVisibility() != View.GONE && mRemoveViews) {
veto.performClick();
@@ -231,7 +225,7 @@
* get removed properly.
*/
public void setViewRemoval(boolean removeViews) {
- if (DEBUG) Slog.v(TAG, "setViewRemoval: " + removeViews);
+ if (DEBUG) Log.v(TAG, "setViewRemoval: " + removeViews);
mRemoveViews = removeViews;
}
@@ -266,7 +260,7 @@
super.onDraw(c);
if (DEBUG) logLayoutTransition();
if (DEBUG) {
- //Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
+ //Log.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
// + getMeasuredHeight() + "px");
c.save();
c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java
index 5d2198e..d03f6ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java
@@ -22,13 +22,11 @@
public class Prefs {
private static final String SHARED_PREFS_NAME = "status_bar";
- // a boolean
- public static final String DO_NOT_DISTURB_PREF = "do_not_disturb";
- public static final boolean DO_NOT_DISTURB_DEFAULT = false;
-
public static final String SHOWN_COMPAT_MODE_HELP = "shown_compat_mode_help";
public static final String SHOWN_QUICK_SETTINGS_HELP = "shown_quick_settings_help";
+ public static final String HIDEYBAR_CONFIRMED = "hideybar_confirmed";
+
public static SharedPreferences read(Context context) {
return context.getSharedPreferences(Prefs.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
new file mode 100644
index 0000000..6f61ec8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import com.android.internal.view.RotationPolicy;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public final class RotationLockController {
+ private final Context mContext;
+ private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks =
+ new CopyOnWriteArrayList<RotationLockControllerCallback>();
+
+ private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
+ new RotationPolicy.RotationPolicyListener() {
+ @Override
+ public void onChange() {
+ notifyChanged();
+ }
+ };
+
+ public interface RotationLockControllerCallback {
+ public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible);
+ }
+
+ public RotationLockController(Context context) {
+ mContext = context;
+ notifyChanged();
+ if (RotationPolicy.isRotationLockToggleSupported(mContext)) {
+ RotationPolicy.registerRotationPolicyListener(mContext,
+ mRotationPolicyListener, UserHandle.USER_ALL);
+ }
+ }
+
+ public void addRotationLockControllerCallback(RotationLockControllerCallback callback) {
+ mCallbacks.add(callback);
+ }
+
+ public boolean isRotationLocked() {
+ if (RotationPolicy.isRotationLockToggleSupported(mContext)) {
+ return RotationPolicy.isRotationLocked(mContext);
+ }
+ return false;
+ }
+
+ public void setRotationLocked(boolean locked) {
+ if (RotationPolicy.isRotationLockToggleSupported(mContext)) {
+ RotationPolicy.setRotationLock(mContext, locked);
+ }
+ }
+
+ public boolean isRotationLockAffordanceVisible() {
+ if (RotationPolicy.isRotationLockToggleSupported(mContext)) {
+ return RotationPolicy.isRotationLockToggleVisible(mContext);
+ }
+ return false;
+ }
+
+ public void release() {
+ if (RotationPolicy.isRotationLockToggleSupported(mContext)) {
+ RotationPolicy.unregisterRotationPolicyListener(mContext,
+ mRotationPolicyListener);
+ }
+ }
+
+ private void notifyChanged() {
+ for (RotationLockControllerCallback callback : mCallbacks) {
+ callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext),
+ RotationPolicy.isRotationLockToggleVisible(mContext));
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java
deleted file mode 100644
index 70f9ac8..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.Vibrator;
-import android.media.AudioManager;
-import android.provider.Settings;
-import android.util.Slog;
-import android.view.IWindowManager;
-import android.widget.CompoundButton;
-
-import com.android.systemui.settings.ToggleSlider;
-
-public class VolumeController implements ToggleSlider.Listener {
- private static final String TAG = "StatusBar.VolumeController";
- private static final int STREAM = AudioManager.STREAM_NOTIFICATION;
-
- private Context mContext;
- private ToggleSlider mControl;
- private AudioManager mAudioManager;
-
- private boolean mMute;
- private int mVolume;
- // Is there a vibrator
- private final boolean mHasVibrator;
-
- public VolumeController(Context context, ToggleSlider control) {
- mContext = context;
- mControl = control;
-
- Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
-
- mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
-
- mMute = mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
- mVolume = mAudioManager.getStreamVolume(STREAM);
-
- control.setOnChangedListener(this);
- }
-
- @Override
- public void onInit(ToggleSlider control) {
- control.setMax(mAudioManager.getStreamMaxVolume(STREAM));
- control.setValue(mVolume);
- control.setChecked(mMute);
- }
-
- public void onChanged(ToggleSlider view, boolean tracking, boolean mute, int level) {
- if (!tracking) {
- if (mute) {
- mAudioManager.setRingerMode(
- mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
- : AudioManager.RINGER_MODE_SILENT);
- } else {
- mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- mAudioManager.setStreamVolume(STREAM, level, AudioManager.FLAG_PLAY_SOUND);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java
index d3d4338..4877828 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java
@@ -1,28 +1,27 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import com.android.systemui.statusbar.policy.TelephonyIcons;
-import com.android.systemui.R;
-
-class WimaxIcons {
- static final int[][] WIMAX_SIGNAL_STRENGTH = TelephonyIcons.DATA_SIGNAL_STRENGTH;
-
- static final int WIMAX_DISCONNECTED = WIMAX_SIGNAL_STRENGTH[0][0];
-
- static final int WIMAX_IDLE = WIMAX_DISCONNECTED; // XXX: unclear if we need a different icon
-}
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import com.android.systemui.statusbar.policy.TelephonyIcons;
+
+class WimaxIcons {
+ static final int[][] WIMAX_SIGNAL_STRENGTH = TelephonyIcons.DATA_SIGNAL_STRENGTH;
+
+ static final int WIMAX_DISCONNECTED = WIMAX_SIGNAL_STRENGTH[0][0];
+
+ static final int WIMAX_IDLE = WIMAX_DISCONNECTED; // XXX: unclear if we need a different icon
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java
deleted file mode 100644
index 8c4ae19..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.RemoteException;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-
-import com.android.systemui.R;
-
-public class CompatModePanel extends FrameLayout implements StatusBarPanel,
- View.OnClickListener {
- private static final boolean DEBUG = TabletStatusBar.DEBUG;
- private static final String TAG = "CompatModePanel";
-
- private ActivityManager mAM;
-
- private boolean mAttached = false;
- private Context mContext;
- private RadioButton mOnButton, mOffButton;
-
- private View mTrigger;
-// private InputMethodButton mInputMethodSwitchButton;
-
- public CompatModePanel(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- }
-
- @Override
- public void onFinishInflate() {
- mOnButton = (RadioButton) findViewById(R.id.compat_mode_on_radio);
- mOffButton = (RadioButton) findViewById(R.id.compat_mode_off_radio);
- mOnButton.setOnClickListener(this);
- mOffButton.setOnClickListener(this);
-
- refresh();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAttached) {
- mAttached = false;
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (!mAttached) {
- mAttached = true;
- }
- }
-
- @Override
- public void onClick(View v) {
- if (v == mOnButton) {
- mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_ENABLED);
- } else if (v == mOffButton) {
- mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_DISABLED);
- }
- }
-
- @Override
- public boolean isInContentArea(int x, int y) {
- return false;
- }
-
- @Override
- public boolean dispatchHoverEvent(MotionEvent event) {
- // Ignore hover events outside of this panel bounds since such events
- // generate spurious accessibility events with the panel content when
- // tapping outside of it, thus confusing the user.
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
- return super.dispatchHoverEvent(event);
- }
- return true;
- }
-
- public void setTrigger(View v) {
- mTrigger = v;
- }
-
- public void openPanel() {
- setVisibility(View.VISIBLE);
- if (mTrigger != null) mTrigger.setSelected(true);
- refresh();
- }
-
- public void closePanel() {
- setVisibility(View.GONE);
- if (mTrigger != null) mTrigger.setSelected(false);
- }
-
- private void refresh() {
- int mode = mAM.getFrontActivityScreenCompatMode();
- if (mode == ActivityManager.COMPAT_MODE_ALWAYS
- || mode == ActivityManager.COMPAT_MODE_NEVER) {
- // No longer have something to switch.
- closePanel();
- return;
- }
- final boolean on = (mode == ActivityManager.COMPAT_MODE_ENABLED);
- mOnButton.setChecked(on);
- mOffButton.setChecked(!on);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
deleted file mode 100644
index fa8aa6d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.content.Context;
-import android.os.IBinder;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-import java.util.List;
-
-public class InputMethodButton extends ImageView {
-
- private static final String TAG = "StatusBar/InputMethodButton";
- private static final boolean DEBUG = false;
-
- // These values are defined in Settings application.
- private static final int ID_IME_BUTTON_VISIBILITY_AUTO = 0;
- private static final int ID_IME_BUTTON_VISIBILITY_ALWAYS_SHOW = 1;
- private static final int ID_IME_BUTTON_VISIBILITY_ALWAYS_HIDE = 2;
-
- // other services we wish to talk to
- private final InputMethodManager mImm;
- private final int mId;
- private ImageView mIcon;
- private IBinder mToken;
- private boolean mShowButton = false;
- private boolean mScreenLocked = false;
- private boolean mHardKeyboardAvailable;
-
- // Please refer to InputMethodManagerService.TAG_TRY_SUPPRESSING_IME_SWITCHER
- private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
-
- public InputMethodButton(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- // Resource Id of the input method button. This id is defined in status_bar.xml
- mId = getId();
- // IME hookup
- mImm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- }
-
- @Override
- protected void onAttachedToWindow() {
- mIcon = (ImageView) findViewById(mId);
-
- refreshStatusIcon();
- }
-
- // Refer to InputMethodManagerService.needsToShowImeSwitchOngoingNotification()
- private boolean needsToShowIMEButtonWhenVisibilityAuto() {
- List<InputMethodInfo> imis = mImm.getEnabledInputMethodList();
- final int N = imis.size();
- if (N > 2) return true;
- if (N < 1) return false;
- int nonAuxCount = 0;
- int auxCount = 0;
- InputMethodSubtype nonAuxSubtype = null;
- InputMethodSubtype auxSubtype = null;
- for(int i = 0; i < N; ++i) {
- final InputMethodInfo imi = imis.get(i);
- final List<InputMethodSubtype> subtypes = mImm.getEnabledInputMethodSubtypeList(
- imi, true);
- final int subtypeCount = subtypes.size();
- if (subtypeCount == 0) {
- ++nonAuxCount;
- } else {
- for (int j = 0; j < subtypeCount; ++j) {
- final InputMethodSubtype subtype = subtypes.get(j);
- if (!subtype.isAuxiliary()) {
- ++nonAuxCount;
- nonAuxSubtype = subtype;
- } else {
- ++auxCount;
- auxSubtype = subtype;
- }
- }
- }
- }
- if (nonAuxCount > 1 || auxCount > 1) {
- return true;
- } else if (nonAuxCount == 1 && auxCount == 1) {
- if (nonAuxSubtype != null && auxSubtype != null
- && (nonAuxSubtype.getLocale().equals(auxSubtype.getLocale())
- || auxSubtype.overridesImplicitlyEnabledSubtype()
- || nonAuxSubtype.overridesImplicitlyEnabledSubtype())
- && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) {
- return false;
- }
- return true;
- }
- return false;
- }
-
- private boolean needsToShowIMEButton() {
- if (!mShowButton || mScreenLocked) return false;
-
- if (mHardKeyboardAvailable) {
- return true;
- }
-
- final int visibility = loadInputMethodSelectorVisibility();
- switch (visibility) {
- case ID_IME_BUTTON_VISIBILITY_AUTO:
- return needsToShowIMEButtonWhenVisibilityAuto();
- case ID_IME_BUTTON_VISIBILITY_ALWAYS_SHOW:
- return true;
- case ID_IME_BUTTON_VISIBILITY_ALWAYS_HIDE:
- return false;
- }
- return false;
- }
-
- private void refreshStatusIcon() {
- if (mIcon == null) {
- return;
- }
- if (!needsToShowIMEButton()) {
- setVisibility(View.GONE);
- return;
- } else {
- setVisibility(View.VISIBLE);
- }
- mIcon.setImageResource(R.drawable.ic_sysbar_ime);
- }
-
- private int loadInputMethodSelectorVisibility() {
- return Settings.Secure.getInt(getContext().getContentResolver(),
- Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, ID_IME_BUTTON_VISIBILITY_AUTO);
- }
-
- public void setIconImage(int resId) {
- if (mIcon != null) {
- mIcon.setImageResource(resId);
- }
- }
-
- public void setImeWindowStatus(IBinder token, boolean showButton) {
- mToken = token;
- mShowButton = showButton;
- refreshStatusIcon();
- }
-
- public void setHardKeyboardStatus(boolean available) {
- if (mHardKeyboardAvailable != available) {
- mHardKeyboardAvailable = available;
- refreshStatusIcon();
- }
- }
-
- public void setScreenLocked(boolean locked) {
- mScreenLocked = locked;
- refreshStatusIcon();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
deleted file mode 100644
index 8924087..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import com.android.systemui.R;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.IBinder;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Pair;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-public class InputMethodsPanel extends LinearLayout implements StatusBarPanel,
- View.OnClickListener {
- private static final boolean DEBUG = TabletStatusBar.DEBUG;
- private static final String TAG = "InputMethodsPanel";
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onPackageChanged();
- }
- };
-
- private final InputMethodManager mImm;
- private final IntentFilter mIntentFilter = new IntentFilter();
- private final HashMap<View, Pair<InputMethodInfo, InputMethodSubtype>> mRadioViewAndImiMap =
- new HashMap<View, Pair<InputMethodInfo, InputMethodSubtype>>();
- private final TreeMap<InputMethodInfo, List<InputMethodSubtype>>
- mEnabledInputMethodAndSubtypesCache =
- new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
- new InputMethodComparator());
-
- private boolean mAttached = false;
- private boolean mPackageChanged = false;
- private Context mContext;
- private IBinder mToken;
- private InputMethodButton mInputMethodSwitchButton;
- private LinearLayout mInputMethodMenuList;
- private boolean mHardKeyboardAvailable;
- private boolean mHardKeyboardEnabled;
- private OnHardKeyboardEnabledChangeListener mHardKeyboardEnabledChangeListener;
- private LinearLayout mHardKeyboardSection;
- private Switch mHardKeyboardSwitch;
- private PackageManager mPackageManager;
- private String mEnabledInputMethodAndSubtypesCacheStr;
- private String mLastSystemLocaleString;
- private View mConfigureImeShortcut;
-
- private class InputMethodComparator implements Comparator<InputMethodInfo> {
- @Override
- public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
- if (imi2 == null) return 0;
- if (imi1 == null) return 1;
- if (mPackageManager == null) {
- return imi1.getId().compareTo(imi2.getId());
- }
- CharSequence imiId1 = imi1.loadLabel(mPackageManager) + "/" + imi1.getId();
- CharSequence imiId2 = imi2.loadLabel(mPackageManager) + "/" + imi2.getId();
- return imiId1.toString().compareTo(imiId2.toString());
- }
- }
-
- public InputMethodsPanel(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public InputMethodsPanel(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mContext = context;
- mImm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- mIntentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- mIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- mIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- mIntentFilter.addDataScheme("package");
- }
-
- public void setHardKeyboardEnabledChangeListener(
- OnHardKeyboardEnabledChangeListener listener) {
- mHardKeyboardEnabledChangeListener = listener;
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAttached) {
- getContext().unregisterReceiver(mBroadcastReceiver);
- mAttached = false;
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (!mAttached) {
- getContext().registerReceiver(mBroadcastReceiver, mIntentFilter);
- mAttached = true;
- }
- }
-
- @Override
- public void onFinishInflate() {
- mInputMethodMenuList = (LinearLayout) findViewById(R.id.input_method_menu_list);
- mHardKeyboardSection = (LinearLayout) findViewById(R.id.hard_keyboard_section);
- mHardKeyboardSwitch = (Switch) findViewById(R.id.hard_keyboard_switch);
- mConfigureImeShortcut = findViewById(R.id.ime_settings_shortcut);
- mConfigureImeShortcut.setOnClickListener(this);
- // TODO: If configurations for IME are not changed, do not update
- // by checking onConfigurationChanged.
- updateUiElements();
- }
-
- @Override
- public boolean isInContentArea(int x, int y) {
- return false;
- }
-
- @Override
- public void onClick(View view) {
- if (view == mConfigureImeShortcut) {
- showConfigureInputMethods();
- closePanel(true);
- }
- }
-
- @Override
- public boolean dispatchHoverEvent(MotionEvent event) {
- // Ignore hover events outside of this panel bounds since such events
- // generate spurious accessibility events with the panel content when
- // tapping outside of it, thus confusing the user.
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
- return super.dispatchHoverEvent(event);
- }
- return true;
- }
-
- private void updateHardKeyboardEnabled() {
- if (mHardKeyboardAvailable) {
- final boolean checked = mHardKeyboardSwitch.isChecked();
- if (mHardKeyboardEnabled != checked) {
- mHardKeyboardEnabled = checked;
- if (mHardKeyboardEnabledChangeListener != null)
- mHardKeyboardEnabledChangeListener.onHardKeyboardEnabledChange(checked);
- }
- }
- }
-
- public void openPanel() {
- setVisibility(View.VISIBLE);
- updateUiElements();
- if (mInputMethodSwitchButton != null) {
- mInputMethodSwitchButton.setIconImage(R.drawable.ic_sysbar_ime_pressed);
- }
- }
-
- public void closePanel(boolean closeKeyboard) {
- setVisibility(View.GONE);
- if (mInputMethodSwitchButton != null) {
- mInputMethodSwitchButton.setIconImage(R.drawable.ic_sysbar_ime);
- }
- if (closeKeyboard) {
- mImm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
- }
-
- private void startActivity(Intent intent) {
- mContext.startActivity(intent);
- }
-
- private void showConfigureInputMethods() {
- Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- }
-
- private View createInputMethodItem(
- final InputMethodInfo imi, final InputMethodSubtype subtype) {
- final CharSequence subtypeName;
- if (subtype == null || subtype.overridesImplicitlyEnabledSubtype()) {
- subtypeName = null;
- } else {
- subtypeName = getSubtypeName(imi, subtype);
- }
- final CharSequence imiName = getIMIName(imi);
- final Drawable icon = getSubtypeIcon(imi, subtype);
- final View view = View.inflate(mContext, R.layout.system_bar_input_methods_item, null);
- final ImageView subtypeIcon = (ImageView)view.findViewById(R.id.item_icon);
- final TextView itemTitle = (TextView)view.findViewById(R.id.item_title);
- final TextView itemSubtitle = (TextView)view.findViewById(R.id.item_subtitle);
- final ImageView settingsIcon = (ImageView)view.findViewById(R.id.item_settings_icon);
- final View subtypeView = view.findViewById(R.id.item_subtype);
- if (subtypeName == null) {
- itemTitle.setText(imiName);
- itemSubtitle.setVisibility(View.GONE);
- } else {
- itemTitle.setText(subtypeName);
- itemSubtitle.setVisibility(View.VISIBLE);
- itemSubtitle.setText(imiName);
- }
- subtypeIcon.setImageDrawable(icon);
- subtypeIcon.setContentDescription(itemTitle.getText());
- final String settingsActivity = imi.getSettingsActivity();
- if (!TextUtils.isEmpty(settingsActivity)) {
- settingsIcon.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View arg0) {
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClassName(imi.getPackageName(), settingsActivity);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- closePanel(true);
- }
- });
- } else {
- // Do not show the settings icon if the IME does not have a settings preference
- view.findViewById(R.id.item_vertical_separator).setVisibility(View.GONE);
- settingsIcon.setVisibility(View.GONE);
- }
- mRadioViewAndImiMap.put(
- subtypeView, new Pair<InputMethodInfo, InputMethodSubtype> (imi, subtype));
- subtypeView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Pair<InputMethodInfo, InputMethodSubtype> imiAndSubtype =
- updateRadioButtonsByView(v);
- closePanel(false);
- setInputMethodAndSubtype(imiAndSubtype.first, imiAndSubtype.second);
- }
- });
- return view;
- }
-
- private void updateUiElements() {
- updateHardKeyboardSection();
-
- // TODO: Reuse subtype views.
- mInputMethodMenuList.removeAllViews();
- mRadioViewAndImiMap.clear();
- mPackageManager = mContext.getPackageManager();
-
- Map<InputMethodInfo, List<InputMethodSubtype>> enabledIMIs =
- getEnabledInputMethodAndSubtypeList();
- Set<InputMethodInfo> cachedImiSet = enabledIMIs.keySet();
- for (InputMethodInfo imi: cachedImiSet) {
- List<InputMethodSubtype> subtypes = enabledIMIs.get(imi);
- if (subtypes == null || subtypes.size() == 0) {
- mInputMethodMenuList.addView(
- createInputMethodItem(imi, null));
- continue;
- }
- for (InputMethodSubtype subtype: subtypes) {
- mInputMethodMenuList.addView(createInputMethodItem(imi, subtype));
- }
- }
- updateRadioButtons();
- }
-
- public void setImeToken(IBinder token) {
- mToken = token;
- }
-
- public void setImeSwitchButton(InputMethodButton imb) {
- mInputMethodSwitchButton = imb;
- }
-
- private void setInputMethodAndSubtype(InputMethodInfo imi, InputMethodSubtype subtype) {
- if (mToken != null) {
- mImm.setInputMethodAndSubtype(mToken, imi.getId(), subtype);
- } else {
- Log.w(TAG, "IME Token is not set yet.");
- }
- }
-
- public void setHardKeyboardStatus(boolean available, boolean enabled) {
- if (mHardKeyboardAvailable != available || mHardKeyboardEnabled != enabled) {
- mHardKeyboardAvailable = available;
- mHardKeyboardEnabled = enabled;
- updateHardKeyboardSection();
- }
- }
-
- private void updateHardKeyboardSection() {
- if (mHardKeyboardAvailable) {
- mHardKeyboardSection.setVisibility(View.VISIBLE);
- if (mHardKeyboardSwitch.isChecked() != mHardKeyboardEnabled) {
- mHardKeyboardSwitch.setChecked(mHardKeyboardEnabled);
- updateHardKeyboardEnabled();
- }
- } else {
- mHardKeyboardSection.setVisibility(View.GONE);
- }
- }
-
- // Turn on the selected radio button when the user chooses the item
- private Pair<InputMethodInfo, InputMethodSubtype> updateRadioButtonsByView(View selectedView) {
- Pair<InputMethodInfo, InputMethodSubtype> selectedImiAndSubtype = null;
- if (mRadioViewAndImiMap.containsKey(selectedView)) {
- for (View radioView: mRadioViewAndImiMap.keySet()) {
- RadioButton subtypeRadioButton =
- (RadioButton) radioView.findViewById(R.id.item_radio);
- if (subtypeRadioButton == null) {
- Log.w(TAG, "RadioButton was not found in the selected subtype view");
- return null;
- }
- if (radioView == selectedView) {
- Pair<InputMethodInfo, InputMethodSubtype> imiAndSubtype =
- mRadioViewAndImiMap.get(radioView);
- selectedImiAndSubtype = imiAndSubtype;
- subtypeRadioButton.setChecked(true);
- } else {
- subtypeRadioButton.setChecked(false);
- }
- }
- }
- return selectedImiAndSubtype;
- }
-
- private void updateRadioButtons() {
- updateRadioButtonsByImiAndSubtype(
- getCurrentInputMethodInfo(), mImm.getCurrentInputMethodSubtype());
- }
-
- // Turn on the selected radio button at startup
- private void updateRadioButtonsByImiAndSubtype(
- InputMethodInfo imi, InputMethodSubtype subtype) {
- if (imi == null) return;
- if (DEBUG) {
- Log.d(TAG, "Update radio buttons by " + imi.getId() + ", " + subtype);
- }
- for (View radioView: mRadioViewAndImiMap.keySet()) {
- RadioButton subtypeRadioButton =
- (RadioButton) radioView.findViewById(R.id.item_radio);
- if (subtypeRadioButton == null) {
- Log.w(TAG, "RadioButton was not found in the selected subtype view");
- return;
- }
- Pair<InputMethodInfo, InputMethodSubtype> imiAndSubtype =
- mRadioViewAndImiMap.get(radioView);
- if (imiAndSubtype.first.getId().equals(imi.getId())
- && (imiAndSubtype.second == null || imiAndSubtype.second.equals(subtype))) {
- subtypeRadioButton.setChecked(true);
- } else {
- subtypeRadioButton.setChecked(false);
- }
- }
- }
-
- private TreeMap<InputMethodInfo, List<InputMethodSubtype>>
- getEnabledInputMethodAndSubtypeList() {
- String newEnabledIMIs = Settings.Secure.getString(
- mContext.getContentResolver(), Settings.Secure.ENABLED_INPUT_METHODS);
- String currentSystemLocaleString =
- mContext.getResources().getConfiguration().locale.toString();
- if (!TextUtils.equals(mEnabledInputMethodAndSubtypesCacheStr, newEnabledIMIs)
- || !TextUtils.equals(mLastSystemLocaleString, currentSystemLocaleString)
- || mPackageChanged) {
- mEnabledInputMethodAndSubtypesCache.clear();
- final List<InputMethodInfo> imis = mImm.getEnabledInputMethodList();
- for (InputMethodInfo imi: imis) {
- mEnabledInputMethodAndSubtypesCache.put(imi,
- mImm.getEnabledInputMethodSubtypeList(imi, true));
- }
- mEnabledInputMethodAndSubtypesCacheStr = newEnabledIMIs;
- mPackageChanged = false;
- mLastSystemLocaleString = currentSystemLocaleString;
- }
- return mEnabledInputMethodAndSubtypesCache;
- }
-
- private InputMethodInfo getCurrentInputMethodInfo() {
- String curInputMethodId = Settings.Secure.getString(getContext()
- .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
- Set<InputMethodInfo> cachedImiSet = mEnabledInputMethodAndSubtypesCache.keySet();
- // 1. Search IMI in cache
- for (InputMethodInfo imi: cachedImiSet) {
- if (imi.getId().equals(curInputMethodId)) {
- return imi;
- }
- }
- // 2. Get current enabled IMEs and search IMI
- cachedImiSet = getEnabledInputMethodAndSubtypeList().keySet();
- for (InputMethodInfo imi: cachedImiSet) {
- if (imi.getId().equals(curInputMethodId)) {
- return imi;
- }
- }
- return null;
- }
-
- private CharSequence getIMIName(InputMethodInfo imi) {
- if (imi == null) return null;
- return imi.loadLabel(mPackageManager);
- }
-
- private CharSequence getSubtypeName(InputMethodInfo imi, InputMethodSubtype subtype) {
- if (imi == null || subtype == null) return null;
- if (DEBUG) {
- Log.d(TAG, "Get text from: " + imi.getPackageName() + subtype.getNameResId()
- + imi.getServiceInfo().applicationInfo);
- }
- return subtype.getDisplayName(
- mContext, imi.getPackageName(), imi.getServiceInfo().applicationInfo);
- }
-
- private Drawable getSubtypeIcon(InputMethodInfo imi, InputMethodSubtype subtype) {
- if (imi != null) {
- if (DEBUG) {
- Log.d(TAG, "Update icons of IME: " + imi.getPackageName());
- if (subtype != null) {
- Log.d(TAG, "subtype =" + subtype.getLocale() + "," + subtype.getMode());
- }
- }
- if (subtype != null) {
- return mPackageManager.getDrawable(imi.getPackageName(), subtype.getIconResId(),
- imi.getServiceInfo().applicationInfo);
- } else if (imi.getSubtypeCount() > 0) {
- return mPackageManager.getDrawable(imi.getPackageName(),
- imi.getSubtypeAt(0).getIconResId(),
- imi.getServiceInfo().applicationInfo);
- } else {
- try {
- return mPackageManager.getApplicationInfo(
- imi.getPackageName(), 0).loadIcon(mPackageManager);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "IME can't be found: " + imi.getPackageName());
- }
- }
- }
- return null;
- }
-
- private void onPackageChanged() {
- if (DEBUG) {
- Log.d(TAG, "onPackageChanged.");
- }
- mPackageChanged = true;
- }
-
- public interface OnHardKeyboardEnabledChangeListener {
- public void onHardKeyboardEnabledChange(boolean enabled);
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationArea.java
deleted file mode 100644
index 42bdf3d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationArea.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.LinearLayout;
-
-public class NotificationArea extends LinearLayout {
-
- public NotificationArea(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
- if (super.onRequestSendAccessibilityEvent(child, event)) {
- // The event is coming from a descendant like battery but append
- // the content of the entire notification area so accessibility
- // services can choose how to present the content to the user.
- AccessibilityEvent record = AccessibilityEvent.obtain();
- onInitializeAccessibilityEvent(record);
- dispatchPopulateAccessibilityEvent(record);
- event.appendRecord(record);
- return true;
- }
- return false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
deleted file mode 100644
index 3d6c1a2..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.ImageView;
-import android.view.MotionEvent;
-
-import com.android.systemui.R;
-
-
-public class NotificationIconArea extends RelativeLayout {
- private static final String TAG = "NotificationIconArea";
-
- IconLayout mIconLayout;
-
- public NotificationIconArea(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mIconLayout = (IconLayout)findViewById(R.id.icons);
- }
-
- static class IconLayout extends LinearLayout {
- public IconLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public boolean onInterceptTouchEvent(MotionEvent e) {
- return true;
- }
- }
-}
-
-
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationLinearLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationLinearLayout.java
deleted file mode 100644
index 9ecb2e4..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationLinearLayout.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-public class NotificationLinearLayout extends LinearLayout {
- private static final String TAG = "NotificationLinearLayout";
-
- Drawable mItemGlow;
- int mInsetLeft;
- Rect mTmp = new Rect();
-
- public NotificationLinearLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public NotificationLinearLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- final Resources res = context.getResources();
-
- mItemGlow = res.getDrawable(R.drawable.notify_item_glow_bottom);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NotificationLinearLayout,
- defStyle, 0);
- mInsetLeft = a.getDimensionPixelSize(R.styleable.NotificationLinearLayout_insetLeft, 0);
- a.recycle();
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
- setWillNotDraw(false);
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- final Rect padding = mTmp;
- final Drawable glow = mItemGlow;
- glow.getPadding(padding);
- final int glowHeight = glow.getIntrinsicHeight();
- final int insetLeft = mInsetLeft;
-
- final int N = getChildCount();
- for (int i=0; i<N; i++) {
- final View child = getChildAt(i);
-
- final int childBottom = child.getBottom();
-
- glow.setBounds(child.getLeft() - padding.left + insetLeft, childBottom,
- child.getRight() - padding.right, childBottom + glowHeight);
- glow.draw(canvas);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
deleted file mode 100644
index 87fc6fc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-
-import com.android.systemui.ExpandHelper;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.NotificationRowLayout;
-
-public class NotificationPanel extends RelativeLayout implements StatusBarPanel,
- View.OnClickListener {
- private ExpandHelper mExpandHelper;
- private NotificationRowLayout latestItems;
-
- static final String TAG = "Tablet/NotificationPanel";
- static final boolean DEBUG = false;
-
- final static int PANEL_FADE_DURATION = 150;
-
- boolean mShowing;
- boolean mHasClearableNotifications = false;
- int mNotificationCount = 0;
- NotificationPanelTitle mTitleArea;
- ImageView mSettingsButton;
- ImageView mNotificationButton;
- View mNotificationScroller;
- ViewGroup mContentFrame;
- Rect mContentArea = new Rect();
- View mSettingsView;
- ViewGroup mContentParent;
- TabletStatusBar mBar;
- View mClearButton;
- static Interpolator sAccelerateInterpolator = new AccelerateInterpolator();
- static Interpolator sDecelerateInterpolator = new DecelerateInterpolator();
-
- // amount to slide mContentParent down by when mContentFrame is missing
- float mContentFrameMissingTranslation;
-
- Choreographer mChoreo = new Choreographer();
-
- public NotificationPanel(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public NotificationPanel(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public void setBar(TabletStatusBar b) {
- mBar = b;
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
-
- setWillNotDraw(false);
-
- mContentParent = (ViewGroup)findViewById(R.id.content_parent);
- mContentParent.bringToFront();
- mTitleArea = (NotificationPanelTitle) findViewById(R.id.title_area);
- mTitleArea.setPanel(this);
-
- mSettingsButton = (ImageView) findViewById(R.id.settings_button);
- mNotificationButton = (ImageView) findViewById(R.id.notification_button);
-
- mNotificationScroller = findViewById(R.id.notification_scroller);
- mContentFrame = (ViewGroup)findViewById(R.id.content_frame);
- mContentFrameMissingTranslation = 0; // not needed with current assets
-
- // the "X" that appears in place of the clock when the panel is showing notifications
- mClearButton = findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
-
- mShowing = false;
- }
-
- @Override
- protected void onAttachedToWindow () {
- super.onAttachedToWindow();
- latestItems = (NotificationRowLayout) findViewById(R.id.content);
- int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
- int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
- mExpandHelper = new ExpandHelper(mContext, latestItems, minHeight, maxHeight);
- mExpandHelper.setEventSource(this);
- mExpandHelper.setGravity(Gravity.BOTTOM);
- }
-
- private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
- public void onClick(View v) {
- mBar.clearAll();
- }
- };
-
- public View getClearButton() {
- return mClearButton;
- }
-
- public void show(boolean show, boolean animate) {
- if (animate) {
- if (mShowing != show) {
- mShowing = show;
- if (show) {
- setVisibility(View.VISIBLE);
- // Don't start the animation until we've created the layer, which is done
- // right before we are drawn
- mContentParent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
- } else {
- mChoreo.startAnimation(show);
- }
- }
- } else {
- mShowing = show;
- setVisibility(show ? View.VISIBLE : View.GONE);
- }
- }
-
- /**
- * This is used only when we've created a hardware layer and are waiting until it's
- * been created in order to start the appearing animation.
- */
- private ViewTreeObserver.OnPreDrawListener mPreDrawListener =
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getViewTreeObserver().removeOnPreDrawListener(this);
- mChoreo.startAnimation(true);
- return false;
- }
- };
-
- /**
- * Whether the panel is showing, or, if it's animating, whether it will be
- * when the animation is done.
- */
- public boolean isShowing() {
- return mShowing;
- }
-
- @Override
- public void onVisibilityChanged(View v, int vis) {
- super.onVisibilityChanged(v, vis);
- // when we hide, put back the notifications
- if (vis != View.VISIBLE) {
- if (mSettingsView != null) removeSettingsView();
- mNotificationScroller.setVisibility(View.VISIBLE);
- mNotificationScroller.setAlpha(1f);
- mNotificationScroller.scrollTo(0, 0);
- updatePanelModeButtons();
- }
- }
-
- @Override
- public boolean dispatchHoverEvent(MotionEvent event) {
- // Ignore hover events outside of this panel bounds since such events
- // generate spurious accessibility events with the panel content when
- // tapping outside of it, thus confusing the user.
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
- return super.dispatchHoverEvent(event);
- }
- return true;
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- final int keyCode = event.getKeyCode();
- switch (keyCode) {
- // We exclusively handle the back key by hiding this panel.
- case KeyEvent.KEYCODE_BACK: {
- if (event.getAction() == KeyEvent.ACTION_UP) {
- mBar.animateCollapsePanels();
- }
- return true;
- }
- // We react to the home key but let the system handle it.
- case KeyEvent.KEYCODE_HOME: {
- if (event.getAction() == KeyEvent.ACTION_UP) {
- mBar.animateCollapsePanels();
- }
- } break;
- }
- return super.dispatchKeyEvent(event);
- }
-
- /*
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
-
- if (DEBUG) Slog.d(TAG, String.format("PANEL: onLayout: (%d, %d, %d, %d)", l, t, r, b));
- }
-
- @Override
- public void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
-
- if (DEBUG) {
- Slog.d(TAG, String.format("PANEL: onSizeChanged: (%d -> %d, %d -> %d)",
- oldw, w, oldh, h));
- }
- }
- */
-
- public void onClick(View v) {
- if (mSettingsButton.isEnabled() && v == mTitleArea) {
- swapPanels();
- }
- }
-
- public void setNotificationCount(int n) {
- mNotificationCount = n;
- }
-
- public void setContentFrameVisible(final boolean showing, boolean animate) {
- }
-
- public void swapPanels() {
- final View toShow, toHide;
- if (mSettingsView == null) {
- addSettingsView();
- toShow = mSettingsView;
- toHide = mNotificationScroller;
- } else {
- toShow = mNotificationScroller;
- toHide = mSettingsView;
- }
- Animator a = ObjectAnimator.ofFloat(toHide, "alpha", 1f, 0f)
- .setDuration(PANEL_FADE_DURATION);
- a.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator _a) {
- toHide.setVisibility(View.GONE);
- if (toShow != null) {
- toShow.setVisibility(View.VISIBLE);
- if (toShow == mSettingsView || mNotificationCount > 0) {
- ObjectAnimator.ofFloat(toShow, "alpha", 0f, 1f)
- .setDuration(PANEL_FADE_DURATION)
- .start();
- }
-
- if (toHide == mSettingsView) {
- removeSettingsView();
- }
- }
- updateClearButton();
- updatePanelModeButtons();
- }
- });
- a.start();
- }
-
- public void updateClearButton() {
- if (mBar != null) {
- final boolean showX
- = (isShowing()
- && mHasClearableNotifications
- && mNotificationScroller.getVisibility() == View.VISIBLE);
- getClearButton().setVisibility(showX ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
- public void setClearable(boolean clearable) {
- mHasClearableNotifications = clearable;
- }
-
- public void updatePanelModeButtons() {
- final boolean settingsVisible = (mSettingsView != null);
- mSettingsButton.setVisibility(!settingsVisible && mSettingsButton.isEnabled() ? View.VISIBLE : View.GONE);
- mNotificationButton.setVisibility(settingsVisible ? View.VISIBLE : View.GONE);
- }
-
- public boolean isInContentArea(int x, int y) {
- mContentArea.left = mContentFrame.getLeft() + mContentFrame.getPaddingLeft();
- mContentArea.top = mContentFrame.getTop() + mContentFrame.getPaddingTop()
- + (int)mContentParent.getTranslationY(); // account for any adjustment
- mContentArea.right = mContentFrame.getRight() - mContentFrame.getPaddingRight();
- mContentArea.bottom = mContentFrame.getBottom() - mContentFrame.getPaddingBottom();
-
- offsetDescendantRectToMyCoords(mContentParent, mContentArea);
- return mContentArea.contains(x, y);
- }
-
- void removeSettingsView() {
- if (mSettingsView != null) {
- mContentFrame.removeView(mSettingsView);
- mSettingsView = null;
- }
- }
-
- // NB: it will be invisible until you show it
- void addSettingsView() {
- LayoutInflater infl = LayoutInflater.from(getContext());
- mSettingsView = infl.inflate(R.layout.system_bar_settings_view, mContentFrame, false);
- mSettingsView.setVisibility(View.GONE);
- mContentFrame.addView(mSettingsView);
- }
-
- private class Choreographer implements Animator.AnimatorListener {
- boolean mVisible;
- int mPanelHeight;
- AnimatorSet mContentAnim;
-
- // should group this into a multi-property animation
- final static int OPEN_DURATION = 250;
- final static int CLOSE_DURATION = 250;
-
- // the panel will start to appear this many px from the end
- final int HYPERSPACE_OFFRAMP = 200;
-
- Choreographer() {
- }
-
- void createAnimation(boolean appearing) {
- // mVisible: previous state; appearing: new state
-
- float start, end;
-
- // 0: on-screen
- // height: off-screen
- float y = mContentParent.getTranslationY();
- if (appearing) {
- // we want to go from near-the-top to the top, unless we're half-open in the right
- // general vicinity
- end = 0;
- if (mNotificationCount == 0) {
- end += mContentFrameMissingTranslation;
- }
- start = HYPERSPACE_OFFRAMP+end;
- } else {
- start = y;
- end = y + HYPERSPACE_OFFRAMP;
- }
-
- Animator posAnim = ObjectAnimator.ofFloat(mContentParent, "translationY",
- start, end);
- posAnim.setInterpolator(appearing ? sDecelerateInterpolator : sAccelerateInterpolator);
-
- if (mContentAnim != null && mContentAnim.isRunning()) {
- mContentAnim.cancel();
- }
-
- Animator fadeAnim = ObjectAnimator.ofFloat(mContentParent, "alpha",
- appearing ? 1.0f : 0.0f);
- fadeAnim.setInterpolator(appearing ? sAccelerateInterpolator : sDecelerateInterpolator);
-
- mContentAnim = new AnimatorSet();
- mContentAnim
- .play(fadeAnim)
- .with(posAnim)
- ;
- mContentAnim.setDuration((DEBUG?10:1)*(appearing ? OPEN_DURATION : CLOSE_DURATION));
- mContentAnim.addListener(this);
- }
-
- void startAnimation(boolean appearing) {
- if (DEBUG) Slog.d(TAG, "startAnimation(appearing=" + appearing + ")");
-
- createAnimation(appearing);
- mContentAnim.start();
-
- mVisible = appearing;
-
- // we want to start disappearing promptly
- if (!mVisible) updateClearButton();
- }
-
- public void onAnimationCancel(Animator animation) {
- if (DEBUG) Slog.d(TAG, "onAnimationCancel");
- }
-
- public void onAnimationEnd(Animator animation) {
- if (DEBUG) Slog.d(TAG, "onAnimationEnd");
- if (! mVisible) {
- setVisibility(View.GONE);
- }
- mContentParent.setLayerType(View.LAYER_TYPE_NONE, null);
- mContentAnim = null;
-
- // we want to show the X lazily
- if (mVisible) updateClearButton();
- }
-
- public void onAnimationRepeat(Animator animation) {
- }
-
- public void onAnimationStart(Animator animation) {
- }
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- MotionEvent cancellation = MotionEvent.obtain(ev);
- cancellation.setAction(MotionEvent.ACTION_CANCEL);
-
- boolean intercept = mExpandHelper.onInterceptTouchEvent(ev) ||
- super.onInterceptTouchEvent(ev);
- if (intercept) {
- latestItems.onInterceptTouchEvent(cancellation);
- }
- return intercept;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- boolean handled = mExpandHelper.onTouchEvent(ev) ||
- super.onTouchEvent(ev);
- return handled;
- }
-
- public void setSettingsEnabled(boolean settingsEnabled) {
- if (mSettingsButton != null) {
- mSettingsButton.setEnabled(settingsEnabled);
- mSettingsButton.setVisibility(settingsEnabled ? View.VISIBLE : View.GONE);
- }
- }
-
- public void refreshLayout(int layoutDirection) {
- // Force asset reloading
- mSettingsButton.setImageDrawable(null);
- mSettingsButton.setImageResource(R.drawable.ic_notify_settings);
-
- // Force asset reloading
- mNotificationButton.setImageDrawable(null);
- mNotificationButton.setImageResource(R.drawable.ic_notifications);
- }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanelTitle.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanelTitle.java
deleted file mode 100644
index d180ab9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanelTitle.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import java.util.ArrayList;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.SoundEffectConstants;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.RelativeLayout;
-
-import com.android.systemui.R;
-
-
-public class NotificationPanelTitle extends RelativeLayout implements View.OnClickListener {
- private NotificationPanel mPanel;
- private ArrayList<View> buttons;
- private View mSettingsButton;
-
- public NotificationPanelTitle(Context context, AttributeSet attrs) {
- super(context, attrs);
- buttons = new ArrayList<View>();
- setOnClickListener(this);
- }
-
- public void setPanel(NotificationPanel p) {
- mPanel = p;
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
- buttons.add(mSettingsButton = findViewById(R.id.settings_button));
- buttons.add(findViewById(R.id.notification_button));
- }
-
- @Override
- public void setPressed(boolean pressed) {
- super.setPressed(pressed);
- for (View button : buttons) {
- if (button != null) {
- button.setPressed(pressed);
- }
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent e) {
- if (!mSettingsButton.isEnabled())
- return false;
- switch (e.getAction()) {
- case MotionEvent.ACTION_DOWN:
- setPressed(true);
- break;
- case MotionEvent.ACTION_MOVE:
- final int x = (int) e.getX();
- final int y = (int) e.getY();
- setPressed(x > 0 && x < getWidth() && y > 0 && y < getHeight());
- break;
- case MotionEvent.ACTION_UP:
- if (isPressed()) {
- playSoundEffect(SoundEffectConstants.CLICK);
- mPanel.swapPanels();
- setPressed(false);
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- setPressed(false);
- break;
- }
- return true;
- }
-
- @Override
- public void onClick(View v) {
- if (mSettingsButton.isEnabled() && v == this) {
- mPanel.swapPanels();
- }
- }
-
- @Override
- public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
- if (super.onRequestSendAccessibilityEvent(child, event)) {
- AccessibilityEvent record = AccessibilityEvent.obtain();
- onInitializeAccessibilityEvent(record);
- dispatchPopulateAccessibilityEvent(record);
- event.appendRecord(record);
- return true;
- }
- return false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java
deleted file mode 100644
index ba28306..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.RelativeLayout;
-
-public class NotificationPeekPanel extends RelativeLayout implements StatusBarPanel {
- TabletStatusBar mBar;
-
- public NotificationPeekPanel(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public NotificationPeekPanel(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public boolean isInContentArea(int x, int y) {
- final int l = getPaddingLeft();
- final int r = getWidth() - getPaddingRight();
- final int t = getPaddingTop();
- final int b = getHeight() - getPaddingBottom();
- return x >= l && x < r && y >= t && y < b;
- }
-
- public void setBar(TabletStatusBar bar) {
- mBar = bar;
- }
-
- // We don't really want to intercept the touch event, but we *do* want to reset the fade timer
- // in case the user is interacting with some custom controls or something.
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- mBar.resetNotificationPeekFadeTimer();
- return false;
- }
-
- @Override
- public boolean dispatchHoverEvent(MotionEvent event) {
- // Ignore hover events outside of this panel bounds since such events
- // generate spurious accessibility events with the panel content when
- // tapping outside of it, thus confusing the user.
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
- return super.dispatchHoverEvent(event);
- }
- return true;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/PanelBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/PanelBackgroundView.java
deleted file mode 100644
index 9ac933f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/PanelBackgroundView.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.view.View;
-
-public class PanelBackgroundView extends View {
- /*
- private Bitmap mTexture;
- private Paint mPaint;
- private int mTextureWidth;
- private int mTextureHeight;
- */
-
- public PanelBackgroundView(Context context, AttributeSet attrs) {
- super(context, attrs);
- /*
- mTexture = BitmapFactory.decodeResource(getResources(),
- com.android.internal.R.drawable.status_bar_background);
- mTextureWidth = mTexture.getWidth();
- mTextureHeight = mTexture.getHeight();
-
- mPaint = new Paint();
- mPaint.setDither(false);
- */
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- /*
- final Bitmap texture = mTexture;
- final Paint paint = mPaint;
-
- final int width = getWidth();
- final int height = getHeight();
-
- final int textureWidth = mTextureWidth;
- final int textureHeight = mTextureHeight;
-
- int x = 0;
- int y;
-
- while (x < width) {
- y = 0;
- while (y < height) {
- canvas.drawBitmap(texture, x, y, paint);
- y += textureHeight;
- }
- x += textureWidth;
- }
- */
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
deleted file mode 100644
index e0dcbcd..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.app.StatusBarManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.widget.LinearLayout;
-import android.view.View;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.settings.BrightnessController;
-import com.android.systemui.settings.ToggleSlider;
-import com.android.systemui.statusbar.policy.AirplaneModeController;
-import com.android.systemui.statusbar.policy.AutoRotateController;
-import com.android.systemui.statusbar.policy.DoNotDisturbController;
-import com.android.systemui.statusbar.policy.VolumeController;
-
-public class SettingsView extends LinearLayout implements View.OnClickListener {
- static final String TAG = "SettingsView";
-
- AirplaneModeController mAirplane;
- AutoRotateController mRotate;
- BrightnessController mBrightness;
- DoNotDisturbController mDoNotDisturb;
- View mRotationLockContainer;
- View mRotationLockSeparator;
-
- public SettingsView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SettingsView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- final Context context = getContext();
-
- mAirplane = new AirplaneModeController(context,
- (CompoundButton)findViewById(R.id.airplane_checkbox));
- findViewById(R.id.network).setOnClickListener(this);
-
- mRotationLockContainer = findViewById(R.id.rotate);
- mRotationLockSeparator = findViewById(R.id.rotate_separator);
- mRotate = new AutoRotateController(context,
- (CompoundButton)findViewById(R.id.rotate_checkbox),
- new AutoRotateController.RotationLockCallbacks() {
- @Override
- public void setRotationLockControlVisibility(boolean show) {
- mRotationLockContainer.setVisibility(show ? View.VISIBLE : View.GONE);
- mRotationLockSeparator.setVisibility(show ? View.VISIBLE : View.GONE);
- }
- });
-
- mBrightness = new BrightnessController(context,
- (ImageView)findViewById(R.id.brightness_icon),
- (ToggleSlider)findViewById(R.id.brightness));
- mDoNotDisturb = new DoNotDisturbController(context,
- (CompoundButton)findViewById(R.id.do_not_disturb_checkbox));
- findViewById(R.id.settings).setOnClickListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mAirplane.release();
- mDoNotDisturb.release();
- mRotate.release();
- }
-
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.network:
- onClickNetwork();
- break;
- case R.id.settings:
- onClickSettings();
- break;
- }
- }
-
- private StatusBarManager getStatusBarManager() {
- return (StatusBarManager)getContext().getSystemService(Context.STATUS_BAR_SERVICE);
- }
-
- // Network
- // ----------------------------
- private void onClickNetwork() {
- getContext().startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- getStatusBarManager().collapsePanels();
- }
-
- // Settings
- // ----------------------------
- private void onClickSettings() {
- getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
- new UserHandle(UserHandle.USER_CURRENT));
- getStatusBarManager().collapsePanels();
- }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
deleted file mode 100644
index 2924cc9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.DragEvent;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-public class ShirtPocket extends ImageView {
- private static final boolean DEBUG = false;
- private static final String TAG = "StatusBar/ShirtPocket";
-
- private ClipData mClipping = null;
-
- private ImageView mPreviewIcon;
-
- public static class DropZone extends View {
- ShirtPocket mPocket;
- public DropZone(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- public void setPocket(ShirtPocket p) {
- mPocket = p;
- }
-
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mPocket.holding()) {
- show(false);
- } else {
- hide(false);
- }
- }
-
- // Drag API notes: we must be visible to receive drag events
- private void show(boolean animate) {
- setTranslationY(0f);
- if (animate) {
- setAlpha(0f);
- ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).start();
- } else {
- setAlpha(1f);
- }
- }
-
- private void hide(boolean animate) {
- AnimatorListenerAdapter onEnd = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator _a) {
- DropZone.this.setTranslationY(getHeight() + 2);
- DropZone.this.setAlpha(0f);
- }
- };
- if (animate) {
- Animator a = ObjectAnimator.ofFloat(this, "alpha", getAlpha(), 0f);
- a.addListener(onEnd);
- a.start();
- } else {
- onEnd.onAnimationEnd(null);
- }
- }
-
- @Override
- public boolean onDragEvent(DragEvent event) {
- if (DEBUG) Slog.d(TAG, "onDragEvent: " + event);
- switch (event.getAction()) {
- // We want to appear whenever a potential drag takes off from anywhere in the UI.
- case DragEvent.ACTION_DRAG_STARTED:
- show(true);
- break;
- case DragEvent.ACTION_DRAG_ENTERED:
- if (DEBUG) Slog.d(TAG, "entered!");
- // XXX: TODO
- break;
- case DragEvent.ACTION_DRAG_EXITED:
- if (DEBUG) Slog.d(TAG, "exited!");
- break;
- case DragEvent.ACTION_DROP:
- if (DEBUG) Slog.d(TAG, "dropped!");
- mPocket.stash(event.getClipData());
- break;
- case DragEvent.ACTION_DRAG_ENDED:
- hide(true);
- break;
- }
- return true; // we want everything, thank you
- }
- }
-
- public ShirtPocket(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- // TODO: "pin area" panel, dragging things out
- ObjectAnimator mAnimHide, mAnimShow;
-
- protected void onAttachedToWindow() {
- }
-
- public boolean holding() {
- return (mClipping != null);
- }
-
- private void stash(ClipData clipping) {
- mClipping = clipping;
- if (mClipping != null) {
- setVisibility(View.VISIBLE);
- Bitmap icon = mClipping.getIcon();
-// mDescription.setText(mClipping.getDescription().getLabel());
- if (icon != null) {
- setImageBitmap(icon);
- } else {
- if (mClipping.getItemCount() > 0) {
- // TODO: figure out how to visualize every kind of ClipData!
- //mAltText.setText(mClipping.getItemAt(0).coerceToText(getContext()));
- }
- }
- } else {
- setVisibility(View.GONE);
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- final int action = ev.getAction();
- if (action == MotionEvent.ACTION_DOWN) {
- final ClipData clip = mClipping;
- if (clip != null) {
- final Bitmap icon = clip.getIcon();
- DragShadowBuilder shadow;
- if (icon != null) {
- shadow = new DragShadowBuilder(this) {
- public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
- shadowSize.set(icon.getWidth(), icon.getHeight());
- shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2);
- }
- public void onDrawShadow(Canvas canvas) {
- canvas.drawBitmap(icon, 0, 0, new Paint());
- }
- };
- } else {
- // uhhh, what now?
- shadow = new DragShadowBuilder(this);
- }
-
- startDrag(clip, shadow, null, 0);
-
- // TODO: only discard the clipping if it was accepted
- stash(null);
-
- return true;
- }
- }
- return false;
- }
-
- /*
- private boolean isInViewContentArea(View v, int x, int y) {
- final int l = v.getPaddingLeft();
- final int r = v.getWidth() - v.getPaddingRight();
- final int t = v.getPaddingTop();
- final int b = v.getHeight() - v.getPaddingBottom();
- return x >= l && x < r && y >= t && y < b;
- }
-
- View.OnTouchListener mWindowTouchListener = new View.OnTouchListener() {
- public boolean onTouch(View v, MotionEvent ev) {
- final int action = ev.getAction();
- if (action == MotionEvent.ACTION_OUTSIDE
- || (action == MotionEvent.ACTION_DOWN
- && !isInViewContentArea(mWindow, (int)ev.getX(), (int)ev.getY()))) {
- hideWindow();
- return true;
- } else if (action == MotionEvent.ACTION_DOWN) {
- final ClipData clip = mClipping;
- if (clip != null) {
- final Bitmap icon = clip.getIcon();
- DragShadowBuilder shadow;
- if (icon != null) {
- shadow = new DragShadowBuilder(v) {
- public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
- shadowSize.set(icon.getWidth(), icon.getHeight());
- shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2);
- }
- public void onDrawShadow(Canvas canvas) {
- canvas.drawBitmap(icon, 0, 0, new Paint());
- }
- };
- } else {
- // uhhh, what now?
- shadow = new DragShadowBuilder(mWindow.findViewById(R.id.preview));
- }
-
- v.startDrag(clip, shadow, null, 0);
-
- // TODO: only discard the clipping if it was accepted
- stash(null);
-
- return true;
- }
- }
- return false;
- }
- };
- */
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/StatusBarPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/StatusBarPanel.java
deleted file mode 100644
index 8fa01d5..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/StatusBarPanel.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-public interface StatusBarPanel {
- public boolean isInContentArea(int x, int y);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
deleted file mode 100644
index bfa1b63..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ /dev/null
@@ -1,1545 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import android.animation.LayoutTransition;
-import android.animation.ObjectAnimator;
-import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
-import android.service.notification.StatusBarNotification;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.SoundEffectConstants;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.DoNotDisturb;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.SignalClusterView;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CompatModeButton;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NotificationRowLayout;
-import com.android.systemui.statusbar.policy.Prefs;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-public class TabletStatusBar extends BaseStatusBar implements
- InputMethodsPanel.OnHardKeyboardEnabledChangeListener {
- public static final boolean DEBUG = false;
- public static final boolean DEBUG_COMPAT_HELP = false;
- public static final String TAG = "TabletStatusBar";
-
-
- public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
- public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
- public static final int MSG_OPEN_NOTIFICATION_PEEK = 1002;
- public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003;
- // 1020-1029 reserved for BaseStatusBar
- public static final int MSG_SHOW_CHROME = 1030;
- public static final int MSG_HIDE_CHROME = 1031;
- public static final int MSG_OPEN_INPUT_METHODS_PANEL = 1040;
- public static final int MSG_CLOSE_INPUT_METHODS_PANEL = 1041;
- public static final int MSG_OPEN_COMPAT_MODE_PANEL = 1050;
- public static final int MSG_CLOSE_COMPAT_MODE_PANEL = 1051;
- public static final int MSG_STOP_TICKER = 2000;
-
- // Fitts' Law assistance for LatinIME; see policy.EventHole
- private static final boolean FAKE_SPACE_BAR = true;
-
- // Notification "peeking" (flyover preview of individual notifications)
- final static int NOTIFICATION_PEEK_HOLD_THRESH = 200; // ms
- final static int NOTIFICATION_PEEK_FADE_DELAY = 3000; // ms
-
- private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
- private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
-
- // The height of the bar, as definied by the build. It may be taller if we're plugged
- // into hdmi.
- int mNaturalBarHeight = -1;
- int mIconSize = -1;
- int mIconHPadding = -1;
- int mNavIconWidth = -1;
- int mMenuNavIconWidth = -1;
- private int mMaxNotificationIcons = 5;
-
- TabletStatusBarView mStatusBarView;
- View mNotificationArea;
- View mNotificationTrigger;
- NotificationIconArea mNotificationIconArea;
- ViewGroup mNavigationArea;
-
- boolean mNotificationDNDMode;
- NotificationData.Entry mNotificationDNDDummyEntry;
-
- ImageView mBackButton;
- View mHomeButton;
- View mMenuButton;
- View mRecentButton;
- private boolean mAltBackButtonEnabledForIme;
-
- ViewGroup mFeedbackIconArea; // notification icons, IME icon, compat icon
- InputMethodButton mInputMethodSwitchButton;
- CompatModeButton mCompatModeButton;
-
- NotificationPanel mNotificationPanel;
- WindowManager.LayoutParams mNotificationPanelParams;
- NotificationPeekPanel mNotificationPeekWindow;
- ViewGroup mNotificationPeekRow;
- int mNotificationPeekIndex;
- IBinder mNotificationPeekKey;
- LayoutTransition mNotificationPeekScrubLeft, mNotificationPeekScrubRight;
-
- int mNotificationPeekTapDuration;
- int mNotificationFlingVelocity;
-
- BatteryController mBatteryController;
- BluetoothController mBluetoothController;
- LocationController mLocationController;
- NetworkController mNetworkController;
- DoNotDisturb mDoNotDisturb;
-
- ViewGroup mBarContents;
-
- // hide system chrome ("lights out") support
- View mShadow;
-
- NotificationIconArea.IconLayout mIconLayout;
-
- TabletTicker mTicker;
-
- View mFakeSpaceBar;
- KeyEvent mSpaceBarKeyEvent = null;
-
- View mCompatibilityHelpDialog = null;
-
- // for disabling the status bar
- int mDisabled = 0;
-
- private InputMethodsPanel mInputMethodsPanel;
- private CompatModePanel mCompatModePanel;
-
- private int mSystemUiVisibility = 0;
-
- private int mNavigationIconHints = 0;
-
- private int mShowSearchHoldoff = 0;
-
- public Context getContext() { return mContext; }
-
- private Runnable mShowSearchPanel = new Runnable() {
- public void run() {
- showSearchPanel();
- }
- };
-
- private View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
- public boolean onTouch(View v, MotionEvent event) {
- switch(event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- if (!shouldDisableNavbarGestures() && !inKeyguardRestrictedInputMode()) {
- mHandler.removeCallbacks(mShowSearchPanel);
- mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mHandler.removeCallbacks(mShowSearchPanel);
- break;
- }
- return false;
- }
- };
-
- @Override
- protected void createAndAddWindows() {
- addStatusBarWindow();
- addPanelWindows();
- }
-
- private void addStatusBarWindow() {
- final View sb = makeStatusBarView();
-
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.OPAQUE);
-
- // We explicitly leave FLAG_HARDWARE_ACCELERATED out of the flags. The status bar occupies
- // very little screen real-estate and is updated fairly frequently. By using CPU rendering
- // for the status bar, we prevent the GPU from having to wake up just to do these small
- // updates, which should help keep power consumption down.
-
- lp.gravity = getStatusBarGravity();
- lp.setTitle("SystemBar");
- lp.packageName = mContext.getPackageName();
- mWindowManager.addView(sb, lp);
- }
-
- protected void addPanelWindows() {
- final Context context = mContext;
- final Resources res = mContext.getResources();
-
- // Notification Panel
- mNotificationPanel = (NotificationPanel)View.inflate(context,
- R.layout.system_bar_notification_panel, null);
- mNotificationPanel.setBar(this);
- mNotificationPanel.show(false, false);
- mNotificationPanel.setOnTouchListener(
- new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPanel));
-
- // the battery icon
- mBatteryController.addIconView((ImageView)mNotificationPanel.findViewById(R.id.battery));
- mBatteryController.addLabelView(
- (TextView)mNotificationPanel.findViewById(R.id.battery_text));
-
- // Bt
- mBluetoothController.addIconView(
- (ImageView)mNotificationPanel.findViewById(R.id.bluetooth));
-
- // network icons: either a combo icon that switches between mobile and data, or distinct
- // mobile and data icons
- final ImageView mobileRSSI =
- (ImageView)mNotificationPanel.findViewById(R.id.mobile_signal);
- if (mobileRSSI != null) {
- mNetworkController.addPhoneSignalIconView(mobileRSSI);
- }
- final ImageView wifiRSSI =
- (ImageView)mNotificationPanel.findViewById(R.id.wifi_signal);
- if (wifiRSSI != null) {
- mNetworkController.addWifiIconView(wifiRSSI);
- }
- mNetworkController.addWifiLabelView(
- (TextView)mNotificationPanel.findViewById(R.id.wifi_text));
-
- mNetworkController.addDataTypeIconView(
- (ImageView)mNotificationPanel.findViewById(R.id.mobile_type));
- mNetworkController.addMobileLabelView(
- (TextView)mNotificationPanel.findViewById(R.id.mobile_text));
- mNetworkController.addCombinedLabelView(
- (TextView)mBarContents.findViewById(R.id.network_text));
-
- mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel);
-
- WindowManager.LayoutParams lp = mNotificationPanelParams = new WindowManager.LayoutParams(
- res.getDimensionPixelSize(R.dimen.notification_panel_width),
- getNotificationPanelHeight(),
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT);
- lp.gravity = Gravity.BOTTOM | Gravity.END;
- lp.setTitle("NotificationPanel");
- lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
- | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
- lp.windowAnimations = com.android.internal.R.style.Animation; // == no animation
-// lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; // simple fade
-
- mWindowManager.addView(mNotificationPanel, lp);
-
- // Search Panel
- mStatusBarView.setBar(this);
- mHomeButton.setOnTouchListener(mHomeSearchActionListener);
- updateSearchPanel();
-
- // Input methods Panel
- mInputMethodsPanel = (InputMethodsPanel) View.inflate(context,
- R.layout.system_bar_input_methods_panel, null);
- mInputMethodsPanel.setHardKeyboardEnabledChangeListener(this);
- mInputMethodsPanel.setOnTouchListener(new TouchOutsideListener(
- MSG_CLOSE_INPUT_METHODS_PANEL, mInputMethodsPanel));
- mInputMethodsPanel.setImeSwitchButton(mInputMethodSwitchButton);
- mStatusBarView.setIgnoreChildren(2, mInputMethodSwitchButton, mInputMethodsPanel);
- lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT);
- lp.gravity = Gravity.BOTTOM | Gravity.END;
- lp.setTitle("InputMethodsPanel");
- lp.windowAnimations = R.style.Animation_RecentPanel;
-
- mWindowManager.addView(mInputMethodsPanel, lp);
-
- // Compatibility mode selector panel
- mCompatModePanel = (CompatModePanel) View.inflate(context,
- R.layout.system_bar_compat_mode_panel, null);
- mCompatModePanel.setOnTouchListener(new TouchOutsideListener(
- MSG_CLOSE_COMPAT_MODE_PANEL, mCompatModePanel));
- mCompatModePanel.setTrigger(mCompatModeButton);
- mCompatModePanel.setVisibility(View.GONE);
- mStatusBarView.setIgnoreChildren(3, mCompatModeButton, mCompatModePanel);
- lp = new WindowManager.LayoutParams(
- 250,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT);
- lp.gravity = Gravity.BOTTOM | Gravity.END;
- lp.setTitle("CompatModePanel");
- lp.windowAnimations = android.R.style.Animation_Dialog;
-
- mWindowManager.addView(mCompatModePanel, lp);
-
- mRecentButton.setOnTouchListener(mRecentsPreloadOnTouchListener);
-
- mPile = (NotificationRowLayout)mNotificationPanel.findViewById(R.id.content);
- mPile.removeAllViews();
- mPile.setLongPressListener(getNotificationLongClicker());
-
- ScrollView scroller = (ScrollView)mPile.getParent();
- scroller.setFillViewport(true);
- }
-
- @Override
- protected int getExpandedViewMaxHeight() {
- return getNotificationPanelHeight();
- }
-
- private int getNotificationPanelHeight() {
- final Resources res = mContext.getResources();
- final Display d = mWindowManager.getDefaultDisplay();
- final Point size = new Point();
- d.getRealSize(size);
- return Math.max(res.getDimensionPixelSize(R.dimen.notification_panel_min_height), size.y);
- }
-
- @Override
- public void start() {
- super.start(); // will add the main bar view
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- loadDimens();
- mNotificationPanelParams.height = getNotificationPanelHeight();
- mWindowManager.updateViewLayout(mNotificationPanel, mNotificationPanelParams);
- mShowSearchHoldoff = mContext.getResources().getInteger(
- R.integer.config_show_search_delay);
- updateSearchPanel();
- }
-
- @Override
- protected void refreshLayout(int layoutDirection) {
- mNotificationPanel.refreshLayout(layoutDirection);
- }
-
- protected void loadDimens() {
- final Resources res = mContext.getResources();
-
- mNaturalBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height);
-
- int newIconSize = res.getDimensionPixelSize(
- com.android.internal.R.dimen.system_bar_icon_size);
- int newIconHPadding = res.getDimensionPixelSize(
- R.dimen.status_bar_icon_padding);
- int newNavIconWidth = res.getDimensionPixelSize(R.dimen.navigation_key_width);
- int newMenuNavIconWidth = res.getDimensionPixelSize(R.dimen.navigation_menu_key_width);
-
- if (mNavigationArea != null && newNavIconWidth != mNavIconWidth) {
- mNavIconWidth = newNavIconWidth;
-
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- mNavIconWidth, ViewGroup.LayoutParams.MATCH_PARENT);
- mBackButton.setLayoutParams(lp);
- mHomeButton.setLayoutParams(lp);
- mRecentButton.setLayoutParams(lp);
- }
-
- if (mNavigationArea != null && newMenuNavIconWidth != mMenuNavIconWidth) {
- mMenuNavIconWidth = newMenuNavIconWidth;
-
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- mMenuNavIconWidth, ViewGroup.LayoutParams.MATCH_PARENT);
- mMenuButton.setLayoutParams(lp);
- }
-
- if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
-// Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
- mIconHPadding = newIconHPadding;
- mIconSize = newIconSize;
- reloadAllNotificationIcons(); // reload the tray
- }
-
- final int numIcons = res.getInteger(R.integer.config_maxNotificationIcons);
- if (numIcons != mMaxNotificationIcons) {
- mMaxNotificationIcons = numIcons;
- if (DEBUG) Slog.d(TAG, "max notification icons: " + mMaxNotificationIcons);
- reloadAllNotificationIcons();
- }
- }
-
- @Override
- public View getStatusBarView() {
- return mStatusBarView;
- }
-
- protected View makeStatusBarView() {
- final Context context = mContext;
-
- loadDimens();
-
- final TabletStatusBarView sb = (TabletStatusBarView)View.inflate(
- context, R.layout.system_bar, null);
- mStatusBarView = sb;
-
- sb.setHandler(mHandler);
-
- try {
- // Sanity-check that someone hasn't set up the config wrong and asked for a navigation
- // bar on a tablet that has only the system bar
- if (mWindowManagerService.hasNavigationBar()) {
- Slog.e(TAG, "Tablet device cannot show navigation bar and system bar");
- }
- } catch (RemoteException ex) {
- }
-
- mBarContents = (ViewGroup) sb.findViewById(R.id.bar_contents);
-
- // the whole right-hand side of the bar
- mNotificationArea = sb.findViewById(R.id.notificationArea);
- mNotificationArea.setOnTouchListener(new NotificationTriggerTouchListener());
-
- // the button to open the notification area
- mNotificationTrigger = sb.findViewById(R.id.notificationTrigger);
-
- // the more notifications icon
- mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
-
- // where the icons go
- mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
-
- mNotificationPeekTapDuration = ViewConfiguration.getTapTimeout();
- mNotificationFlingVelocity = 300; // px/s
-
- mTicker = new TabletTicker(this);
-
- // The icons
- mLocationController = new LocationController(mContext); // will post a notification
-
- // watch the PREF_DO_NOT_DISTURB and convert to appropriate disable() calls
- mDoNotDisturb = new DoNotDisturb(mContext);
-
- mBatteryController = new BatteryController(mContext);
- mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery));
- mBluetoothController = new BluetoothController(mContext);
- mBluetoothController.addIconView((ImageView)sb.findViewById(R.id.bluetooth));
-
- mNetworkController = new NetworkController(mContext);
- final SignalClusterView signalCluster =
- (SignalClusterView)sb.findViewById(R.id.signal_cluster);
- mNetworkController.addSignalCluster(signalCluster);
-
- // The navigation buttons
- mBackButton = (ImageView)sb.findViewById(R.id.back);
- mNavigationArea = (ViewGroup) sb.findViewById(R.id.navigationArea);
- mHomeButton = mNavigationArea.findViewById(R.id.home);
- mMenuButton = mNavigationArea.findViewById(R.id.menu);
- mRecentButton = mNavigationArea.findViewById(R.id.recent_apps);
- mRecentButton.setOnClickListener(mOnClickListener);
-
- LayoutTransition lt = new LayoutTransition();
- lt.setDuration(250);
- // don't wait for these transitions; we just want icons to fade in/out, not move around
- lt.setDuration(LayoutTransition.CHANGE_APPEARING, 0);
- lt.setDuration(LayoutTransition.CHANGE_DISAPPEARING, 0);
- lt.addTransitionListener(new LayoutTransition.TransitionListener() {
- public void endTransition(LayoutTransition transition, ViewGroup container,
- View view, int transitionType) {
- // ensure the menu button doesn't stick around on the status bar after it's been
- // removed
- mBarContents.invalidate();
- }
- public void startTransition(LayoutTransition transition, ViewGroup container,
- View view, int transitionType) {}
- });
- mNavigationArea.setLayoutTransition(lt);
- // no multi-touch on the nav buttons
- mNavigationArea.setMotionEventSplittingEnabled(false);
-
- // The bar contents buttons
- mFeedbackIconArea = (ViewGroup)sb.findViewById(R.id.feedbackIconArea);
- mInputMethodSwitchButton = (InputMethodButton) sb.findViewById(R.id.imeSwitchButton);
- // Overwrite the lister
- mInputMethodSwitchButton.setOnClickListener(mOnClickListener);
-
- mCompatModeButton = (CompatModeButton) sb.findViewById(R.id.compatModeButton);
- mCompatModeButton.setOnClickListener(mOnClickListener);
- mCompatModeButton.setVisibility(View.GONE);
-
- // for redirecting errant bar taps to the IME
- mFakeSpaceBar = sb.findViewById(R.id.fake_space_bar);
-
- // "shadows" of the status bar features, for lights-out mode
- mShadow = sb.findViewById(R.id.bar_shadow);
- mShadow.setOnTouchListener(
- new View.OnTouchListener() {
- public boolean onTouch(View v, MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- // even though setting the systemUI visibility below will turn these views
- // on, we need them to come up faster so that they can catch this motion
- // event
- mShadow.setVisibility(View.GONE);
- mBarContents.setVisibility(View.VISIBLE);
-
- try {
- mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
- } catch (RemoteException ex) {
- // system process dead
- }
- }
- return false;
- }
- });
-
- // tuning parameters
- final int LIGHTS_GOING_OUT_SYSBAR_DURATION = 750;
- final int LIGHTS_GOING_OUT_SHADOW_DURATION = 750;
- final int LIGHTS_GOING_OUT_SHADOW_DELAY = 0;
-
- final int LIGHTS_COMING_UP_SYSBAR_DURATION = 200;
-// final int LIGHTS_COMING_UP_SYSBAR_DELAY = 50;
- final int LIGHTS_COMING_UP_SHADOW_DURATION = 0;
-
- LayoutTransition xition = new LayoutTransition();
- xition.setAnimator(LayoutTransition.APPEARING,
- ObjectAnimator.ofFloat(null, "alpha", 0.5f, 1f));
- xition.setDuration(LayoutTransition.APPEARING, LIGHTS_COMING_UP_SYSBAR_DURATION);
- xition.setStartDelay(LayoutTransition.APPEARING, 0);
- xition.setAnimator(LayoutTransition.DISAPPEARING,
- ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
- xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_GOING_OUT_SYSBAR_DURATION);
- xition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
- ((ViewGroup)sb.findViewById(R.id.bar_contents_holder)).setLayoutTransition(xition);
-
- xition = new LayoutTransition();
- xition.setAnimator(LayoutTransition.APPEARING,
- ObjectAnimator.ofFloat(null, "alpha", 0f, 1f));
- xition.setDuration(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DURATION);
- xition.setStartDelay(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DELAY);
- xition.setAnimator(LayoutTransition.DISAPPEARING,
- ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
- xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_COMING_UP_SHADOW_DURATION);
- xition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
- ((ViewGroup)sb.findViewById(R.id.bar_shadow_holder)).setLayoutTransition(xition);
-
- // set the initial view visibility
- setAreThereNotifications();
-
- // receive broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- context.registerReceiver(mBroadcastReceiver, filter);
-
- return sb;
- }
-
- @Override
- protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- (int) mContext.getResources().getDimension(R.dimen.status_bar_recents_width),
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT);
- lp.gravity = Gravity.BOTTOM | Gravity.START;
- lp.setTitle("RecentsPanel");
- lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
- lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
- | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
-
- return lp;
- }
-
- @Override
- protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
- boolean opaque = false;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
- if (ActivityManager.isHighEndGfx()) {
- lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- } else {
- lp.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
- lp.dimAmount = 0.7f;
- }
- lp.gravity = Gravity.BOTTOM | Gravity.START;
- lp.setTitle("SearchPanel");
- // TODO: Define custom animation for Search panel
- lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
- lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
- | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
- return lp;
- }
-
- @Override
- protected void updateSearchPanel() {
- super.updateSearchPanel();
- mSearchPanelView.setStatusBarView(mStatusBarView);
- mStatusBarView.setDelegateView(mSearchPanelView);
- }
-
- @Override
- public void showSearchPanel() {
- super.showSearchPanel();
- WindowManager.LayoutParams lp =
- (android.view.WindowManager.LayoutParams) mStatusBarView.getLayoutParams();
- lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- mWindowManager.updateViewLayout(mStatusBarView, lp);
- }
-
- @Override
- public void hideSearchPanel() {
- super.hideSearchPanel();
- WindowManager.LayoutParams lp =
- (android.view.WindowManager.LayoutParams) mStatusBarView.getLayoutParams();
- lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- mWindowManager.updateViewLayout(mStatusBarView, lp);
- }
-
- public int getStatusBarHeight() {
- return mStatusBarView != null ? mStatusBarView.getHeight()
- : mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height);
- }
-
- protected int getStatusBarGravity() {
- return Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
- }
-
- public void onBarHeightChanged(int height) {
- final WindowManager.LayoutParams lp
- = (WindowManager.LayoutParams)mStatusBarView.getLayoutParams();
- if (lp == null) {
- // haven't been added yet
- return;
- }
- if (lp.height != height) {
- lp.height = height;
- mWindowManager.updateViewLayout(mStatusBarView, lp);
- }
- }
-
- @Override
- protected BaseStatusBar.H createHandler() {
- return new TabletStatusBar.H();
- }
-
- private class H extends BaseStatusBar.H {
- public void handleMessage(Message m) {
- super.handleMessage(m);
- switch (m.what) {
- case MSG_OPEN_NOTIFICATION_PEEK:
- if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1);
-
- if (m.arg1 >= 0) {
- final int N = mNotificationData.size();
-
- if (!mNotificationDNDMode) {
- if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
- NotificationData.Entry entry = mNotificationData.get(N-1-mNotificationPeekIndex);
- entry.icon.setBackgroundColor(0);
- mNotificationPeekIndex = -1;
- mNotificationPeekKey = null;
- }
- }
-
- final int peekIndex = m.arg1;
- if (peekIndex < N) {
- //Slog.d(TAG, "loading peek: " + peekIndex);
- NotificationData.Entry entry =
- mNotificationDNDMode
- ? mNotificationDNDDummyEntry
- : mNotificationData.get(N-1-peekIndex);
- NotificationData.Entry copy = new NotificationData.Entry(
- entry.key,
- entry.notification,
- entry.icon);
- inflateViews(copy, mNotificationPeekRow);
-
- if (mNotificationDNDMode) {
- copy.content.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- SharedPreferences.Editor editor = Prefs.edit(mContext);
- editor.putBoolean(Prefs.DO_NOT_DISTURB_PREF, false);
- editor.apply();
- animateCollapsePanels();
- visibilityChanged(false);
- }
- });
- }
-
- entry.icon.setBackgroundColor(0x20FFFFFF);
-
-// mNotificationPeekRow.setLayoutTransition(
-// peekIndex < mNotificationPeekIndex
-// ? mNotificationPeekScrubLeft
-// : mNotificationPeekScrubRight);
-
- mNotificationPeekRow.removeAllViews();
- mNotificationPeekRow.addView(copy.row);
-
- mNotificationPeekWindow.setVisibility(View.VISIBLE);
- mNotificationPanel.show(false, true);
-
- mNotificationPeekIndex = peekIndex;
- mNotificationPeekKey = entry.key;
- }
- }
- break;
- case MSG_CLOSE_NOTIFICATION_PEEK:
- if (DEBUG) Slog.d(TAG, "closing notification peek window");
- mNotificationPeekWindow.setVisibility(View.GONE);
- mNotificationPeekRow.removeAllViews();
-
- final int N = mNotificationData.size();
- if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
- NotificationData.Entry entry =
- mNotificationDNDMode
- ? mNotificationDNDDummyEntry
- : mNotificationData.get(N-1-mNotificationPeekIndex);
- entry.icon.setBackgroundColor(0);
- }
-
- mNotificationPeekIndex = -1;
- mNotificationPeekKey = null;
- break;
- case MSG_OPEN_NOTIFICATION_PANEL:
- if (DEBUG) Slog.d(TAG, "opening notifications panel");
- if (!mNotificationPanel.isShowing()) {
- mNotificationPanel.show(true, true);
- mNotificationArea.setVisibility(View.INVISIBLE);
- mTicker.halt();
- }
- break;
- case MSG_CLOSE_NOTIFICATION_PANEL:
- if (DEBUG) Slog.d(TAG, "closing notifications panel");
- if (mNotificationPanel.isShowing()) {
- mNotificationPanel.show(false, true);
- mNotificationArea.setVisibility(View.VISIBLE);
- }
- break;
- case MSG_OPEN_INPUT_METHODS_PANEL:
- if (DEBUG) Slog.d(TAG, "opening input methods panel");
- if (mInputMethodsPanel != null) mInputMethodsPanel.openPanel();
- break;
- case MSG_CLOSE_INPUT_METHODS_PANEL:
- if (DEBUG) Slog.d(TAG, "closing input methods panel");
- if (mInputMethodsPanel != null) mInputMethodsPanel.closePanel(false);
- break;
- case MSG_OPEN_COMPAT_MODE_PANEL:
- if (DEBUG) Slog.d(TAG, "opening compat panel");
- if (mCompatModePanel != null) mCompatModePanel.openPanel();
- break;
- case MSG_CLOSE_COMPAT_MODE_PANEL:
- if (DEBUG) Slog.d(TAG, "closing compat panel");
- if (mCompatModePanel != null) mCompatModePanel.closePanel();
- break;
- case MSG_SHOW_CHROME:
- if (DEBUG) Slog.d(TAG, "hiding shadows (lights on)");
- mBarContents.setVisibility(View.VISIBLE);
- mShadow.setVisibility(View.GONE);
- mSystemUiVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
- notifyUiVisibilityChanged();
- break;
- case MSG_HIDE_CHROME:
- if (DEBUG) Slog.d(TAG, "showing shadows (lights out)");
- animateCollapsePanels();
- visibilityChanged(false);
- mBarContents.setVisibility(View.GONE);
- mShadow.setVisibility(View.VISIBLE);
- mSystemUiVisibility |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
- notifyUiVisibilityChanged();
- break;
- case MSG_STOP_TICKER:
- mTicker.halt();
- break;
- }
- }
- }
-
- public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
- if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
- }
-
- public void updateIcon(String slot, int index, int viewIndex,
- StatusBarIcon old, StatusBarIcon icon) {
- if (DEBUG) Slog.d(TAG, "updateIcon(" + slot + ") -> " + icon);
- }
-
- public void removeIcon(String slot, int index, int viewIndex) {
- if (DEBUG) Slog.d(TAG, "removeIcon(" + slot + ")");
- }
-
- public void addNotification(IBinder key, StatusBarNotification notification) {
- if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
- addNotificationViews(key, notification);
-
- final boolean immersive = isImmersive();
- if (false && immersive) {
- // TODO: immersive mode popups for tablet
- } else if (notification.getNotification().fullScreenIntent != null) {
- // not immersive & a full-screen alert should be shown
- Slog.w(TAG, "Notification has fullScreenIntent and activity is not immersive;"
- + " sending fullScreenIntent");
- try {
- notification.getNotification().fullScreenIntent.send();
- } catch (PendingIntent.CanceledException e) {
- }
- } else {
- tick(key, notification, true);
- }
-
- setAreThereNotifications();
- }
-
- public void removeNotification(IBinder key) {
- if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ")");
- removeNotificationViews(key);
- mTicker.remove(key);
- setAreThereNotifications();
- }
-
- public void showClock(boolean show) {
- View clock = mBarContents.findViewById(R.id.clock);
- View network_text = mBarContents.findViewById(R.id.network_text);
- if (clock != null) {
- clock.setVisibility(show ? View.VISIBLE : View.GONE);
- }
- if (network_text != null) {
- network_text.setVisibility((!show) ? View.VISIBLE : View.GONE);
- }
- }
-
- public void disable(int state) {
- int old = mDisabled;
- int diff = state ^ old;
- mDisabled = state;
-
- // act accordingly
- if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
- boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
- Slog.i(TAG, "DISABLE_CLOCK: " + (show ? "no" : "yes"));
- showClock(show);
- }
- if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
- boolean show = (state & StatusBarManager.DISABLE_SYSTEM_INFO) == 0;
- Slog.i(TAG, "DISABLE_SYSTEM_INFO: " + (show ? "no" : "yes"));
- mNotificationTrigger.setVisibility(show ? View.VISIBLE : View.GONE);
- }
- if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
- if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
- Slog.i(TAG, "DISABLE_EXPAND: yes");
- animateCollapsePanels();
- visibilityChanged(false);
- }
- }
- if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
- mNotificationDNDMode = Prefs.read(mContext)
- .getBoolean(Prefs.DO_NOT_DISTURB_PREF, Prefs.DO_NOT_DISTURB_DEFAULT);
-
- if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
- Slog.i(TAG, "DISABLE_NOTIFICATION_ICONS: yes" + (mNotificationDNDMode?" (DND)":""));
- mTicker.halt();
- } else {
- Slog.i(TAG, "DISABLE_NOTIFICATION_ICONS: no" + (mNotificationDNDMode?" (DND)":""));
- }
-
- // refresh icons to show either notifications or the DND message
- reloadAllNotificationIcons();
- } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- mTicker.halt();
- }
- }
- if ((diff & (StatusBarManager.DISABLE_RECENT
- | StatusBarManager.DISABLE_BACK
- | StatusBarManager.DISABLE_HOME)) != 0) {
- setNavigationVisibility(state);
-
- if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
- // close recents if it's visible
- mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
- mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
- }
- }
- }
-
- private void setNavigationVisibility(int visibility) {
- boolean disableHome = ((visibility & StatusBarManager.DISABLE_HOME) != 0);
- boolean disableRecent = ((visibility & StatusBarManager.DISABLE_RECENT) != 0);
- boolean disableBack = ((visibility & StatusBarManager.DISABLE_BACK) != 0);
-
- mBackButton.setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
- mHomeButton.setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
- mRecentButton.setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
-
- mInputMethodSwitchButton.setScreenLocked(
- (visibility & StatusBarManager.DISABLE_SYSTEM_INFO) != 0);
- }
-
- private boolean hasTicker(Notification n) {
- return n.tickerView != null || !TextUtils.isEmpty(n.tickerText);
- }
-
- @Override
- protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
- // Don't show the ticker when the windowshade is open.
- if (mNotificationPanel.isShowing()) {
- return;
- }
- // If they asked for FLAG_ONLY_ALERT_ONCE, then only show this notification
- // if it's a new notification.
- if (!firstTime && (n.getNotification().flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
- return;
- }
- // Show the ticker if one is requested. Also don't do this
- // until status bar window is attached to the window manager,
- // because... well, what's the point otherwise? And trying to
- // run a ticker without being attached will crash!
- if (hasTicker(n.getNotification()) && mStatusBarView.getWindowToken() != null) {
- if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
- | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
- mTicker.add(key, n);
- mFeedbackIconArea.setVisibility(View.GONE);
- }
- }
- }
-
- // called by TabletTicker when it's done with all queued ticks
- public void doneTicking() {
- mFeedbackIconArea.setVisibility(View.VISIBLE);
- }
-
- public void animateExpandNotificationsPanel() {
- mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
- mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
- }
-
- public void animateCollapsePanels() {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
- }
-
- public void animateCollapsePanels(int flags) {
- if ((flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
- mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PANEL);
- mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PANEL);
- }
- if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
- mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
- mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
- }
- if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
- mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
- mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
- }
- if ((flags & CommandQueue.FLAG_EXCLUDE_INPUT_METHODS_PANEL) == 0) {
- mHandler.removeMessages(MSG_CLOSE_INPUT_METHODS_PANEL);
- mHandler.sendEmptyMessage(MSG_CLOSE_INPUT_METHODS_PANEL);
- }
- if ((flags & CommandQueue.FLAG_EXCLUDE_COMPAT_MODE_PANEL) == 0) {
- mHandler.removeMessages(MSG_CLOSE_COMPAT_MODE_PANEL);
- mHandler.sendEmptyMessage(MSG_CLOSE_COMPAT_MODE_PANEL);
- }
-
- }
-
- @Override
- public void animateExpandSettingsPanel() {
- // TODO: Implement when TabletStatusBar begins to be used.
- }
-
- @Override // CommandQueue
- public void setNavigationIconHints(int hints) {
- if (hints == mNavigationIconHints) return;
-
- if (DEBUG) {
- android.widget.Toast.makeText(mContext,
- "Navigation icon hints = " + hints,
- 500).show();
- }
-
- mNavigationIconHints = hints;
-
- mBackButton.setAlpha(
- (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_NOP)) ? 0.5f : 1.0f);
- mHomeButton.setAlpha(
- (0 != (hints & StatusBarManager.NAVIGATION_HINT_HOME_NOP)) ? 0.5f : 1.0f);
- mRecentButton.setAlpha(
- (0 != (hints & StatusBarManager.NAVIGATION_HINT_RECENT_NOP)) ? 0.5f : 1.0f);
-
- mBackButton.setImageResource(
- (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT))
- ? R.drawable.ic_sysbar_back_ime
- : R.drawable.ic_sysbar_back);
- }
-
- private void notifyUiVisibilityChanged() {
- try {
- mWindowManagerService.statusBarVisibilityChanged(mSystemUiVisibility);
- } catch (RemoteException ex) {
- }
- }
-
- @Override // CommandQueue
- public void setSystemUiVisibility(int vis, int mask) {
- final int oldVal = mSystemUiVisibility;
- final int newVal = (oldVal&~mask) | (vis&mask);
- final int diff = newVal ^ oldVal;
-
- if (diff != 0) {
- mSystemUiVisibility = newVal;
-
- if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
- mHandler.removeMessages(MSG_HIDE_CHROME);
- mHandler.removeMessages(MSG_SHOW_CHROME);
- mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE)
- ? MSG_SHOW_CHROME : MSG_HIDE_CHROME);
- }
-
- notifyUiVisibilityChanged();
- }
- }
-
- public void setLightsOn(boolean on) {
- // Policy note: if the frontmost activity needs the menu key, we assume it is a legacy app
- // that can't handle lights-out mode.
- if (mMenuButton.getVisibility() == View.VISIBLE) {
- on = true;
- }
-
- Slog.v(TAG, "setLightsOn(" + on + ")");
- if (on) {
- setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
- } else {
- setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
- }
- }
-
- public void topAppWindowChanged(boolean showMenu) {
- if (DEBUG) {
- Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
- }
- mMenuButton.setVisibility(showMenu ? View.VISIBLE : View.GONE);
-
- // See above re: lights-out policy for legacy apps.
- if (showMenu) setLightsOn(true);
-
- mCompatModeButton.refresh();
- if (mCompatModeButton.getVisibility() == View.VISIBLE) {
- if (DEBUG_COMPAT_HELP
- || ! Prefs.read(mContext).getBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, false)) {
- showCompatibilityHelp();
- }
- } else {
- hideCompatibilityHelp();
- mCompatModePanel.closePanel();
- }
- }
-
- private void showCompatibilityHelp() {
- if (mCompatibilityHelpDialog != null) {
- return;
- }
-
- mCompatibilityHelpDialog = View.inflate(mContext, R.layout.compat_mode_help, null);
- View button = mCompatibilityHelpDialog.findViewById(R.id.button);
-
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- hideCompatibilityHelp();
- SharedPreferences.Editor editor = Prefs.edit(mContext);
- editor.putBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, true);
- editor.apply();
- }
- });
-
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- PixelFormat.TRANSLUCENT);
- lp.setTitle("CompatibilityModeDialog");
- lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
- | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
- lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; // simple fade
-
- mWindowManager.addView(mCompatibilityHelpDialog, lp);
- }
-
- private void hideCompatibilityHelp() {
- if (mCompatibilityHelpDialog != null) {
- mWindowManager.removeView(mCompatibilityHelpDialog);
- mCompatibilityHelpDialog = null;
- }
- }
-
- public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
- mInputMethodSwitchButton.setImeWindowStatus(token,
- (vis & InputMethodService.IME_ACTIVE) != 0);
- updateNotificationIcons();
- mInputMethodsPanel.setImeToken(token);
-
- boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
- || ((vis & InputMethodService.IME_VISIBLE) != 0);
- mAltBackButtonEnabledForIme = altBack;
-
- mCommandQueue.setNavigationIconHints(
- altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
- : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
-
- if (FAKE_SPACE_BAR) {
- mFakeSpaceBar.setVisibility(((vis & InputMethodService.IME_VISIBLE) != 0)
- ? View.VISIBLE : View.GONE);
- }
- }
-
- @Override
- public void setHardKeyboardStatus(boolean available, boolean enabled) {
- if (DEBUG) {
- Slog.d(TAG, "Set hard keyboard status: available=" + available
- + ", enabled=" + enabled);
- }
- mInputMethodSwitchButton.setHardKeyboardStatus(available);
- updateNotificationIcons();
- mInputMethodsPanel.setHardKeyboardStatus(available, enabled);
- }
-
- @Override
- public void onHardKeyboardEnabledChange(boolean enabled) {
- try {
- mBarService.setHardKeyboardEnabled(enabled);
- } catch (RemoteException ex) {
- }
- }
-
- private boolean isImmersive() {
- try {
- return ActivityManagerNative.getDefault().isTopActivityImmersive();
- //Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
- } catch (RemoteException ex) {
- // the end is nigh
- return false;
- }
- }
-
- @Override
- protected void setAreThereNotifications() {
- if (mNotificationPanel != null) {
- mNotificationPanel.setClearable(isDeviceProvisioned() && mNotificationData.hasClearableItems());
- }
- }
-
- private View.OnClickListener mOnClickListener = new View.OnClickListener() {
- public void onClick(View v) {
- if (v == mRecentButton) {
- onClickRecentButton();
- } else if (v == mInputMethodSwitchButton) {
- onClickInputMethodSwitchButton();
- } else if (v == mCompatModeButton) {
- onClickCompatModeButton();
- }
- }
- };
-
- public void onClickRecentButton() {
- if (DEBUG) Slog.d(TAG, "clicked recent apps; disabled=" + mDisabled);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
- toggleRecentApps();
- }
- }
-
- public void onClickInputMethodSwitchButton() {
- if (DEBUG) Slog.d(TAG, "clicked input methods panel; disabled=" + mDisabled);
- int msg = (mInputMethodsPanel.getVisibility() == View.GONE) ?
- MSG_OPEN_INPUT_METHODS_PANEL : MSG_CLOSE_INPUT_METHODS_PANEL;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- public void onClickCompatModeButton() {
- int msg = (mCompatModePanel.getVisibility() == View.GONE) ?
- MSG_OPEN_COMPAT_MODE_PANEL : MSG_CLOSE_COMPAT_MODE_PANEL;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- private class NotificationTriggerTouchListener implements View.OnTouchListener {
- VelocityTracker mVT;
- float mInitialTouchX, mInitialTouchY;
- int mTouchSlop;
-
- public NotificationTriggerTouchListener() {
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
-
- private Runnable mHiliteOnR = new Runnable() { public void run() {
- mNotificationArea.setBackgroundResource(
- com.android.internal.R.drawable.list_selector_pressed_holo_dark);
- }};
- public void hilite(final boolean on) {
- if (on) {
- mNotificationArea.postDelayed(mHiliteOnR, 100);
- } else {
- mNotificationArea.removeCallbacks(mHiliteOnR);
- mNotificationArea.setBackground(null);
- }
- }
-
- public boolean onTouch(View v, MotionEvent event) {
-// Slog.d(TAG, String.format("touch: (%.1f, %.1f) initial: (%.1f, %.1f)",
-// event.getX(),
-// event.getY(),
-// mInitialTouchX,
-// mInitialTouchY));
-
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return true;
- }
-
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mVT = VelocityTracker.obtain();
- mInitialTouchX = event.getX();
- mInitialTouchY = event.getY();
- hilite(true);
- // fall through
- case MotionEvent.ACTION_OUTSIDE:
- case MotionEvent.ACTION_MOVE:
- // check for fling
- if (mVT != null) {
- mVT.addMovement(event);
- mVT.computeCurrentVelocity(1000); // pixels per second
- // require a little more oomph once we're already in peekaboo mode
- if (mVT.getYVelocity() < -mNotificationFlingVelocity) {
- animateExpandNotificationsPanel();
- visibilityChanged(true);
- hilite(false);
- mVT.recycle();
- mVT = null;
- }
- }
- return true;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- hilite(false);
- if (mVT != null) {
- if (action == MotionEvent.ACTION_UP
- // was this a sloppy tap?
- && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
- && Math.abs(event.getY() - mInitialTouchY) < (mTouchSlop / 3)
- // dragging off the bottom doesn't count
- && (int)event.getY() < v.getBottom()) {
- animateExpandNotificationsPanel();
- visibilityChanged(true);
- v.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
- v.playSoundEffect(SoundEffectConstants.CLICK);
- }
-
- mVT.recycle();
- mVT = null;
- return true;
- }
- }
- return false;
- }
- }
-
- public void resetNotificationPeekFadeTimer() {
- if (DEBUG) {
- Slog.d(TAG, "setting peek fade timer for " + NOTIFICATION_PEEK_FADE_DELAY
- + "ms from now");
- }
- mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PEEK);
- mHandler.sendEmptyMessageDelayed(MSG_CLOSE_NOTIFICATION_PEEK,
- NOTIFICATION_PEEK_FADE_DELAY);
- }
-
- private void reloadAllNotificationIcons() {
- if (mIconLayout == null) return;
- mIconLayout.removeAllViews();
- updateNotificationIcons();
- }
-
- @Override
- protected void updateNotificationIcons() {
- // XXX: need to implement a new limited linear layout class
- // to avoid removing & readding everything
-
- if (mIconLayout == null) return;
-
- // first, populate the main notification panel
- loadNotificationPanel();
-
- final LinearLayout.LayoutParams params
- = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
-
- // alternate behavior in DND mode
- if (mNotificationDNDMode) {
- if (mIconLayout.getChildCount() == 0) {
- final Notification dndNotification = new Notification.Builder(mContext)
- .setContentTitle(mContext.getText(R.string.notifications_off_title))
- .setContentText(mContext.getText(R.string.notifications_off_text))
- .setSmallIcon(R.drawable.ic_notification_dnd)
- .setOngoing(true)
- .getNotification();
-
- final StatusBarIconView iconView = new StatusBarIconView(mContext, "_dnd",
- dndNotification);
- iconView.setImageResource(R.drawable.ic_notification_dnd);
- iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- iconView.setPadding(mIconHPadding, 0, mIconHPadding, 0);
-
- mNotificationDNDDummyEntry = new NotificationData.Entry(
- null, new StatusBarNotification("", 0, "", 0, 0, Notification.PRIORITY_MAX,
- dndNotification, android.os.Process.myUserHandle()), iconView);
-
- mIconLayout.addView(iconView, params);
- }
-
- return;
- } else if (0 != (mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) {
- // if icons are disabled but we're not in DND mode, this is probably Setup and we should
- // just leave the area totally empty
- return;
- }
-
- int N = mNotificationData.size();
-
- if (DEBUG) {
- Slog.d(TAG, "refreshing icons: " + N + " notifications, mIconLayout=" + mIconLayout);
- }
-
- ArrayList<View> toShow = new ArrayList<View>();
-
- // Extra Special Icons
- // The IME switcher and compatibility mode icons take the place of notifications. You didn't
- // need to see all those new emails, did you?
- int maxNotificationIconsCount = mMaxNotificationIcons;
- if (mInputMethodSwitchButton.getVisibility() != View.GONE) maxNotificationIconsCount --;
- if (mCompatModeButton.getVisibility() != View.GONE) maxNotificationIconsCount --;
-
- final boolean provisioned = isDeviceProvisioned();
- // If the device hasn't been through Setup, we only show system notifications
- for (int i=0; toShow.size()< maxNotificationIconsCount; i++) {
- if (i >= N) break;
- Entry ent = mNotificationData.get(N-i-1);
- if ((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
- || showNotificationEvenIfUnprovisioned(ent.notification)) {
- toShow.add(ent.icon);
- }
- }
-
- ArrayList<View> toRemove = new ArrayList<View>();
- for (int i=0; i<mIconLayout.getChildCount(); i++) {
- View child = mIconLayout.getChildAt(i);
- if (!toShow.contains(child)) {
- toRemove.add(child);
- }
- }
-
- for (View remove : toRemove) {
- mIconLayout.removeView(remove);
- }
-
- for (int i=0; i<toShow.size(); i++) {
- View v = toShow.get(i);
- v.setPadding(mIconHPadding, 0, mIconHPadding, 0);
- if (v.getParent() == null) {
- mIconLayout.addView(v, i, params);
- }
- }
- }
-
- private void loadNotificationPanel() {
- int N = mNotificationData.size();
-
- ArrayList<View> toShow = new ArrayList<View>();
-
- final boolean provisioned = isDeviceProvisioned();
- // If the device hasn't been through Setup, we only show system notifications
- for (int i=0; i<N; i++) {
- Entry ent = mNotificationData.get(N-i-1);
- if (provisioned || showNotificationEvenIfUnprovisioned(ent.notification)) {
- toShow.add(ent.row);
- }
- }
-
- ArrayList<View> toRemove = new ArrayList<View>();
- for (int i=0; i<mPile.getChildCount(); i++) {
- View child = mPile.getChildAt(i);
- if (!toShow.contains(child)) {
- toRemove.add(child);
- }
- }
-
- for (View remove : toRemove) {
- mPile.removeView(remove);
- }
-
- for (int i=0; i<toShow.size(); i++) {
- View v = toShow.get(i);
- if (v.getParent() == null) {
- // the notification panel has the most important things at the bottom
- mPile.addView(v, Math.min(toShow.size()-1-i, mPile.getChildCount()));
- }
- }
-
- mNotificationPanel.setNotificationCount(toShow.size());
- mNotificationPanel.setSettingsEnabled(isDeviceProvisioned());
- }
-
- @Override
- protected void workAroundBadLayerDrawableOpacity(View v) {
- Drawable bgd = v.getBackground();
- if (!(bgd instanceof LayerDrawable)) return;
-
- LayerDrawable d = (LayerDrawable) bgd;
- v.setBackground(null);
- d.setOpacity(PixelFormat.TRANSLUCENT);
- v.setBackground(d);
- }
-
- public void clearAll() {
- try {
- mBarService.onClearAllNotifications();
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- animateCollapsePanels();
- visibilityChanged(false);
- }
-
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
- || Intent.ACTION_SCREEN_OFF.equals(action)) {
- int flags = CommandQueue.FLAG_EXCLUDE_NONE;
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
- String reason = intent.getStringExtra("reason");
- if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
- flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
- }
- }
- animateCollapsePanels(flags);
- }
- }
- };
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.print("mDisabled=0x");
- pw.println(Integer.toHexString(mDisabled));
- pw.println("mNetworkController:");
- mNetworkController.dump(fd, pw, args);
- }
-
- @Override
- protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
- if (parent == null || entry == null) return false;
- return parent.indexOfChild(entry.row) == parent.getChildCount()-1;
- }
-
- @Override
- protected void haltTicker() {
- mTicker.halt();
- }
-
- @Override
- protected void updateExpandedViewPos(int expandedPosition) {
- }
-
- @Override
- protected boolean shouldDisableNavbarGestures() {
- return mNotificationPanel.getVisibility() == View.VISIBLE
- || (mDisabled & StatusBarManager.DISABLE_HOME) != 0;
- }
-}
-
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
deleted file mode 100644
index 30d49ca..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.DelegateViewHelper;
-
-import android.content.Context;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-public class TabletStatusBarView extends FrameLayout {
- private Handler mHandler;
-
- private final int MAX_PANELS = 5;
- private final View[] mIgnoreChildren = new View[MAX_PANELS];
- private final View[] mPanels = new View[MAX_PANELS];
- private final int[] mPos = new int[2];
- private DelegateViewHelper mDelegateHelper;
-
- public TabletStatusBarView(Context context) {
- this(context, null);
- }
-
- public TabletStatusBarView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mDelegateHelper = new DelegateViewHelper(this);
- }
-
- public void setDelegateView(View view) {
- mDelegateHelper.setDelegateView(view);
- }
-
- public void setBar(BaseStatusBar phoneStatusBar) {
- mDelegateHelper.setBar(phoneStatusBar);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mDelegateHelper != null) {
- mDelegateHelper.onInterceptTouchEvent(event);
- }
- return true;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- // Find the view we wish to grab events from in order to detect search gesture.
- // Depending on the device, this will be one of the id's listed below.
- // If we don't find one, we'll use the view provided in the constructor above (this view).
- View view = findViewById(R.id.navigationArea);
- if (view == null) {
- view = findViewById(R.id.nav_buttons);
- }
- mDelegateHelper.setSourceView(view);
- mDelegateHelper.setInitialTouchRegion(view);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- if (TabletStatusBar.DEBUG) {
- Slog.d(TabletStatusBar.TAG, "TabletStatusBarView intercepting touch event: " + ev);
- }
- // do not close the recents panel here- the intended behavior is that recents is dismissed
- // on touch up when clicking on status bar buttons
- // TODO: should we be closing the notification panel and input methods panel?
- mHandler.removeMessages(TabletStatusBar.MSG_CLOSE_NOTIFICATION_PANEL);
- mHandler.sendEmptyMessage(TabletStatusBar.MSG_CLOSE_NOTIFICATION_PANEL);
- mHandler.removeMessages(TabletStatusBar.MSG_CLOSE_INPUT_METHODS_PANEL);
- mHandler.sendEmptyMessage(TabletStatusBar.MSG_CLOSE_INPUT_METHODS_PANEL);
- mHandler.removeMessages(TabletStatusBar.MSG_STOP_TICKER);
- mHandler.sendEmptyMessage(TabletStatusBar.MSG_STOP_TICKER);
-
- for (int i=0; i < mPanels.length; i++) {
- if (mPanels[i] != null && mPanels[i].getVisibility() == View.VISIBLE) {
- if (eventInside(mIgnoreChildren[i], ev)) {
- if (TabletStatusBar.DEBUG) {
- Slog.d(TabletStatusBar.TAG,
- "TabletStatusBarView eating event for view: "
- + mIgnoreChildren[i]);
- }
- return true;
- }
- }
- }
- }
- if (TabletStatusBar.DEBUG) {
- Slog.d(TabletStatusBar.TAG, "TabletStatusBarView not intercepting event");
- }
- if (mDelegateHelper != null && mDelegateHelper.onInterceptTouchEvent(ev)) {
- return true;
- }
- return super.onInterceptTouchEvent(ev);
- }
-
- private boolean eventInside(View v, MotionEvent ev) {
- // assume that x and y are window coords because we are.
- final int x = (int)ev.getX();
- final int y = (int)ev.getY();
-
- final int[] p = mPos;
- v.getLocationInWindow(p);
-
- final int l = p[0];
- final int t = p[1];
- final int r = p[0] + v.getWidth();
- final int b = p[1] + v.getHeight();
-
- return x >= l && x < r && y >= t && y < b;
- }
-
- public void setHandler(Handler h) {
- mHandler = h;
- }
-
- /**
- * Let the status bar know that if you tap on ignore while panel is showing, don't do anything.
- *
- * Debounces taps on, say, a popup's trigger when the popup is already showing.
- */
- public void setIgnoreChildren(int index, View ignore, View panel) {
- mIgnoreChildren[index] = ignore;
- mPanels[index] = panel;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
deleted file mode 100644
index 095c441..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tablet;
-
-import java.util.Arrays;
-
-import android.animation.LayoutTransition;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.service.notification.StatusBarNotification;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.statusbar.StatusBarIcon;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.StatusBarIconView;
-
-public class TabletTicker
- extends Handler
- implements LayoutTransition.TransitionListener {
-
- private static final String TAG = "StatusBar.TabletTicker";
-
- private static final boolean CLICKABLE_TICKER = true;
-
- // 3 is enough to let us see most cases, but not get so far behind that it's too annoying.
- private static final int QUEUE_LENGTH = 3;
-
- private static final int MSG_ADVANCE = 1;
-
- private static final int ADVANCE_DELAY = 5000; // 5 seconds
-
- private final Context mContext;
- private final WindowManager mWindowManager;
-
- private ViewGroup mWindow;
- private IBinder mCurrentKey;
- private StatusBarNotification mCurrentNotification;
- private View mCurrentView;
-
- private IBinder[] mKeys = new IBinder[QUEUE_LENGTH];
- private StatusBarNotification[] mQueue = new StatusBarNotification[QUEUE_LENGTH];
- private int mQueuePos;
-
- private final int mLargeIconHeight;
-
- private TabletStatusBar mBar;
-
- private LayoutTransition mLayoutTransition;
- private boolean mWindowShouldClose;
-
- public TabletTicker(TabletStatusBar bar) {
- mBar = bar;
- mContext = bar.getContext();
- mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- final Resources res = mContext.getResources();
- mLargeIconHeight = res.getDimensionPixelSize(
- android.R.dimen.notification_large_icon_height);
- }
-
- public void add(IBinder key, StatusBarNotification notification) {
- if (false) {
- Slog.d(TAG, "add 1 mCurrentNotification=" + mCurrentNotification
- + " mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue));
- }
-
- // If it's already in here, remove whatever's in there and put the new one at the end.
- remove(key, false);
-
- mKeys[mQueuePos] = key;
- mQueue[mQueuePos] = notification;
-
- // If nothing is running now, start the next one.
- if (mQueuePos == 0 && mCurrentNotification == null) {
- sendEmptyMessage(MSG_ADVANCE);
- }
-
- if (mQueuePos < QUEUE_LENGTH - 1) {
- mQueuePos++;
- }
- }
-
- public void remove(IBinder key) {
- remove(key, true);
- }
-
- public void remove(IBinder key, boolean advance) {
- if (mCurrentKey == key) {
- // Showing now
- if (advance) {
- removeMessages(MSG_ADVANCE);
- sendEmptyMessage(MSG_ADVANCE);
- }
- } else {
- // In the queue
- for (int i=0; i<QUEUE_LENGTH; i++) {
- if (mKeys[i] == key) {
- for (; i<QUEUE_LENGTH-1; i++) {
- mKeys[i] = mKeys[i+1];
- mQueue[i] = mQueue[i+1];
- }
- mKeys[QUEUE_LENGTH-1] = null;
- mQueue[QUEUE_LENGTH-1] = null;
- if (mQueuePos > 0) {
- mQueuePos--;
- }
- break;
- }
- }
- }
- }
-
- public void halt() {
- removeMessages(MSG_ADVANCE);
- if (mCurrentView != null || mQueuePos != 0) {
- for (int i=0; i<QUEUE_LENGTH; i++) {
- mKeys[i] = null;
- mQueue[i] = null;
- }
- mQueuePos = 0;
- sendEmptyMessage(MSG_ADVANCE);
- }
- }
-
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ADVANCE:
- advance();
- break;
- }
- }
-
- private void advance() {
- // Out with the old...
- if (mCurrentView != null) {
- if (mWindow != null) {
- mWindow.removeView(mCurrentView);
- }
- mCurrentView = null;
- mCurrentKey = null;
- mCurrentNotification = null;
- }
-
- // In with the new...
- dequeue();
- while (mCurrentNotification != null) {
- mCurrentView = makeTickerView(mCurrentNotification);
- if (mCurrentView != null) {
- if (mWindow == null) {
- mWindow = makeWindow();
- mWindowManager.addView(mWindow, mWindow.getLayoutParams());
- }
-
- mWindow.addView(mCurrentView);
- sendEmptyMessageDelayed(MSG_ADVANCE, ADVANCE_DELAY);
- break;
- }
- dequeue();
- }
-
- // if there's nothing left, close the window
- mWindowShouldClose = (mCurrentView == null && mWindow != null);
- }
-
- private void dequeue() {
- mCurrentKey = mKeys[0];
- mCurrentNotification = mQueue[0];
- if (false) {
- Slog.d(TAG, "dequeue mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue));
- }
- final int N = mQueuePos;
- for (int i=0; i<N; i++) {
- mKeys[i] = mKeys[i+1];
- mQueue[i] = mQueue[i+1];
- }
- mKeys[N] = null;
- mQueue[N] = null;
- if (mQueuePos > 0) {
- mQueuePos--;
- }
- }
-
- private ViewGroup makeWindow() {
- final Resources res = mContext.getResources();
- final FrameLayout view = new FrameLayout(mContext);
- final int width = res.getDimensionPixelSize(R.dimen.notification_ticker_width);
- int windowFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
- if (CLICKABLE_TICKER) {
- windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- } else {
- windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- }
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, mLargeIconHeight,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, windowFlags,
- PixelFormat.TRANSLUCENT);
- lp.gravity = Gravity.BOTTOM | Gravity.END;
-// lp.windowAnimations = com.android.internal.R.style.Animation_Toast;
-
- mLayoutTransition = new LayoutTransition();
- mLayoutTransition.addTransitionListener(this);
- view.setLayoutTransition(mLayoutTransition);
- lp.setTitle("NotificationTicker");
- view.setLayoutParams(lp);
- return view;
- }
-
- public void startTransition(LayoutTransition transition, ViewGroup container,
- View view, int transitionType) {}
-
- public void endTransition(LayoutTransition transition, ViewGroup container,
- View view, int transitionType) {
- if (mWindowShouldClose) {
- mWindowManager.removeView(mWindow);
- mWindow = null;
- mWindowShouldClose = false;
- mBar.doneTicking();
- }
- }
-
- private View makeTickerView(StatusBarNotification notification) {
- final Notification n = notification.getNotification();
-
- LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- ViewGroup group;
- int layoutId;
- int iconId;
- if (n.largeIcon != null) {
- iconId = R.id.right_icon;
- } else {
- iconId = R.id.left_icon;
- }
- if (n.tickerView != null) {
- group = (ViewGroup)inflater.inflate(R.layout.system_bar_ticker_panel, null, false);
- ViewGroup content = (FrameLayout) group.findViewById(R.id.ticker_expanded);
- View expanded = null;
- Exception exception = null;
- try {
- expanded = n.tickerView.apply(mContext, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (expanded == null) {
- final String ident = notification.getPackageName()
- + "/0x" + Integer.toHexString(notification.getId());
- Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
- return null;
- }
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- content.addView(expanded, lp);
- } else if (n.tickerText != null) {
- group = (ViewGroup)inflater.inflate(R.layout.system_bar_ticker_compat, mWindow, false);
- final Drawable icon = StatusBarIconView.getIcon(mContext,
- new StatusBarIcon(notification.getPackageName(), notification.getUser(), n.icon, n.iconLevel, 0,
- n.tickerText));
- ImageView iv = (ImageView)group.findViewById(iconId);
- iv.setImageDrawable(icon);
- iv.setVisibility(View.VISIBLE);
- TextView tv = (TextView)group.findViewById(R.id.text);
- tv.setText(n.tickerText);
- } else {
- throw new RuntimeException("tickerView==null && tickerText==null");
- }
- ImageView largeIcon = (ImageView)group.findViewById(R.id.large_icon);
- if (n.largeIcon != null) {
- largeIcon.setImageBitmap(n.largeIcon);
- largeIcon.setVisibility(View.VISIBLE);
- final ViewGroup.LayoutParams lp = largeIcon.getLayoutParams();
- final int statusBarHeight = mBar.getStatusBarHeight();
- if (n.largeIcon.getHeight() <= statusBarHeight) {
- // for smallish largeIcons, it looks a little odd to have them floating halfway up
- // the ticker, so we vertically center them in the status bar area instead
- lp.height = statusBarHeight;
- } else {
- lp.height = mLargeIconHeight;
- }
- largeIcon.setLayoutParams(lp);
- }
-
- if (CLICKABLE_TICKER) {
- PendingIntent contentIntent = notification.getNotification().contentIntent;
- if (contentIntent != null) {
- // create the usual notification clicker, but chain it together with a halt() call
- // to abort the ticker too
- final View.OnClickListener clicker = mBar.makeClicker(contentIntent,
- notification.getPackageName(), notification.getTag(), notification.getId());
- group.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- halt();
- clicker.onClick(v);
- }
- });
- } else {
- group.setOnClickListener(null);
- }
- }
-
- return group;
- }
-}
-
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 dc5de02..cf909f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -16,15 +16,15 @@
package com.android.systemui.statusbar.tv;
-import android.service.notification.StatusBarNotification;
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.statusbar.BaseStatusBar;
-
import android.os.IBinder;
+import android.service.notification.StatusBarNotification;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.BaseStatusBar;
+
/*
* Status bar implementation for "large screen" products that mostly present no on-screen nav
*/
@@ -97,12 +97,6 @@
}
@Override
- protected WindowManager.LayoutParams getRecentsLayoutParams(
- LayoutParams layoutParams) {
- return null;
- }
-
- @Override
protected WindowManager.LayoutParams getSearchLayoutParams(
LayoutParams layoutParams) {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 06696fe..b75f8b3 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -29,20 +29,17 @@
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.provider.Settings;
-import android.util.Slog;
+import android.util.Log;
-public class StorageNotification extends StorageEventListener {
+import com.android.systemui.SystemUI;
+
+public class StorageNotification extends SystemUI {
private static final String TAG = "StorageNotification";
private static final boolean DEBUG = false;
private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true;
/**
- * Binder context for this service
- */
- private Context mContext;
-
- /**
* The notification that is shown when a USB mass storage host
* is connected.
* <p>
@@ -66,32 +63,40 @@
private Handler mAsyncEventHandler;
- public StorageNotification(Context context) {
- mContext = context;
+ private class StorageNotificationEventListener extends StorageEventListener {
+ public void onUsbMassStorageConnectionChanged(final boolean connected) {
+ mAsyncEventHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onUsbMassStorageConnectionChangedAsync(connected);
+ }
+ });
+ }
+ public void onStorageStateChanged(final String path,
+ final String oldState, final String newState) {
+ mAsyncEventHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onStorageStateChangedAsync(path, oldState, newState);
+ }
+ });
+ }
+ }
- mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+ @Override
+ public void start() {
+ mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
final boolean connected = mStorageManager.isUsbMassStorageConnected();
- if (DEBUG) Slog.d(TAG, String.format( "Startup with UMS connection %s (media state %s)",
+ if (DEBUG) Log.d(TAG, String.format( "Startup with UMS connection %s (media state %s)",
mUmsAvailable, Environment.getExternalStorageState()));
-
+
HandlerThread thr = new HandlerThread("SystemUI StorageNotification");
thr.start();
mAsyncEventHandler = new Handler(thr.getLooper());
- onUsbMassStorageConnectionChanged(connected);
- }
-
- /*
- * @override com.android.os.storage.StorageEventListener
- */
- @Override
- public void onUsbMassStorageConnectionChanged(final boolean connected) {
- mAsyncEventHandler.post(new Runnable() {
- @Override
- public void run() {
- onUsbMassStorageConnectionChangedAsync(connected);
- }
- });
+ StorageNotificationEventListener listener = new StorageNotificationEventListener();
+ listener.onUsbMassStorageConnectionChanged(connected);
+ mStorageManager.registerListener(listener);
}
private void onUsbMassStorageConnectionChangedAsync(boolean connected) {
@@ -102,7 +107,7 @@
*/
String st = Environment.getExternalStorageState();
- if (DEBUG) Slog.i(TAG, String.format("UMS connection changed to %s (media state %s)",
+ if (DEBUG) Log.i(TAG, String.format("UMS connection changed to %s (media state %s)",
connected, st));
if (connected && (st.equals(
@@ -115,21 +120,8 @@
updateUsbMassStorageNotification(connected);
}
- /*
- * @override com.android.os.storage.StorageEventListener
- */
- @Override
- public void onStorageStateChanged(final String path, final String oldState, final String newState) {
- mAsyncEventHandler.post(new Runnable() {
- @Override
- public void run() {
- onStorageStateChangedAsync(path, oldState, newState);
- }
- });
- }
-
private void onStorageStateChangedAsync(String path, String oldState, String newState) {
- if (DEBUG) Slog.i(TAG, String.format(
+ if (DEBUG) Log.i(TAG, String.format(
"Media {%s} state changed from {%s} -> {%s}", path, oldState, newState));
if (newState.equals(Environment.MEDIA_SHARED)) {
/*
@@ -250,7 +242,7 @@
true, true, null);
updateUsbMassStorageNotification(false);
} else {
- Slog.w(TAG, String.format("Ignoring unknown state {%s}", newState));
+ Log.w(TAG, String.format("Ignoring unknown state {%s}", newState));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
index ff06630..b5f98ad 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
@@ -16,22 +16,19 @@
package com.android.systemui.usb;
-import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.net.Uri;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
+import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-
import com.android.systemui.R;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 2c25236..7abfc88 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -23,9 +23,7 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.Typeface;
import android.hardware.usb.IUsbManager;
-import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.IBinder;
@@ -35,12 +33,9 @@
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
-import android.widget.LinearLayout;
-import android.widget.TextView;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-
import com.android.systemui.R;
public class UsbDebuggingActivity extends AlertActivity
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 6e88d0d..1e69fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -16,7 +16,6 @@
package com.android.systemui.usb;
-import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
@@ -25,8 +24,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
-import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.IBinder;
@@ -42,7 +41,6 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-
import com.android.systemui.R;
public class UsbPermissionActivity extends AlertActivity
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
index e61ef8a..65315f3 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
@@ -16,7 +16,6 @@
package com.android.systemui.usb;
-import com.android.internal.R;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
@@ -24,31 +23,30 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.DialogInterface.OnCancelListener;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.storage.IMountService;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageEventListener;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.widget.ImageView;
+import android.os.storage.IMountService;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
import android.widget.Button;
+import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.util.Log;
+
+import com.android.internal.R;
import java.util.List;
diff --git a/packages/VpnDialogs/Android.mk b/packages/VpnDialogs/Android.mk
index ac84125..4c80a26 100644
--- a/packages/VpnDialogs/Android.mk
+++ b/packages/VpnDialogs/Android.mk
@@ -22,6 +22,8 @@
LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := VpnDialogs
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index a2ac8fe..884f57e 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -265,7 +265,7 @@
// next: bug report, if enabled
if (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0) {
+ Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
mItems.add(
new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb,
R.string.global_action_bug_report) {
@@ -349,16 +349,24 @@
return dialog;
}
+ private UserInfo getCurrentUser() {
+ try {
+ return ActivityManagerNative.getDefault().getCurrentUser();
+ } catch (RemoteException re) {
+ return null;
+ }
+ }
+
+ private boolean isCurrentUserOwner() {
+ UserInfo currentUser = getCurrentUser();
+ return currentUser == null || currentUser.isPrimary();
+ }
+
private void addUsersToMenu(ArrayList<Action> items) {
List<UserInfo> users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE))
.getUsers();
if (users.size() > 1) {
- UserInfo currentUser;
- try {
- currentUser = ActivityManagerNative.getDefault().getCurrentUser();
- } catch (RemoteException re) {
- currentUser = null;
- }
+ UserInfo currentUser = getCurrentUser();
for (final UserInfo user : users) {
boolean isCurrentUser = currentUser == null
? user.id == 0 : (currentUser.id == user.id);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 6b28e8e..459c92c 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -49,6 +49,7 @@
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
@@ -58,6 +59,7 @@
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.ActionMode;
@@ -139,6 +141,22 @@
private ActionMenuPresenterCallback mActionMenuPresenterCallback;
private PanelMenuPresenterCallback mPanelMenuPresenterCallback;
+ // The icon resource has been explicitly set elsewhere
+ // and should not be overwritten with a default.
+ static final int FLAG_RESOURCE_SET_ICON = 1 << 0;
+
+ // The logo resource has been explicitly set elsewhere
+ // and should not be overwritten with a default.
+ static final int FLAG_RESOURCE_SET_LOGO = 1 << 1;
+
+ // The icon resource is currently configured to use the system fallback
+ // as no default was previously specified. Anything can override this.
+ static final int FLAG_RESOURCE_SET_ICON_FALLBACK = 1 << 2;
+
+ int mResourcesSetFlags;
+ int mIconRes;
+ int mLogoRes;
+
private DrawableFeatureState[] mDrawables;
private PanelFeatureState[] mPanels;
@@ -627,7 +645,6 @@
}
}
- st.isOpen = true;
st.isHandled = false;
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -645,8 +662,9 @@
}
lp.windowAnimations = st.windowAnimations;
-
+
wm.addView(st.decorView, lp);
+ st.isOpen = true;
// Log.v(TAG, "Adding main menu to window manager.");
}
@@ -1393,6 +1411,54 @@
}
}
+ @Override
+ public void setIcon(int resId) {
+ mIconRes = resId;
+ mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON;
+ mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK;
+ if (mActionBar != null) {
+ mActionBar.setIcon(resId);
+ }
+ }
+
+ @Override
+ public void setDefaultIcon(int resId) {
+ if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0) {
+ return;
+ }
+ mIconRes = resId;
+ if (mActionBar != null && (!mActionBar.hasIcon() ||
+ (mResourcesSetFlags & FLAG_RESOURCE_SET_ICON_FALLBACK) != 0)) {
+ if (resId != 0) {
+ mActionBar.setIcon(resId);
+ mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK;
+ } else {
+ mActionBar.setIcon(getContext().getPackageManager().getDefaultActivityIcon());
+ mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
+ }
+ }
+ }
+
+ @Override
+ public void setLogo(int resId) {
+ mLogoRes = resId;
+ mResourcesSetFlags |= FLAG_RESOURCE_SET_LOGO;
+ if (mActionBar != null) {
+ mActionBar.setLogo(resId);
+ }
+ }
+
+ @Override
+ public void setDefaultLogo(int resId) {
+ if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0) {
+ return;
+ }
+ mLogoRes = resId;
+ if (mActionBar != null && !mActionBar.hasLogo()) {
+ mActionBar.setLogo(resId);
+ }
+ }
+
/**
* Request that key events come to this activity. Use this if your activity
* has no views with focus, but the activity still wants a chance to process
@@ -2946,6 +3012,20 @@
"incompatible window decor! Ignoring request.");
}
+ if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
+ (mIconRes != 0 && !mActionBar.hasIcon())) {
+ mActionBar.setIcon(mIconRes);
+ } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
+ mIconRes == 0 && !mActionBar.hasIcon()) {
+ mActionBar.setIcon(
+ getContext().getPackageManager().getDefaultActivityIcon());
+ mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
+ }
+ if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
+ (mLogoRes != 0 && !mActionBar.hasLogo())) {
+ mActionBar.setLogo(mLogoRes);
+ }
+
// Post the panel invalidate for later; avoid application onCreateOptionsMenu
// being called in the middle of onCreate or similar.
mDecor.post(new Runnable() {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index b0cd11ae..0b8b028 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -94,8 +94,7 @@
import com.android.internal.R;
import com.android.internal.policy.PolicyManager;
-import com.android.internal.policy.impl.keyguard.KeyguardViewManager;
-import com.android.internal.policy.impl.keyguard.KeyguardViewMediator;
+import com.android.internal.policy.impl.keyguard.KeyguardServiceDelegate;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.ITelephony;
import com.android.internal.widget.PointerLocationView;
@@ -104,6 +103,7 @@
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.HashSet;
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
@@ -114,7 +114,7 @@
* WindowManagerPolicy implementation for the Android phone UI. This
* introduces a new method suffix, Lp, for an internal lock of the
* PhoneWindowManager. This is used to protect some internal state, and
- * can be acquired with either thw Lw and Li lock held, so has the restrictions
+ * can be acquired with either the Lw and Li lock held, so has the restrictions
* of both of those when held.
*/
public class PhoneWindowManager implements WindowManagerPolicy {
@@ -164,6 +164,11 @@
static final int SYSTEM_UI_CHANGING_LAYOUT =
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ /**
+ * Keyguard stuff
+ */
+ private WindowState mKeyguardScrim;
+
/* Table of Application Launch keys. Maps from key codes to intent categories.
*
* These are special keys that are used to launch particular kinds of applications,
@@ -209,13 +214,13 @@
// Vibrator pattern for haptic feedback of virtual key press.
long[] mVirtualKeyVibePattern;
-
+
// Vibrator pattern for a short vibration.
long[] mKeyboardTapVibePattern;
// Vibrator pattern for haptic feedback during boot when safe mode is disabled.
long[] mSafeModeDisabledVibePattern;
-
+
// Vibrator pattern for haptic feedback during boot when safe mode is enabled.
long[] mSafeModeEnabledVibePattern;
@@ -225,7 +230,6 @@
boolean mHeadless;
boolean mSafeMode;
WindowState mStatusBar = null;
- boolean mHasSystemNavBar;
int mStatusBarHeight;
WindowState mNavigationBar = null;
boolean mHasNavigationBar = false;
@@ -236,7 +240,7 @@
int[] mNavigationBarWidthForRotation = new int[4];
WindowState mKeyguard = null;
- KeyguardViewMediator mKeyguardMediator;
+ KeyguardServiceDelegate mKeyguardDelegate;
GlobalActions mGlobalActions;
volatile boolean mPowerKeyHandled; // accessed from input reader and handler thread
boolean mPendingPowerKeyUpCanceled;
@@ -284,42 +288,25 @@
boolean mOrientationSensorEnabled = false;
int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean mHasSoftInput = false;
-
+
int mPointerLocationMode = 0; // guarded by mLock
// The last window we were told about in focusChanged.
WindowState mFocusedWindow;
IApplicationToken mFocusedApp;
- private static final class PointerLocationInputEventReceiver extends InputEventReceiver {
- private final PointerLocationView mView;
-
- public PointerLocationInputEventReceiver(InputChannel inputChannel, Looper looper,
- PointerLocationView view) {
- super(inputChannel, looper);
- mView = view;
- }
-
+ private final class PointerLocationPointerEventListener implements PointerEventListener {
@Override
- public void onInputEvent(InputEvent event) {
- boolean handled = false;
- try {
- if (event instanceof MotionEvent
- && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- final MotionEvent motionEvent = (MotionEvent)event;
- mView.addPointerEvent(motionEvent);
- handled = true;
- }
- } finally {
- finishInputEvent(event, handled);
+ public void onPointerEvent(MotionEvent motionEvent) {
+ if (mPointerLocationView != null) {
+ mPointerLocationView.addPointerEvent(motionEvent);
}
}
}
// Pointer location view state, only modified on the mHandler Looper.
- PointerLocationInputEventReceiver mPointerLocationInputEventReceiver;
+ PointerLocationPointerEventListener mPointerLocationPointerEventListener;
PointerLocationView mPointerLocationView;
- InputChannel mPointerLocationInputChannel;
// The current size of the screen; really; extends into the overscan area of
// the screen and doesn't account for any system elements like the status bar.
@@ -381,7 +368,7 @@
static final Rect mTmpContentFrame = new Rect();
static final Rect mTmpVisibleFrame = new Rect();
static final Rect mTmpNavigationFrame = new Rect();
-
+
WindowState mTopFullscreenOpaqueWindowState;
boolean mTopIsFullscreen;
boolean mForceStatusBar;
@@ -456,6 +443,9 @@
private boolean mPowerKeyTriggered;
private long mPowerKeyTime;
+ /* The number of steps between min and max brightness */
+ private static final int BRIGHTNESS_STEPS = 10;
+
SettingsObserver mSettingsObserver;
ShortcutManager mShortcutManager;
PowerManager.WakeLock mBroadcastWakeLock;
@@ -530,7 +520,7 @@
Settings.Secure.DEFAULT_INPUT_METHOD), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
- "fancy_rotation_anim"), false, this,
+ OverlayTesting.ENABLED_SETTING), false, this,
UserHandle.USER_ALL);
updateSettings();
}
@@ -540,20 +530,28 @@
updateRotation(false);
}
}
-
+
class MyOrientationListener extends WindowOrientationListener {
MyOrientationListener(Context context, Handler handler) {
super(context, handler);
}
-
+
@Override
public void onProposedRotationChanged(int rotation) {
- if (localLOGV) Log.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
+ if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
updateRotation(false);
}
}
MyOrientationListener mOrientationListener;
+ private static final int HIDEYBAR_NONE = 0;
+ private static final int HIDEYBAR_SHOWING = 1;
+ private static final int HIDEYBAR_HIDING = 2;
+ private int mStatusHideybar;
+ private int mNavigationHideybar;
+
+ private SystemGesturesPointerEventListener mSystemGestures;
+
IStatusBarService getStatusBarService() {
synchronized (mServiceAquireLock) {
if (mStatusBarService == null) {
@@ -617,7 +615,7 @@
}
//Could have been invoked due to screen turning on or off or
//change of the currently visible window's orientation
- if (localLOGV) Log.v(TAG, "Screen status="+mScreenOnEarly+
+ if (localLOGV) Slog.v(TAG, "Screen status="+mScreenOnEarly+
", current orientation="+mCurrentAppOrientation+
", SensorEnabled="+mOrientationSensorEnabled);
boolean disable = true;
@@ -627,7 +625,7 @@
//enable listener if not already enabled
if (!mOrientationSensorEnabled) {
mOrientationListener.enable();
- if(localLOGV) Log.v(TAG, "Enabling listeners");
+ if(localLOGV) Slog.v(TAG, "Enabling listeners");
mOrientationSensorEnabled = true;
}
}
@@ -635,7 +633,7 @@
//check if sensors need to be disabled
if (disable && mOrientationSensorEnabled) {
mOrientationListener.disable();
- if(localLOGV) Log.v(TAG, "Disabling listeners");
+ if(localLOGV) Slog.v(TAG, "Disabling listeners");
mOrientationSensorEnabled = false;
}
}
@@ -679,7 +677,7 @@
}
private long getScreenshotChordLongPressDelay() {
- if (mKeyguardMediator.isShowing()) {
+ if (mKeyguardDelegate.isShowing()) {
// Double the time it takes to take a screenshot from the keyguard
return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
ViewConfiguration.getGlobalActionKeyTimeout());
@@ -742,7 +740,7 @@
if (keyguardShowing) {
// since it took two seconds of long press to bring this up,
// poke the wake lock so they have some time to see the dialog.
- mKeyguardMediator.userActivity();
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
}
@@ -835,10 +833,6 @@
mWindowManager = windowManager;
mWindowManagerFuncs = windowManagerFuncs;
mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
- if (!mHeadless) {
- // don't create KeyguardViewMediator if headless
- mKeyguardMediator = new KeyguardViewMediator(context, null);
- }
mHandler = new PolicyHandler();
mOrientationListener = new MyOrientationListener(mContext, mHandler);
try {
@@ -911,6 +905,36 @@
filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
context.registerReceiver(mMultiuserReceiver, filter);
+ // monitor for system gestures
+ mSystemGestures = new SystemGesturesPointerEventListener(context,
+ new SystemGesturesPointerEventListener.Callbacks() {
+ @Override
+ public void onSwipeFromTop() {
+ if (mStatusBar != null) {
+ requestHideybars(mStatusBar);
+ }
+ }
+ @Override
+ public void onSwipeFromBottom() {
+ if (mNavigationBar != null && mNavigationBarOnBottom) {
+ requestHideybars(mNavigationBar);
+ }
+ }
+ @Override
+ public void onSwipeFromRight() {
+ if (mNavigationBar != null && !mNavigationBarOnBottom) {
+ requestHideybars(mNavigationBar);
+ }
+ }
+ @Override
+ public void onDebug() {
+ if (OverlayTesting.enabled) {
+ OverlayTesting.toggleForceOverlay(mFocusedWindow, mContext);
+ }
+ }
+ });
+ mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
+
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mLongPressVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_longPressVibePattern);
@@ -1021,43 +1045,22 @@
int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
if (shortSizeDp < 600) {
- // 0-599dp: "phone" UI with a separate status & navigation bar
- mHasSystemNavBar = false;
+ // Allow the navigation bar to move on small devices (phones).
mNavigationBarCanMove = true;
- } else if (shortSizeDp < 720) {
- // 600+dp: "phone" UI with modifications for larger screens
- mHasSystemNavBar = false;
- mNavigationBarCanMove = false;
}
- if (!mHasSystemNavBar) {
- mHasNavigationBar = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_showNavigationBar);
- // Allow a system property to override this. Used by the emulator.
- // See also hasNavigationBar().
- String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
- if (! "".equals(navBarOverride)) {
- if (navBarOverride.equals("1")) mHasNavigationBar = false;
- else if (navBarOverride.equals("0")) mHasNavigationBar = true;
- }
- } else {
+ mHasNavigationBar = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_showNavigationBar);
+ // Allow a system property to override this. Used by the emulator.
+ // See also hasNavigationBar().
+ String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
+ if ("1".equals(navBarOverride)) {
mHasNavigationBar = false;
+ } else if ("0".equals(navBarOverride)) {
+ mHasNavigationBar = true;
}
- if (mHasSystemNavBar) {
- // The system bar is always at the bottom. If you are watching
- // a video in landscape, we don't need to hide it if we can still
- // show a 16:9 aspect ratio with it.
- int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density;
- int barHeightDp = mNavigationBarHeightForRotation[mLandscapeRotation]
- * DisplayMetrics.DENSITY_DEFAULT / density;
- int aspect = ((shortSizeDp-barHeightDp) * 16) / longSizeDp;
- // We have computed the aspect ratio with the bar height taken
- // out to be 16:aspect. If this is less than 9, then hiding
- // the navigation bar will provide more useful space for wide
- // screen movies.
- mCanHideNavigationBar = aspect < 9;
- } else if (mHasNavigationBar) {
+ if (mHasNavigationBar) {
// The navigation bar is at the right in landscape; it seems always
// useful to hide it for showing a video.
mCanHideNavigationBar = true;
@@ -1135,6 +1138,8 @@
mHasSoftInput = hasSoftInput;
updateRotation = true;
}
+ OverlayTesting.enabled = Settings.System.getIntForUser(resolver,
+ OverlayTesting.ENABLED_SETTING, 0, UserHandle.USER_CURRENT) != 0;
}
if (updateRotation) {
updateRotation(true);
@@ -1166,23 +1171,16 @@
lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
wm.addView(mPointerLocationView, lp);
- mPointerLocationInputChannel =
- mWindowManagerFuncs.monitorInput("PointerLocationView");
- mPointerLocationInputEventReceiver =
- new PointerLocationInputEventReceiver(mPointerLocationInputChannel,
- Looper.myLooper(), mPointerLocationView);
+ mPointerLocationPointerEventListener = new PointerLocationPointerEventListener();
+ mWindowManagerFuncs.registerPointerEventListener(mPointerLocationPointerEventListener);
}
}
private void disablePointerLocation() {
- if (mPointerLocationInputEventReceiver != null) {
- mPointerLocationInputEventReceiver.dispose();
- mPointerLocationInputEventReceiver = null;
- }
-
- if (mPointerLocationInputChannel != null) {
- mPointerLocationInputChannel.dispose();
- mPointerLocationInputChannel = null;
+ if (mPointerLocationPointerEventListener != null) {
+ mWindowManagerFuncs.unregisterPointerEventListener(
+ mPointerLocationPointerEventListener);
+ mPointerLocationPointerEventListener = null;
}
if (mPointerLocationView != null) {
@@ -1279,6 +1277,7 @@
case TYPE_DISPLAY_OVERLAY:
case TYPE_HIDDEN_NAV_CONSUMER:
case TYPE_KEYGUARD:
+ case TYPE_KEYGUARD_SCRIM:
case TYPE_KEYGUARD_DIALOG:
case TYPE_MAGNIFICATION_OVERLAY:
case TYPE_NAVIGATION_BAR:
@@ -1303,11 +1302,11 @@
!= PackageManager.PERMISSION_GRANTED;
}
+ @Override
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
switch (attrs.type) {
case TYPE_SYSTEM_OVERLAY:
case TYPE_SECURE_SYSTEM_OVERLAY:
- case TYPE_TOAST:
// These types of windows can't receive input events.
attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -1331,11 +1330,8 @@
}
}
- private boolean isBuiltInKeyboardVisible() {
- return mHaveBuiltInKeyboard && !isHidden(mLidKeyboardAccessibility);
- }
-
/** {@inheritDoc} */
+ @Override
public void adjustConfigurationLw(Configuration config, int keyboardPresence,
int navigationPresence) {
mHaveBuiltInKeyboard = (keyboardPresence & PRESENCE_INTERNAL) != 0;
@@ -1361,6 +1357,7 @@
}
/** {@inheritDoc} */
+ @Override
public int windowTypeToLayerLw(int type) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return 2;
@@ -1396,54 +1393,57 @@
case TYPE_INPUT_METHOD_DIALOG:
// on-screen keyboards and other such input method user interfaces go here.
return 11;
+ case TYPE_KEYGUARD_SCRIM:
+ // the safety window that shows behind keyguard while keyguard is starting
+ return 12;
case TYPE_KEYGUARD:
// the keyguard; nothing on top of these can take focus, since they are
// responsible for power management when displayed.
- return 12;
- case TYPE_KEYGUARD_DIALOG:
return 13;
- case TYPE_STATUS_BAR_SUB_PANEL:
+ case TYPE_KEYGUARD_DIALOG:
return 14;
- case TYPE_STATUS_BAR:
+ case TYPE_STATUS_BAR_SUB_PANEL:
return 15;
- case TYPE_STATUS_BAR_PANEL:
+ case TYPE_STATUS_BAR:
return 16;
+ case TYPE_STATUS_BAR_PANEL:
+ return 17;
case TYPE_VOLUME_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
- return 17;
+ return 18;
case TYPE_SYSTEM_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
- return 18;
+ return 19;
case TYPE_NAVIGATION_BAR:
// the navigation bar, if available, shows atop most things
- return 19;
+ return 20;
case TYPE_NAVIGATION_BAR_PANEL:
// some panels (e.g. search) need to show on top of the navigation bar
- return 20;
+ return 21;
case TYPE_SYSTEM_ERROR:
// system-level error dialogs
- return 21;
+ return 22;
case TYPE_MAGNIFICATION_OVERLAY:
// used to highlight the magnified portion of a display
- return 22;
+ return 23;
case TYPE_DISPLAY_OVERLAY:
// used to simulate secondary display devices
- return 23;
+ return 24;
case TYPE_DRAG:
// the drag layer: input for drag-and-drop is associated with this window,
// which sits above all other focusable windows
- return 24;
- case TYPE_SECURE_SYSTEM_OVERLAY:
return 25;
- case TYPE_BOOT_PROGRESS:
+ case TYPE_SECURE_SYSTEM_OVERLAY:
return 26;
+ case TYPE_BOOT_PROGRESS:
+ return 27;
case TYPE_POINTER:
// the (mouse) pointer layer
- return 27;
- case TYPE_HIDDEN_NAV_CONSUMER:
return 28;
+ case TYPE_HIDDEN_NAV_CONSUMER:
+ return 29;
}
Log.e(TAG, "Unknown window type: " + type);
return 2;
@@ -1474,10 +1474,6 @@
return windowTypeToLayerLw(TYPE_SYSTEM_ERROR);
}
- public boolean hasSystemNavBar() {
- return mHasSystemNavBar;
- }
-
public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation) {
if (mHasNavigationBar) {
// For a basic navigation bar, when we are in landscape mode we place
@@ -1490,10 +1486,6 @@
}
public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation) {
- if (mHasSystemNavBar) {
- // For the system navigation bar, we always place it at the bottom.
- return fullHeight - mNavigationBarHeightForRotation[rotation];
- }
if (mHasNavigationBar) {
// For a basic navigation bar, when we are in portrait mode we place
// the navigation bar to the bottom.
@@ -1509,15 +1501,11 @@
}
public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation) {
- // If we don't have a system nav bar, then there is a separate status
- // bar at the top of the display. We don't count that as part of the
- // fixed decor, since it can hide; however, for purposes of configurations,
+ // There is a separate status bar at the top of the display. We don't count that as part
+ // of the fixed decor, since it can hide; however, for purposes of configurations,
// we do want to exclude it since applications can't generally use that part
// of the screen.
- if (!mHasSystemNavBar) {
- return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation) - mStatusBarHeight;
- }
- return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation);
+ return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation) - mStatusBarHeight;
}
@Override
@@ -1534,6 +1522,7 @@
case TYPE_DREAM:
case TYPE_UNIVERSE_BACKGROUND:
case TYPE_KEYGUARD:
+ case TYPE_KEYGUARD_SCRIM:
return false;
default:
return true;
@@ -1544,7 +1533,7 @@
@Override
public View addStartingWindow(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
- int icon, int windowFlags) {
+ int icon, int logo, int windowFlags) {
if (!SHOW_STARTING_ANIMATIONS) {
return null;
}
@@ -1601,6 +1590,9 @@
win.addFlags(WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW);
}
+ win.setDefaultIcon(icon);
+ win.setDefaultLogo(logo);
+
win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
@@ -1707,7 +1699,7 @@
}
}
mNavigationBar = win;
- if (DEBUG_LAYOUT) Log.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
case TYPE_NAVIGATION_BAR_PANEL:
mContext.enforceCallingOrSelfPermission(
@@ -1730,6 +1722,13 @@
}
mKeyguard = win;
break;
+ case TYPE_KEYGUARD_SCRIM:
+ if (mKeyguardScrim != null) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ mKeyguardScrim = win;
+ break;
+
}
return WindowManagerGlobal.ADD_OKAY;
}
@@ -1739,8 +1738,13 @@
if (mStatusBar == win) {
mStatusBar = null;
} else if (mKeyguard == win) {
+ Log.v(TAG, "Removing keyguard window (Did it crash?)");
mKeyguard = null;
- } else if (mNavigationBar == win) {
+ mKeyguardDelegate.showScrim();
+ } else if (mKeyguardScrim == win) {
+ Log.v(TAG, "Removing keyguard scrim");
+ mKeyguardScrim = null;
+ } if (mNavigationBar == win) {
mNavigationBar = null;
}
}
@@ -1980,6 +1984,7 @@
if (attrs != null) {
final int type = attrs.type;
if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
+ || type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM
|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
// the "app" is keyguard, so give it the key
return 0;
@@ -2085,6 +2090,43 @@
mHandler.post(mScreenshotRunnable);
}
return -1;
+ } else if (keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP
+ || keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN) {
+ if (down) {
+ int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1;
+
+ // Disable autobrightness if it's on
+ int auto = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
+ UserHandle.USER_CURRENT_OR_SELF);
+ if (auto != 0) {
+ Settings.System.putIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
+ UserHandle.USER_CURRENT_OR_SELF);
+ }
+
+ int min = mPowerManager.getMinimumScreenBrightnessSetting();
+ int max = mPowerManager.getMaximumScreenBrightnessSetting();
+ int step = (max - min + BRIGHTNESS_STEPS - 1) / BRIGHTNESS_STEPS * direction;
+ int brightness = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS,
+ mPowerManager.getDefaultScreenBrightnessSetting(),
+ UserHandle.USER_CURRENT_OR_SELF);
+ brightness += step;
+ // Make sure we don't go beyond the limits.
+ brightness = Math.min(max, brightness);
+ brightness = Math.max(min, brightness);
+
+ Settings.System.putIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, brightness,
+ UserHandle.USER_CURRENT_OR_SELF);
+ Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
+ mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ }
+ return -1;
}
// Shortcuts are invoked through Search+key, so intercept those here
@@ -2102,7 +2144,7 @@
if (shortcutIntent != null) {
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- mContext.startActivity(shortcutIntent);
+ mContext.startActivityAsUser(shortcutIntent, UserHandle.CURRENT);
} catch (ActivityNotFoundException ex) {
Slog.w(TAG, "Dropping shortcut key combination because "
+ "the activity to which it is registered was not found: "
@@ -2128,7 +2170,7 @@
if (shortcutIntent != null) {
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- mContext.startActivity(shortcutIntent);
+ mContext.startActivityAsUser(shortcutIntent, UserHandle.CURRENT);
} catch (ActivityNotFoundException ex) {
Slog.w(TAG, "Dropping shortcut key combination because "
+ "the activity to which it is registered was not found: "
@@ -2146,7 +2188,7 @@
Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- mContext.startActivity(intent);
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
} catch (ActivityNotFoundException ex) {
Slog.w(TAG, "Dropping application launch key because "
+ "the activity to which it is registered was not found: "
@@ -2293,7 +2335,7 @@
if (searchManager != null) {
searchManager.stopSearch();
}
- mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "No activity to handle assist long press action.", e);
}
@@ -2308,7 +2350,7 @@
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
- mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "No activity to handle assist action.", e);
}
@@ -2372,12 +2414,12 @@
* given the situation with the keyguard.
*/
void launchHomeFromHotKey() {
- if (mKeyguardMediator != null && mKeyguardMediator.isShowingAndNotHidden()) {
+ if (mKeyguardDelegate != null && mKeyguardDelegate.isShowingAndNotHidden()) {
// don't launch home if keyguard showing
- } else if (!mHideLockScreen && mKeyguardMediator.isInputRestricted()) {
+ } else if (!mHideLockScreen && mKeyguardDelegate.isInputRestricted()) {
// when in keyguard restricted mode, must first verify unlock
// before launching home
- mKeyguardMediator.verifyUnlock(new OnKeyguardExitResult() {
+ mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
public void onKeyguardExitResult(boolean success) {
if (success) {
try {
@@ -2400,13 +2442,15 @@
}
}
- /**
- * A delayed callback use to determine when it is okay to re-allow applications
- * to use certain system UI flags. This is used to prevent applications from
- * spamming system UI changes that prevent the navigation bar from being shown.
- */
- final Runnable mAllowSystemUiDelay = new Runnable() {
- @Override public void run() {
+ private final Runnable mClearHideNavigationFlag = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
+ // Clear flags.
+ mForceClearedSystemUiFlags &=
+ ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ }
+ mWindowManagerFuncs.reevaluateStatusBarVisibility();
}
};
@@ -2430,7 +2474,7 @@
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
// When the user taps down, we re-show the nav bar.
boolean changed = false;
- synchronized (mLock) {
+ synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
// Any user activity always causes us to show the
// navigation controls, if they had been hidden.
// We also clear the low profile and only content
@@ -2452,16 +2496,7 @@
if (mForceClearedSystemUiFlags != newVal) {
mForceClearedSystemUiFlags = newVal;
changed = true;
- mHandler.postDelayed(new Runnable() {
- @Override public void run() {
- synchronized (mLock) {
- // Clear flags.
- mForceClearedSystemUiFlags &=
- ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- }
- mWindowManagerFuncs.reevaluateStatusBarVisibility();
- }
- }, 1000);
+ mHandler.postDelayed(mClearHideNavigationFlag, 1000);
}
}
if (changed) {
@@ -2485,6 +2520,16 @@
@Override
public int adjustSystemUiVisibilityLw(int visibility) {
+ if (mStatusBar != null && mStatusHideybar == HIDEYBAR_SHOWING &&
+ 0 == (visibility & View.STATUS_BAR_OVERLAY)) {
+ mStatusHideybar = HIDEYBAR_HIDING;
+ mStatusBar.hideLw(true);
+ }
+ if (mNavigationBar != null && mNavigationHideybar == HIDEYBAR_SHOWING &&
+ 0 == (visibility & View.NAVIGATION_BAR_OVERLAY)) {
+ mNavigationHideybar = HIDEYBAR_HIDING;
+ mNavigationBar.hideLw(true);
+ }
// Reset any bits in mForceClearingStatusBarVisibility that
// are now clear.
mResettingSystemUiFlags &= visibility;
@@ -2586,8 +2631,8 @@
mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom;
mRestrictedScreenLeft = mUnrestrictedScreenLeft;
mRestrictedScreenTop = mUnrestrictedScreenTop;
- mRestrictedScreenWidth = mUnrestrictedScreenWidth;
- mRestrictedScreenHeight = mUnrestrictedScreenHeight;
+ mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth;
+ mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight;
mDockLeft = mContentLeft = mStableLeft = mStableFullscreenLeft
= mCurLeft = mUnrestrictedScreenLeft;
mDockTop = mContentTop = mStableTop = mStableFullscreenTop
@@ -2613,12 +2658,13 @@
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
boolean navVisible = (mLastSystemUiFlags&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+ boolean overlayAllowed = (mLastSystemUiFlags&View.SYSTEM_UI_FLAG_ALLOW_OVERLAY) != 0;
// When the navigation bar isn't visible, we put up a fake
// input window to catch all touch events. This way we can
// detect when the user presses anywhere to bring back the nav
// bar and ensure the application doesn't see the event.
- if (navVisible) {
+ if (navVisible || overlayAllowed) {
if (mHideNavFakeWindow != null) {
mHideNavFakeWindow.dismiss();
mHideNavFakeWindow = null;
@@ -2635,7 +2681,9 @@
// then take that into account.
navVisible |= !mCanHideNavigationBar;
+ boolean updateSysUiVisibility = false;
if (mNavigationBar != null) {
+ boolean navBarHideyShowing = mNavigationHideybar == HIDEYBAR_SHOWING;
// Force the navigation bar to its appropriate place and
// size. We need to do this directly, instead of relying on
// it to bubble up from the nav bar, because this needs to
@@ -2647,7 +2695,9 @@
- mNavigationBarHeightForRotation[displayRotation];
mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
- if (navVisible) {
+ if (navBarHideyShowing) {
+ mNavigationBar.showLw(true);
+ } else if (navVisible) {
mNavigationBar.showLw(true);
mDockBottom = mTmpNavigationFrame.top;
mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
@@ -2668,7 +2718,9 @@
- mNavigationBarWidthForRotation[displayRotation];
mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
- if (navVisible) {
+ if (navBarHideyShowing) {
+ mNavigationBar.showLw(true);
+ } else if (navVisible) {
mNavigationBar.showLw(true);
mDockRight = mTmpNavigationFrame.left;
mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
@@ -2694,9 +2746,14 @@
// And compute the final frame.
mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame);
- if (DEBUG_LAYOUT) Log.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+ if (mNavigationHideybar == HIDEYBAR_HIDING && !mNavigationBar.isVisibleLw()) {
+ // Finished animating out, clean up and reset alpha
+ mNavigationHideybar = HIDEYBAR_NONE;
+ updateSysUiVisibility = true;
+ }
}
- if (DEBUG_LAYOUT) Log.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
+ if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
mDockLeft, mDockTop, mDockRight, mDockBottom));
// decide where the status bar goes ahead of time
@@ -2720,9 +2777,11 @@
// For layout, the status bar is always at the top with our fixed height.
mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
+ boolean statusBarOverlay = (mLastSystemUiFlags & View.STATUS_BAR_OVERLAY) != 0;
+
// If the status bar is hidden, we don't want to cause
// windows behind it to scroll.
- if (mStatusBar.isVisibleLw()) {
+ if (mStatusBar.isVisibleLw() && !statusBarOverlay) {
// Status bar may go away, so the screen area it occupies
// is available to apps but just covering them when the
// status bar is visible.
@@ -2733,24 +2792,34 @@
mContentLeft = mCurLeft = mDockLeft;
mContentRight = mCurRight = mDockRight;
- if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: " +
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
String.format(
"dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
mDockLeft, mDockTop, mDockRight, mDockBottom,
mContentLeft, mContentTop, mContentRight, mContentBottom,
mCurLeft, mCurTop, mCurRight, mCurBottom));
}
- if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()) {
+ if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw() && !statusBarOverlay) {
// If the status bar is currently requested to be visible,
// and not in the process of animating on or off, then
// we can tell the app that it is covered by it.
mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
}
+
+ if (mStatusHideybar == HIDEYBAR_HIDING && !mStatusBar.isVisibleLw()) {
+ // Finished animating out, clean up and reset alpha
+ mStatusHideybar = HIDEYBAR_NONE;
+ updateSysUiVisibility = true;
+ }
+ }
+ if (updateSysUiVisibility) {
+ updateSystemUiVisibilityLw();
}
}
}
/** {@inheritDoc} */
+ @Override
public int getSystemDecorRectLw(Rect systemRect) {
systemRect.left = mSystemLeft;
systemRect.top = mSystemTop;
@@ -2761,6 +2830,11 @@
return 0;
}
+ @Override
+ public void getContentRectLw(Rect r) {
+ r.set(mContentLeft, mContentTop, mContentRight, mContentBottom);
+ }
+
void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf) {
if (win.getSurfaceLayer() > mDockLayer && attached.getSurfaceLayer() < mDockLayer) {
@@ -2840,9 +2914,7 @@
final boolean needsToOffsetInputMethodTarget = isDefaultDisplay &&
(win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
if (needsToOffsetInputMethodTarget) {
- if (DEBUG_LAYOUT) {
- Slog.i(TAG, "Offset ime target window by the last ime window state");
- }
+ if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
offsetInputMethodWindowLw(mLastInputMethodWindow);
}
@@ -2886,8 +2958,7 @@
} else {
if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
- if (DEBUG_LAYOUT)
- Log.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ "): IN_SCREEN, INSET_DECOR");
// This is the case for a normal activity window: we want it
// to cover all of the screen space, and it can take care of
@@ -2917,11 +2988,9 @@
? mRestrictedScreenTop+mRestrictedScreenHeight
: mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
- if (DEBUG_LAYOUT) {
- Log.v(TAG, String.format(
+ if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
- }
} else if ((attrs.flags&FLAG_LAYOUT_IN_OVERSCAN) != 0
&& attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
@@ -3003,8 +3072,8 @@
} else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl
& (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
- if (DEBUG_LAYOUT)
- Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN");
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
+ "): IN_SCREEN");
// A window that has requested to fill the entire screen just
// gets everything, period.
if (attrs.type == TYPE_STATUS_BAR_PANEL
@@ -3018,11 +3087,9 @@
pf.bottom = df.bottom = of.bottom = cf.bottom = hasNavBar
? mRestrictedScreenTop+mRestrictedScreenHeight
: mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
- if (DEBUG_LAYOUT) {
- Log.v(TAG, String.format(
+ if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
- }
} else if (attrs.type == TYPE_NAVIGATION_BAR
|| attrs.type == TYPE_NAVIGATION_BAR_PANEL) {
// The navigation bar has Real Ultimate Power.
@@ -3032,11 +3099,9 @@
+ mUnrestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop
+ mUnrestrictedScreenHeight;
- if (DEBUG_LAYOUT) {
- Log.v(TAG, String.format(
+ if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out navigation bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
- }
} else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
|| attrs.type == TYPE_BOOT_PROGRESS)
&& ((fl & FLAG_FULLSCREEN) != 0)) {
@@ -3112,14 +3177,14 @@
vf.set(cf);
}
} else if (attached != null) {
- if (DEBUG_LAYOUT)
- Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): attached to " + attached);
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
+ "): attached to " + attached);
// A child window should be placed inside of the same visible
// frame that its parent had.
setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf);
} else {
- if (DEBUG_LAYOUT)
- Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): normal window");
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
+ "): normal window");
// Otherwise, a normal window must be placed inside the content
// of all screen decorations.
if (attrs.type == TYPE_STATUS_BAR_PANEL) {
@@ -3167,7 +3232,7 @@
= vf.right = vf.bottom = 10000;
}
- if (DEBUG_LAYOUT) Log.v(TAG, "Compute frame " + attrs.getTitle()
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
+ ": sim=#" + Integer.toHexString(sim)
+ " attach=" + attached + " type=" + attrs.type
+ String.format(" flags=0x%08x", fl)
@@ -3197,7 +3262,7 @@
if (mCurBottom > top) {
mCurBottom = top;
}
- if (DEBUG_LAYOUT) Log.v(TAG, "Input method: mDockBottom="
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
+ mDockBottom + " mContentBottom="
+ mContentBottom + " mCurBottom=" + mCurBottom);
}
@@ -3264,21 +3329,20 @@
&& attrs.x == 0 && attrs.y == 0
&& attrs.width == WindowManager.LayoutParams.MATCH_PARENT
&& attrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
- if (DEBUG_LAYOUT) Log.v(TAG, "Fullscreen window: " + win);
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
mTopFullscreenOpaqueWindowState = win;
if ((attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) {
- if (DEBUG_LAYOUT) Log.v(TAG, "Setting mHideLockScreen to true by win " + win);
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mHideLockScreen to true by win " + win);
mHideLockScreen = true;
mForceStatusBarFromKeyguard = false;
}
if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0
&& mDismissKeyguard == DISMISS_KEYGUARD_NONE) {
- if (DEBUG_LAYOUT) Log.v(TAG, "Setting mDismissKeyguard to true by win " + win);
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mDismissKeyguard true by win " + win);
mDismissKeyguard = mWinDismissingKeyguard == win ?
DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START;
mWinDismissingKeyguard = win;
- mForceStatusBarFromKeyguard =
- mShowingLockscreen && mKeyguardMediator.isSecure();
+ mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure();
}
if ((attrs.flags & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
mAllowLockscreenWhenOn = true;
@@ -3306,17 +3370,17 @@
}
if (mStatusBar != null) {
- if (DEBUG_LAYOUT) Log.i(TAG, "force=" + mForceStatusBar
+ if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
+ " forcefkg=" + mForceStatusBarFromKeyguard
+ " top=" + mTopFullscreenOpaqueWindowState);
if (mForceStatusBar || mForceStatusBarFromKeyguard) {
- if (DEBUG_LAYOUT) Log.v(TAG, "Showing status bar: forced");
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
if (mStatusBar.showLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT;
} else if (mTopFullscreenOpaqueWindowState != null) {
if (localLOGV) {
- Log.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
+ Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
+ " shown frame: " + mTopFullscreenOpaqueWindowState.getShownFrameLw());
- Log.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ " lp.flags=0x" + Integer.toHexString(lp.flags));
}
topIsFullscreen = (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0
@@ -3325,8 +3389,12 @@
// and mTopIsFullscreen is that that mTopIsFullscreen is set only if the window
// has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the
// case though.
- if (topIsFullscreen) {
- if (DEBUG_LAYOUT) Log.v(TAG, "** HIDING status bar");
+ if (mStatusHideybar == HIDEYBAR_SHOWING) {
+ if (mStatusBar.showLw(true)) {
+ changes |= FINISH_LAYOUT_REDO_LAYOUT;
+ }
+ } else if (topIsFullscreen) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
if (mStatusBar.hideLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -3343,11 +3411,11 @@
mStatusBarService = null;
}
}});
- } else if (DEBUG_LAYOUT) {
- Log.v(TAG, "Preventing status bar from hiding by policy");
+ } else {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding");
}
} else {
- if (DEBUG_LAYOUT) Log.v(TAG, "** SHOWING status bar: top is not fullscreen");
+ if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen");
if (mStatusBar.showLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
}
@@ -3358,19 +3426,19 @@
// Hide the key guard if a visible window explicitly specifies that it wants to be
// displayed when the screen is locked.
if (mKeyguard != null) {
- if (localLOGV) Log.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard="
+ if (localLOGV) Slog.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard="
+ mHideLockScreen);
- if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !mKeyguardMediator.isSecure()) {
+ if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !mKeyguardDelegate.isSecure()) {
if (mKeyguard.hideLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
}
- if (mKeyguardMediator.isShowing()) {
+ if (mKeyguardDelegate.isShowing()) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mKeyguardMediator.keyguardDone(false, false);
+ mKeyguardDelegate.keyguardDone(false, false);
}
});
}
@@ -3380,7 +3448,7 @@
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
}
- mKeyguardMediator.setHidden(true);
+ mKeyguardDelegate.setHidden(true);
} else if (mDismissKeyguard != DISMISS_KEYGUARD_NONE) {
// This is the case of keyguard isSecure() and not mHideLockScreen.
if (mDismissKeyguard == DISMISS_KEYGUARD_START) {
@@ -3390,11 +3458,11 @@
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
}
- mKeyguardMediator.setHidden(false);
+ mKeyguardDelegate.setHidden(false);
mHandler.post(new Runnable() {
@Override
public void run() {
- mKeyguardMediator.dismiss();
+ mKeyguardDelegate.dismiss();
}
});
}
@@ -3405,7 +3473,7 @@
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
}
- mKeyguardMediator.setHidden(false);
+ mKeyguardDelegate.setHidden(false);
}
}
@@ -3455,11 +3523,7 @@
updateRotation(true);
if (lidOpen) {
- if (keyguardIsShowingTq()) {
- mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(KeyEvent.KEYCODE_POWER);
- } else {
- mPowerManager.wakeUp(SystemClock.uptimeMillis());
- }
+ mPowerManager.wakeUp(SystemClock.uptimeMillis());
} else if (!mLidControlsSleep) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
@@ -3541,7 +3605,8 @@
keycode == KeyEvent.KEYCODE_VOLUME_UP
? AudioManager.ADJUST_RAISE
: AudioManager.ADJUST_LOWER,
- 0);
+ 0,
+ mContext.getBasePackageName());
} catch (RemoteException e) {
Log.w(TAG, "IAudioService.adjustStreamVolume() threw RemoteException " + e);
} finally {
@@ -3636,10 +3701,10 @@
// the same as if it were open and in front.
// This will prevent any keys other than the power button from waking the screen
// when the keyguard is hidden by another activity.
- final boolean keyguardActive = (mKeyguardMediator == null ? false :
+ final boolean keyguardActive = (mKeyguardDelegate == null ? false :
(isScreenOn ?
- mKeyguardMediator.isShowingAndNotHidden() :
- mKeyguardMediator.isShowing()));
+ mKeyguardDelegate.isShowingAndNotHidden() :
+ mKeyguardDelegate.isShowing()));
if (keyCode == KeyEvent.KEYCODE_POWER) {
policyFlags |= WindowManagerPolicy.FLAG_WAKE;
@@ -3676,13 +3741,7 @@
// to wake the device but don't pass the key to the application.
result = 0;
if (down && isWakeKey && isWakeKeyWhenScreenOff(keyCode)) {
- if (keyguardActive) {
- // If the keyguard is showing, let it wake the device when ready.
- mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
- } else {
- // Otherwise, wake the device ourselves.
- result |= ACTION_WAKE_UP;
- }
+ result |= ACTION_WAKE_UP;
}
}
@@ -3948,13 +4007,7 @@
final boolean isWakeMotion = (policyFlags
& (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
if (isWakeMotion) {
- if (mKeyguardMediator != null && mKeyguardMediator.isShowing()) {
- // If the keyguard is showing, let it decide what to do with the wake motion.
- mKeyguardMediator.onWakeMotionWhenKeyguardShowingTq();
- } else {
- // Otherwise, wake the device ourselves.
- result |= ACTION_WAKE_UP;
- }
+ result |= ACTION_WAKE_UP;
}
return result;
}
@@ -4040,12 +4093,12 @@
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) {
- if (mKeyguardMediator != null) {
- mKeyguardMediator.onDreamingStarted();
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.onDreamingStarted();
}
} else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
- if (mKeyguardMediator != null) {
- mKeyguardMediator.onDreamingStopped();
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.onDreamingStopped();
}
}
}
@@ -4064,7 +4117,7 @@
// force a re-application of focused window sysui visibility.
// the window may never have been shown for this user
// e.g. the keyguard when going through the new-user setup flow
- synchronized(mLock) {
+ synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
mLastSystemUiFlags = 0;
updateSystemUiVisibilityLw();
}
@@ -4072,6 +4125,38 @@
}
};
+ private void requestHideybars(WindowState swipeTarget) {
+ synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
+ boolean sb = checkShowHideybar("status", mStatusHideybar, mStatusBar);
+ boolean nb = checkShowHideybar("navigation", mNavigationHideybar, mNavigationBar);
+ if (sb || nb) {
+ WindowState hideyTarget = sb ? mStatusBar : mNavigationBar;
+ if (sb ^ nb && hideyTarget != swipeTarget) {
+ if (DEBUG) Slog.d(TAG, "Not showing hideybar, wrong swipe target");
+ return;
+ }
+ mStatusHideybar = sb ? HIDEYBAR_SHOWING : mStatusHideybar;
+ mNavigationHideybar = nb ? HIDEYBAR_SHOWING : mNavigationHideybar;
+ updateSystemUiVisibilityLw();
+ }
+ }
+ }
+
+ private boolean checkShowHideybar(String tag, int hideybar, WindowState win) {
+ if (hideybar == HIDEYBAR_SHOWING) {
+ if (DEBUG) Slog.d(TAG, "Not showing " + tag + " hideybar, already shown");
+ return false;
+ } else if (win == null) {
+ if (DEBUG) Slog.d(TAG, "Not showing " + tag + " hideybar, bar doesn't exist");
+ return false;
+ } else if (win.isDisplayedLw()) {
+ if (DEBUG) Slog.d(TAG, "Not showing " + tag + " hideybar, bar already visible");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
@Override
public void screenTurnedOff(int why) {
EventLog.writeEvent(70000, 0);
@@ -4079,8 +4164,8 @@
mScreenOnEarly = false;
mScreenOnFully = false;
}
- if (mKeyguardMediator != null) {
- mKeyguardMediator.onScreenTurnedOff(why);
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.onScreenTurnedOff(why);
}
synchronized (mLock) {
updateOrientationListenerLp();
@@ -4107,9 +4192,9 @@
}
private void waitForKeyguard(final ScreenOnListener screenOnListener) {
- if (mKeyguardMediator != null) {
+ if (mKeyguardDelegate != null) {
if (screenOnListener != null) {
- mKeyguardMediator.onScreenTurnedOn(new KeyguardViewManager.ShowListener() {
+ mKeyguardDelegate.onScreenTurnedOn(new KeyguardServiceDelegate.ShowListener() {
@Override
public void onShown(IBinder windowToken) {
waitForKeyguardWindowDrawn(windowToken, screenOnListener);
@@ -4117,10 +4202,10 @@
});
return;
} else {
- mKeyguardMediator.onScreenTurnedOn(null);
+ mKeyguardDelegate.onScreenTurnedOn(null);
}
} else {
- Slog.i(TAG, "No keyguard mediator!");
+ Slog.i(TAG, "No keyguard interface!");
}
finishScreenTurningOn(screenOnListener);
}
@@ -4175,21 +4260,21 @@
/** {@inheritDoc} */
public void enableKeyguard(boolean enabled) {
- if (mKeyguardMediator != null) {
- mKeyguardMediator.setKeyguardEnabled(enabled);
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.setKeyguardEnabled(enabled);
}
}
/** {@inheritDoc} */
public void exitKeyguardSecurely(OnKeyguardExitResult callback) {
- if (mKeyguardMediator != null) {
- mKeyguardMediator.verifyUnlock(callback);
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.verifyUnlock(callback);
}
}
private boolean keyguardIsShowingTq() {
- if (mKeyguardMediator == null) return false;
- return mKeyguardMediator.isShowingAndNotHidden();
+ if (mKeyguardDelegate == null) return false;
+ return mKeyguardDelegate.isShowingAndNotHidden();
}
@@ -4200,26 +4285,26 @@
/** {@inheritDoc} */
public boolean isKeyguardSecure() {
- if (mKeyguardMediator == null) return false;
- return mKeyguardMediator.isSecure();
+ if (mKeyguardDelegate == null) return false;
+ return mKeyguardDelegate.isSecure();
}
/** {@inheritDoc} */
public boolean inKeyguardRestrictedKeyInputMode() {
- if (mKeyguardMediator == null) return false;
- return mKeyguardMediator.isInputRestricted();
+ if (mKeyguardDelegate == null) return false;
+ return mKeyguardDelegate.isInputRestricted();
}
public void dismissKeyguardLw() {
- if (mKeyguardMediator.isShowing()) {
+ if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
mHandler.post(new Runnable() {
public void run() {
- if (mKeyguardMediator.isDismissable()) {
+ if (mKeyguardDelegate.isDismissable()) {
// Can we just finish the keyguard straight away?
- mKeyguardMediator.keyguardDone(false, true);
+ mKeyguardDelegate.keyguardDone(false, true);
} else {
// ask the keyguard to prompt the user to authenticate if necessary
- mKeyguardMediator.dismiss();
+ mKeyguardDelegate.dismiss();
}
}
});
@@ -4462,7 +4547,7 @@
? HapticFeedbackConstants.SAFE_MODE_ENABLED
: HapticFeedbackConstants.SAFE_MODE_DISABLED, true);
}
-
+
static long[] getLongIntArray(Resources r, int resid) {
int[] ar = r.getIntArray(resid);
if (ar == null) {
@@ -4474,17 +4559,19 @@
}
return out;
}
-
+
/** {@inheritDoc} */
+ @Override
public void systemReady() {
- if (mKeyguardMediator != null) {
- // tell the keyguard
- mKeyguardMediator.onSystemReady();
+ if (!mHeadless) {
+ mKeyguardDelegate = new KeyguardServiceDelegate(mContext, null);
+ mKeyguardDelegate.onSystemReady();
}
synchronized (mLock) {
updateOrientationListenerLp();
mSystemReady = true;
mHandler.post(new Runnable() {
+ @Override
public void run() {
updateSettings();
}
@@ -4591,8 +4678,8 @@
public void run() {
synchronized (this) {
if (localLOGV) Log.v(TAG, "mScreenLockTimeout activating keyguard");
- if (mKeyguardMediator != null) {
- mKeyguardMediator.doKeyguardTimeout(options);
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.doKeyguardTimeout(options);
}
mLockScreenTimerActive = false;
options = null;
@@ -4620,7 +4707,7 @@
private void updateLockScreenTimeout() {
synchronized (mScreenLockTimeout) {
boolean enable = (mAllowLockscreenWhenOn && mScreenOnEarly &&
- mKeyguardMediator != null && mKeyguardMediator.isSecure());
+ mKeyguardDelegate != null && mKeyguardDelegate.isSecure());
if (mLockScreenTimerActive != enable) {
if (enable) {
if (localLOGV) Log.v(TAG, "setting lockscreen timer");
@@ -4635,6 +4722,7 @@
}
/** {@inheritDoc} */
+ @Override
public void enableScreenAfterBoot() {
readLidState();
applyLidSwitchState();
@@ -4676,7 +4764,7 @@
* <li>The device is in car mode but there's no CAR_DOCK app with METADATA_DOCK_HOME
* <li>The device is in desk mode but there's no DESK_DOCK app with METADATA_DOCK_HOME
* </ul>
- * @return
+ * @return A dock intent.
*/
Intent createHomeDockIntent() {
Intent intent = null;
@@ -4731,7 +4819,7 @@
mContext.startActivityAsUser(mHomeIntent, UserHandle.CURRENT);
}
-
+
/**
* goes to the home screen
* @return whether it did anything
@@ -4783,7 +4871,8 @@
}
return true;
}
-
+
+ @Override
public void setCurrentOrientationLw(int newOrientation) {
synchronized (mLock) {
if (newOrientation != mCurrentAppOrientation) {
@@ -4807,18 +4896,20 @@
ringTone.setStreamType(AudioManager.STREAM_MUSIC);
ringTone.play();
}
+
private boolean isGlobalAccessibilityGestureEnabled() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1;
}
+ @Override
public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
if (!mVibrator.hasVibrator()) {
return false;
}
final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
- if (!always && (hapticsDisabled || mKeyguardMediator.isShowingAndNotHidden())) {
+ if (!always && (hapticsDisabled || mKeyguardDelegate.isShowingAndNotHidden())) {
return false;
}
long[] pattern = null;
@@ -4866,9 +4957,9 @@
@Override
public void keepScreenOnStoppedLw() {
- if (mKeyguardMediator != null && !mKeyguardMediator.isShowingAndNotHidden()) {
+ if (mKeyguardDelegate != null && !mKeyguardDelegate.isShowingAndNotHidden()) {
long curTime = SystemClock.uptimeMillis();
- mPowerManager.userActivity(curTime, false);
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
}
@@ -4887,14 +4978,15 @@
// will quickly lose focus once it correctly gets hidden.
return 0;
}
+
int tmpVisibility = mFocusedWindow.getSystemUiVisibility()
& ~mResettingSystemUiFlags
& ~mForceClearedSystemUiFlags;
if (mForcingShowNavBar && mFocusedWindow.getSurfaceLayer() < mForcingShowNavBarLayer) {
tmpVisibility &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
}
- final int visibility = tmpVisibility;
- int diff = visibility ^ mLastSystemUiFlags;
+ final int visibility = updateHideybarsLw(tmpVisibility);
+ final int diff = visibility ^ mLastSystemUiFlags;
final boolean needsMenu = mFocusedWindow.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
if (diff == 0 && mLastFocusNeedsMenu == needsMenu
&& mFocusedApp == mFocusedWindow.getAppToken()) {
@@ -4904,6 +4996,7 @@
mLastFocusNeedsMenu = needsMenu;
mFocusedApp = mFocusedWindow.getAppToken();
mHandler.post(new Runnable() {
+ @Override
public void run() {
try {
IStatusBarService statusbar = getStatusBarService();
@@ -4920,8 +5013,121 @@
return diff;
}
+ private int updateHideybarsLw(int tmpVisibility) {
+ if (OverlayTesting.enabled) {
+ tmpVisibility = OverlayTesting.applyForced(mFocusedWindow, tmpVisibility);
+ }
+ boolean statusBarHasFocus =
+ mFocusedWindow.getAttrs().type == TYPE_STATUS_BAR;
+ if (statusBarHasFocus) {
+ // prevent status bar interaction from clearing certain flags
+ int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_ALLOW_OVERLAY;
+ tmpVisibility = (tmpVisibility & ~flags) | (mLastSystemUiFlags & flags);
+ }
+ boolean overlayAllowed = (tmpVisibility & View.SYSTEM_UI_FLAG_ALLOW_OVERLAY) != 0;
+ if (mStatusHideybar == HIDEYBAR_SHOWING) {
+ // status hideybar requested
+ boolean hideStatusBarWM =
+ (mFocusedWindow.getAttrs().flags
+ & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
+ boolean hideStatusBarSysui =
+ (tmpVisibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+
+ boolean statusHideyAllowed =
+ hideStatusBarWM
+ || (hideStatusBarSysui && overlayAllowed)
+ || statusBarHasFocus;
+
+ if (mStatusBar == null || !statusHideyAllowed) {
+ mStatusHideybar = HIDEYBAR_NONE;
+ if (mStatusBar != null && hideStatusBarSysui) {
+ // clear the clearable flags instead
+ int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
+ if (newVal != mResettingSystemUiFlags) {
+ mResettingSystemUiFlags = newVal;
+ mWindowManagerFuncs.reevaluateStatusBarVisibility();
+ }
+ }
+ } else {
+ // show status hideybar
+ tmpVisibility |= View.STATUS_BAR_OVERLAY;
+ if ((mLastSystemUiFlags & View.STATUS_BAR_OVERLAY) == 0) {
+ tmpVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
+ mStatusBar.showLw(true);
+ }
+ }
+ }
+ if (mNavigationHideybar == HIDEYBAR_SHOWING) {
+ // navigation hideybar requested
+ boolean hideNavigationBarSysui =
+ (tmpVisibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
+ boolean navigationHideyAllowed =
+ hideNavigationBarSysui && overlayAllowed && mNavigationBar != null;
+ if (!navigationHideyAllowed) {
+ mNavigationHideybar = HIDEYBAR_NONE;
+ } else {
+ // show navigation hideybar
+ tmpVisibility |= View.NAVIGATION_BAR_OVERLAY;
+ if ((mLastSystemUiFlags & View.NAVIGATION_BAR_OVERLAY) == 0) {
+ tmpVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
+ mNavigationBar.showLw(true);
+ }
+ }
+ }
+ return tmpVisibility;
+ }
+
+ // TODO temporary helper that allows testing overlay bars on existing apps
+ private static final class OverlayTesting {
+ static String ENABLED_SETTING = "overlay_testing_enabled";
+ static boolean enabled = false;
+ private static final HashSet<String> sForced = new HashSet<String>();
+
+ private static String parseActivity(WindowState win) {
+ if (win != null && win.getAppToken() != null) {
+ String str = win.getAppToken().toString();
+ int end = str.lastIndexOf(' ');
+ if (end > 0) {
+ int start = str.lastIndexOf(' ', end - 1);
+ if (start > -1) {
+ return str.substring(start + 1, end);
+ }
+ }
+ }
+ return null;
+ }
+
+ public static int applyForced(WindowState focused, int vis) {
+ if (sForced.contains(parseActivity(focused))) {
+ vis |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_ALLOW_OVERLAY;
+ }
+ return vis;
+ }
+
+ public static void toggleForceOverlay(WindowState focused, Context context) {
+ String activity = parseActivity(focused);
+ if (activity != null) {
+ String action;
+ if (sForced.contains(activity)) {
+ sForced.remove(activity);
+ action = "Force overlay disabled";
+ } else {
+ sForced.add(activity);
+ action = "Force overlay enabled";
+ }
+ android.widget.Toast.makeText(context,
+ action + " for " + activity, android.widget.Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
// Use this instead of checking config_showNavigationBar so that it can be consistently
// overridden by qemu.hw.mainkeys in the emulator.
+ @Override
public boolean hasNavigationBar() {
return mHasNavigationBar;
}
@@ -4934,8 +5140,8 @@
@Override
public void setCurrentUserLw(int newUserId) {
- if (mKeyguardMediator != null) {
- mKeyguardMediator.setCurrentUser(newUserId);
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.setCurrentUser(newUserId);
}
if (mStatusBarService != null) {
try {
@@ -4949,7 +5155,7 @@
@Override
public void showAssistant() {
- mKeyguardMediator.showAssistant();
+ mKeyguardDelegate.showAssistant();
}
@Override
diff --git a/policy/src/com/android/internal/policy/impl/SystemGesturesPointerEventListener.java b/policy/src/com/android/internal/policy/impl/SystemGesturesPointerEventListener.java
new file mode 100644
index 0000000..6e8c841
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/SystemGesturesPointerEventListener.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy.PointerEventListener;
+
+/*
+ * Listens for system-wide input gestures, firing callbacks when detected.
+ * @hide
+ */
+public class SystemGesturesPointerEventListener implements PointerEventListener {
+ private static final String TAG = "SystemGestures";
+ private static final boolean DEBUG = false;
+ private static final long SWIPE_TIMEOUT_MS = 500;
+ private static final int MAX_TRACKED_POINTERS = 32; // max per input system
+ private static final int UNTRACKED_POINTER = -1;
+
+ private static final int SWIPE_NONE = 0;
+ private static final int SWIPE_FROM_TOP = 1;
+ private static final int SWIPE_FROM_BOTTOM = 2;
+ private static final int SWIPE_FROM_RIGHT = 3;
+
+ private final int mSwipeStartThreshold;
+ private final int mSwipeEndThreshold;
+ private final Callbacks mCallbacks;
+ private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
+ private final float[] mDownX = new float[MAX_TRACKED_POINTERS];
+ private final float[] mDownY = new float[MAX_TRACKED_POINTERS];
+ private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
+
+ int screenHeight;
+ int screenWidth;
+ private int mDownPointers;
+ private boolean mSwipeFireable;
+ private boolean mDebugFireable;
+
+ public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
+ mCallbacks = checkNull("callbacks", callbacks);
+ mSwipeStartThreshold = checkNull("context", context).getResources()
+ .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ mSwipeEndThreshold = mSwipeStartThreshold * 2;
+ }
+
+ private static <T> T checkNull(String name, T arg) {
+ if (arg == null) {
+ throw new IllegalArgumentException(name + " must not be null");
+ }
+ return arg;
+ }
+
+ @Override
+ public void onPointerEvent(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mSwipeFireable = true;
+ mDebugFireable = true;
+ mDownPointers = 0;
+ captureDown(event, 0);
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ captureDown(event, event.getActionIndex());
+ if (mDebugFireable) {
+ mDebugFireable = event.getPointerCount() < 5;
+ if (!mDebugFireable) {
+ if (DEBUG) Slog.d(TAG, "Firing debug");
+ mCallbacks.onDebug();
+ }
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mSwipeFireable) {
+ final int swipe = detectSwipe(event);
+ mSwipeFireable = swipe == SWIPE_NONE;
+ if (swipe == SWIPE_FROM_TOP) {
+ if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop");
+ mCallbacks.onSwipeFromTop();
+ } else if (swipe == SWIPE_FROM_BOTTOM) {
+ if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom");
+ mCallbacks.onSwipeFromBottom();
+ } else if (swipe == SWIPE_FROM_RIGHT) {
+ if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
+ mCallbacks.onSwipeFromRight();
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mSwipeFireable = false;
+ mDebugFireable = false;
+ break;
+ default:
+ if (DEBUG) Slog.d(TAG, "Ignoring " + event);
+ }
+ }
+
+ private void captureDown(MotionEvent event, int pointerIndex) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final int i = findIndex(pointerId);
+ if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
+ " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
+ if (i != UNTRACKED_POINTER) {
+ mDownX[i] = event.getX(pointerIndex);
+ mDownY[i] = event.getY(pointerIndex);
+ mDownTime[i] = event.getEventTime();
+ if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
+ " down x=" + mDownX[i] + " y=" + mDownY[i]);
+ }
+ }
+
+ private int findIndex(int pointerId) {
+ for (int i = 0; i < mDownPointers; i++) {
+ if (mDownPointerId[i] == pointerId) {
+ return i;
+ }
+ }
+ if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
+ return UNTRACKED_POINTER;
+ }
+ mDownPointerId[mDownPointers++] = pointerId;
+ return mDownPointers - 1;
+ }
+
+ private int detectSwipe(MotionEvent move) {
+ final int historySize = move.getHistorySize();
+ final int pointerCount = move.getPointerCount();
+ for (int p = 0; p < pointerCount; p++) {
+ final int pointerId = move.getPointerId(p);
+ final int i = findIndex(pointerId);
+ if (i != UNTRACKED_POINTER) {
+ for (int h = 0; h < historySize; h++) {
+ final long time = move.getHistoricalEventTime(h);
+ final float x = move.getHistoricalX(p, h);
+ final float y = move.getHistoricalY(p, h);
+ final int swipe = detectSwipe(i, time, x, y);
+ if (swipe != SWIPE_NONE) {
+ return swipe;
+ }
+ }
+ final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p));
+ if (swipe != SWIPE_NONE) {
+ return swipe;
+ }
+ }
+ }
+ return SWIPE_NONE;
+ }
+
+ private int detectSwipe(int i, long time, float x, float y) {
+ final float fromX = mDownX[i];
+ final float fromY = mDownY[i];
+ final long elapsed = time - mDownTime[i];
+ if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
+ + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
+ if (fromY <= mSwipeStartThreshold
+ && y > fromY + mSwipeEndThreshold
+ && elapsed < SWIPE_TIMEOUT_MS) {
+ return SWIPE_FROM_TOP;
+ }
+ if (fromY >= screenHeight - mSwipeStartThreshold
+ && y < fromY - mSwipeEndThreshold
+ && elapsed < SWIPE_TIMEOUT_MS) {
+ return SWIPE_FROM_BOTTOM;
+ }
+ if (fromX >= screenWidth - mSwipeStartThreshold
+ && x < fromX - mSwipeEndThreshold
+ && elapsed < SWIPE_TIMEOUT_MS) {
+ return SWIPE_FROM_RIGHT;
+ }
+ return SWIPE_NONE;
+ }
+
+ interface Callbacks {
+ void onSwipeFromTop();
+ void onSwipeFromBottom();
+ void onSwipeFromRight();
+ void onDebug();
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java
deleted file mode 100644
index e65a716f..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.view.View;
-
-interface BiometricSensorUnlock {
- /**
- * Initializes the view provided for the biometric unlock UI to work within. The provided area
- * completely covers the backup unlock mechanism.
- * @param biometricUnlockView View provided for the biometric unlock UI.
- */
- public void initializeView(View biometricUnlockView);
-
- /**
- * Indicates whether the biometric unlock is running. Before
- * {@link BiometricSensorUnlock#start} is called, isRunning() returns false. After a successful
- * call to {@link BiometricSensorUnlock#start}, isRunning() returns true until the biometric
- * unlock completes, {@link BiometricSensorUnlock#stop} has been called, or an error has
- * forced the biometric unlock to stop.
- * @return whether the biometric unlock is currently running.
- */
- public boolean isRunning();
-
- /**
- * Stops and removes the biometric unlock and shows the backup unlock
- */
- public void stopAndShowBackup();
-
- /**
- * Binds to the biometric unlock service and starts the unlock procedure. Called on the UI
- * thread.
- * @return false if it can't be started or the backup should be used.
- */
- public boolean start();
-
- /**
- * Stops the biometric unlock procedure and unbinds from the service. Called on the UI thread.
- * @return whether the biometric unlock was running when called.
- */
- public boolean stop();
-
- /**
- * Cleans up any resources used by the biometric unlock.
- */
- public void cleanUp();
-
- /**
- * Gets the Device Policy Manager quality of the biometric unlock sensor
- * (e.g., PASSWORD_QUALITY_BIOMETRIC_WEAK).
- * @return biometric unlock sensor quality, as defined by Device Policy Manager.
- */
- public int getQuality();
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
deleted file mode 100644
index 762711d..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-
-import com.android.internal.R;
-import com.android.internal.policy.impl.keyguard.KeyguardActivityLauncher.CameraWidgetInfo;
-
-public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnClickListener {
- private static final String TAG = CameraWidgetFrame.class.getSimpleName();
- private static final boolean DEBUG = KeyguardHostView.DEBUG;
- private static final int WIDGET_ANIMATION_DURATION = 250; // ms
- private static final int WIDGET_WAIT_DURATION = 650; // ms
- private static final int RECOVERY_DELAY = 1000; // ms
-
- interface Callbacks {
- void onLaunchingCamera();
- void onCameraLaunchedSuccessfully();
- void onCameraLaunchedUnsuccessfully();
- }
-
- private final Handler mHandler = new Handler();
- private final KeyguardActivityLauncher mActivityLauncher;
- private final Callbacks mCallbacks;
- private final CameraWidgetInfo mWidgetInfo;
- private final WindowManager mWindowManager;
- private final Point mRenderedSize = new Point();
- private final int[] mTmpLoc = new int[2];
- private final Rect mTmpRect = new Rect();
-
- private long mLaunchCameraStart;
- private boolean mActive;
- private boolean mTransitioning;
- private boolean mDown;
-
- private FixedSizeFrameLayout mPreview;
- private View mFullscreenPreview;
-
- private final Runnable mTransitionToCameraRunnable = new Runnable() {
- @Override
- public void run() {
- transitionToCamera();
- }};
-
- private final Runnable mTransitionToCameraEndAction = new Runnable() {
- @Override
- public void run() {
- if (!mTransitioning)
- return;
- Handler worker = getWorkerHandler() != null ? getWorkerHandler() : mHandler;
- mLaunchCameraStart = SystemClock.uptimeMillis();
- if (DEBUG) Log.d(TAG, "Launching camera at " + mLaunchCameraStart);
- mActivityLauncher.launchCamera(worker, mSecureCameraActivityStartedRunnable);
- }};
-
- private final Runnable mPostTransitionToCameraEndAction = new Runnable() {
- @Override
- public void run() {
- mHandler.post(mTransitionToCameraEndAction);
- }};
-
- private final Runnable mRecoverRunnable = new Runnable() {
- @Override
- public void run() {
- recover();
- }};
-
- private final Runnable mRenderRunnable = new Runnable() {
- @Override
- public void run() {
- render();
- }};
-
- private final Runnable mSecureCameraActivityStartedRunnable = new Runnable() {
- @Override
- public void run() {
- onSecureCameraActivityStarted();
- }
- };
-
- private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
- private boolean mShowing;
- void onKeyguardVisibilityChanged(boolean showing) {
- if (mShowing == showing)
- return;
- mShowing = showing;
- CameraWidgetFrame.this.onKeyguardVisibilityChanged(mShowing);
- };
- };
-
- private static final class FixedSizeFrameLayout extends FrameLayout {
- int width;
- int height;
-
- FixedSizeFrameLayout(Context context) {
- super(context);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- measureChildren(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
- setMeasuredDimension(width, height);
- }
- }
-
- private CameraWidgetFrame(Context context, Callbacks callbacks,
- KeyguardActivityLauncher activityLauncher,
- CameraWidgetInfo widgetInfo, View previewWidget) {
- super(context);
- mCallbacks = callbacks;
- mActivityLauncher = activityLauncher;
- mWidgetInfo = widgetInfo;
- mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- KeyguardUpdateMonitor.getInstance(context).registerCallback(mCallback);
-
- mPreview = new FixedSizeFrameLayout(context);
- mPreview.addView(previewWidget);
- addView(mPreview);
-
- View clickBlocker = new View(context);
- clickBlocker.setBackgroundColor(Color.TRANSPARENT);
- clickBlocker.setOnClickListener(this);
- addView(clickBlocker);
-
- setContentDescription(context.getString(R.string.keyguard_accessibility_camera));
- if (DEBUG) Log.d(TAG, "new CameraWidgetFrame instance " + instanceId());
- }
-
- public static CameraWidgetFrame create(Context context, Callbacks callbacks,
- KeyguardActivityLauncher launcher) {
- if (context == null || callbacks == null || launcher == null)
- return null;
-
- CameraWidgetInfo widgetInfo = launcher.getCameraWidgetInfo();
- if (widgetInfo == null)
- return null;
- View previewWidget = getPreviewWidget(context, widgetInfo);
- if (previewWidget == null)
- return null;
-
- return new CameraWidgetFrame(context, callbacks, launcher, widgetInfo, previewWidget);
- }
-
- private static View getPreviewWidget(Context context, CameraWidgetInfo widgetInfo) {
- return widgetInfo.layoutId > 0 ?
- inflateWidgetView(context, widgetInfo) :
- inflateGenericWidgetView(context);
- }
-
- private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) {
- if (DEBUG) Log.d(TAG, "inflateWidgetView: " + widgetInfo.contextPackage);
- View widgetView = null;
- Exception exception = null;
- try {
- Context cameraContext = context.createPackageContext(
- widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
- LayoutInflater cameraInflater = (LayoutInflater)
- cameraContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- cameraInflater = cameraInflater.cloneInContext(cameraContext);
- widgetView = cameraInflater.inflate(widgetInfo.layoutId, null, false);
- } catch (NameNotFoundException e) {
- exception = e;
- } catch (RuntimeException e) {
- exception = e;
- }
- if (exception != null) {
- Log.w(TAG, "Error creating camera widget view", exception);
- }
- return widgetView;
- }
-
- private static View inflateGenericWidgetView(Context context) {
- if (DEBUG) Log.d(TAG, "inflateGenericWidgetView");
- ImageView iv = new ImageView(context);
- iv.setImageResource(com.android.internal.R.drawable.ic_lockscreen_camera);
- iv.setScaleType(ScaleType.CENTER);
- iv.setBackgroundColor(Color.argb(127, 0, 0, 0));
- return iv;
- }
-
- private void render() {
- final View root = getRootView();
- final int width = root.getWidth();
- final int height = root.getHeight();
- if (mRenderedSize.x == width && mRenderedSize.y == height) {
- if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s", width, height));
- return;
- }
- if (width == 0 || height == 0) {
- return;
- }
-
- mPreview.width = width;
- mPreview.height = height;
- mPreview.requestLayout();
-
- final int thisWidth = getWidth() - getPaddingLeft() - getPaddingRight();
- final int thisHeight = getHeight() - getPaddingTop() - getPaddingBottom();
-
- final float pvScaleX = (float) thisWidth / width;
- final float pvScaleY = (float) thisHeight / height;
- final float pvScale = Math.min(pvScaleX, pvScaleY);
-
- final int pvWidth = (int) (pvScale * width);
- final int pvHeight = (int) (pvScale * height);
-
- final float pvTransX = pvWidth < thisWidth ? (thisWidth - pvWidth) / 2 : 0;
- final float pvTransY = pvHeight < thisHeight ? (thisHeight - pvHeight) / 2 : 0;
-
- mPreview.setPivotX(0);
- mPreview.setPivotY(0);
- mPreview.setScaleX(pvScale);
- mPreview.setScaleY(pvScale);
- mPreview.setTranslationX(pvTransX);
- mPreview.setTranslationY(pvTransY);
-
- mRenderedSize.set(width, height);
- if (DEBUG) Log.d(TAG, String.format("Rendered camera widget size=%sx%s instance=%s",
- width, height, instanceId()));
- }
-
- private void transitionToCamera() {
- if (mTransitioning || mDown) return;
-
- mTransitioning = true;
-
- enableWindowExitAnimation(false);
-
- mPreview.getLocationInWindow(mTmpLoc);
- final float pvHeight = mPreview.getHeight() * mPreview.getScaleY();
- final float pvCenter = mTmpLoc[1] + pvHeight / 2f;
-
- final ViewGroup root = (ViewGroup) getRootView();
- if (mFullscreenPreview == null) {
- mFullscreenPreview = getPreviewWidget(mContext, mWidgetInfo);
- mFullscreenPreview.setClickable(false);
- root.addView(mFullscreenPreview);
- }
-
- root.getWindowVisibleDisplayFrame(mTmpRect);
- final float fsHeight = mTmpRect.height();
- final float fsCenter = mTmpRect.top + fsHeight / 2;
-
- final float fsScaleY = pvHeight / fsHeight;
- final float fsTransY = pvCenter - fsCenter;
- final float fsScaleX = mPreview.getScaleX();
-
- mPreview.setVisibility(View.GONE);
- mFullscreenPreview.setVisibility(View.VISIBLE);
- mFullscreenPreview.setTranslationY(fsTransY);
- mFullscreenPreview.setScaleX(fsScaleX);
- mFullscreenPreview.setScaleY(fsScaleY);
- mFullscreenPreview
- .animate()
- .scaleX(1)
- .scaleY(1)
- .translationX(0)
- .translationY(0)
- .setDuration(WIDGET_ANIMATION_DURATION)
- .withEndAction(mPostTransitionToCameraEndAction)
- .start();
- mCallbacks.onLaunchingCamera();
- }
-
- private void recover() {
- if (DEBUG) Log.d(TAG, "recovering at " + SystemClock.uptimeMillis());
- mCallbacks.onCameraLaunchedUnsuccessfully();
- reset();
- }
-
- @Override
- public void setOnLongClickListener(OnLongClickListener l) {
- // ignore
- }
-
- @Override
- public void onClick(View v) {
- if (DEBUG) Log.d(TAG, "clicked");
- if (mTransitioning) return;
- if (mActive) {
- cancelTransitionToCamera();
- transitionToCamera();
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- if (DEBUG) Log.d(TAG, "onDetachedFromWindow: instance " + instanceId()
- + " at " + SystemClock.uptimeMillis());
- super.onDetachedFromWindow();
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mCallback);
- cancelTransitionToCamera();
- mHandler.removeCallbacks(mRecoverRunnable);
- }
-
- @Override
- public void onActive(boolean isActive) {
- mActive = isActive;
- if (mActive) {
- rescheduleTransitionToCamera();
- } else {
- reset();
- }
- }
-
- @Override
- public boolean onUserInteraction(MotionEvent event) {
- if (mTransitioning) {
- if (DEBUG) Log.d(TAG, "onUserInteraction eaten: mTransitioning");
- return true;
- }
-
- getLocationOnScreen(mTmpLoc);
- int rawBottom = mTmpLoc[1] + getHeight();
- if (event.getRawY() > rawBottom) {
- if (DEBUG) Log.d(TAG, "onUserInteraction eaten: below widget");
- return true;
- }
-
- int action = event.getAction();
- mDown = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE;
- if (mActive) {
- rescheduleTransitionToCamera();
- }
- if (DEBUG) Log.d(TAG, "onUserInteraction observed, not eaten");
- return false;
- }
-
- @Override
- protected void onFocusLost() {
- if (DEBUG) Log.d(TAG, "onFocusLost at " + SystemClock.uptimeMillis());
- cancelTransitionToCamera();
- super.onFocusLost();
- }
-
- public void onScreenTurnedOff() {
- if (DEBUG) Log.d(TAG, "onScreenTurnedOff");
- reset();
- }
-
- private void rescheduleTransitionToCamera() {
- if (DEBUG) Log.d(TAG, "rescheduleTransitionToCamera at " + SystemClock.uptimeMillis());
- mHandler.removeCallbacks(mTransitionToCameraRunnable);
- mHandler.postDelayed(mTransitionToCameraRunnable, WIDGET_WAIT_DURATION);
- }
-
- private void cancelTransitionToCamera() {
- if (DEBUG) Log.d(TAG, "cancelTransitionToCamera at " + SystemClock.uptimeMillis());
- mHandler.removeCallbacks(mTransitionToCameraRunnable);
- }
-
- private void onCameraLaunched() {
- mCallbacks.onCameraLaunchedSuccessfully();
- reset();
- }
-
- private void reset() {
- if (DEBUG) Log.d(TAG, "reset at " + SystemClock.uptimeMillis());
- mLaunchCameraStart = 0;
- mTransitioning = false;
- mDown = false;
- cancelTransitionToCamera();
- mHandler.removeCallbacks(mRecoverRunnable);
- mPreview.setVisibility(View.VISIBLE);
- if (mFullscreenPreview != null) {
- mFullscreenPreview.animate().cancel();
- mFullscreenPreview.setVisibility(View.GONE);
- }
- enableWindowExitAnimation(true);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- if (DEBUG) Log.d(TAG, String.format("onSizeChanged new=%sx%s old=%sx%s at %s",
- w, h, oldw, oldh, SystemClock.uptimeMillis()));
- mHandler.post(mRenderRunnable);
- super.onSizeChanged(w, h, oldw, oldh);
- }
-
- @Override
- public void onBouncerShowing(boolean showing) {
- if (showing) {
- mTransitioning = false;
- mHandler.post(mRecoverRunnable);
- }
- }
-
- private void enableWindowExitAnimation(boolean isEnabled) {
- View root = getRootView();
- ViewGroup.LayoutParams lp = root.getLayoutParams();
- if (!(lp instanceof WindowManager.LayoutParams))
- return;
- WindowManager.LayoutParams wlp = (WindowManager.LayoutParams) lp;
- int newWindowAnimations = isEnabled ? com.android.internal.R.style.Animation_LockScreen : 0;
- if (newWindowAnimations != wlp.windowAnimations) {
- if (DEBUG) Log.d(TAG, "setting windowAnimations to: " + newWindowAnimations
- + " at " + SystemClock.uptimeMillis());
- wlp.windowAnimations = newWindowAnimations;
- mWindowManager.updateViewLayout(root, wlp);
- }
- }
-
- private void onKeyguardVisibilityChanged(boolean showing) {
- if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged " + showing
- + " at " + SystemClock.uptimeMillis());
- if (mTransitioning && !showing) {
- mTransitioning = false;
- mHandler.removeCallbacks(mRecoverRunnable);
- if (mLaunchCameraStart > 0) {
- long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
- if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
- mLaunchCameraStart = 0;
- onCameraLaunched();
- }
- }
- }
-
- private void onSecureCameraActivityStarted() {
- if (DEBUG) Log.d(TAG, "onSecureCameraActivityStarted at " + SystemClock.uptimeMillis());
- mHandler.postDelayed(mRecoverRunnable, RECOVERY_DELAY);
- }
-
- private String instanceId() {
- return Integer.toHexString(hashCode());
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java b/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java
deleted file mode 100644
index a38e86d..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.widget.LockPatternUtils;
-
-public class CarrierText extends TextView {
- private static CharSequence mSeparator;
-
- private LockPatternUtils mLockPatternUtils;
-
- private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
- private CharSequence mPlmn;
- private CharSequence mSpn;
- private State mSimState;
-
- @Override
- public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
- mPlmn = plmn;
- mSpn = spn;
- updateCarrierText(mSimState, mPlmn, mSpn);
- }
-
- @Override
- public void onSimStateChanged(IccCardConstants.State simState) {
- mSimState = simState;
- updateCarrierText(mSimState, mPlmn, mSpn);
- }
- };
- /**
- * The status of this lock screen. Primarily used for widgets on LockScreen.
- */
- private static enum StatusMode {
- Normal, // Normal case (sim card present, it's not locked)
- NetworkLocked, // SIM card is 'network locked'.
- SimMissing, // SIM card is missing.
- SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
- SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
- SimLocked, // SIM card is currently locked
- SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
- SimNotReady; // SIM is not ready yet. May never be on devices w/o a SIM.
- }
-
- public CarrierText(Context context) {
- this(context, null);
- }
-
- public CarrierText(Context context, AttributeSet attrs) {
- super(context, attrs);
- mLockPatternUtils = new LockPatternUtils(mContext);
- }
-
- protected void updateCarrierText(State simState, CharSequence plmn, CharSequence spn) {
- CharSequence text = getCarrierTextForSimState(simState, plmn, spn);
- if (KeyguardViewManager.USE_UPPER_CASE) {
- setText(text != null ? text.toString().toUpperCase() : null);
- } else {
- setText(text);
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mSeparator = getResources().getString(R.string.kg_text_message_separator);
- setSelected(true); // Allow marquee to work.
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mCallback);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mCallback);
- }
-
- /**
- * Top-level function for creating carrier text. Makes text based on simState, PLMN
- * and SPN as well as device capabilities, such as being emergency call capable.
- *
- * @param simState
- * @param plmn
- * @param spn
- * @return
- */
- private CharSequence getCarrierTextForSimState(IccCardConstants.State simState,
- CharSequence plmn, CharSequence spn) {
- CharSequence carrierText = null;
- StatusMode status = getStatusForIccState(simState);
- switch (status) {
- case Normal:
- carrierText = concatenate(plmn, spn);
- break;
-
- case SimNotReady:
- carrierText = null; // nothing to display yet.
- break;
-
- case NetworkLocked:
- carrierText = makeCarrierStringOnEmergencyCapable(
- mContext.getText(R.string.lockscreen_network_locked_message), plmn);
- break;
-
- case SimMissing:
- // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
- // This depends on mPlmn containing the text "Emergency calls only" when the radio
- // has some connectivity. Otherwise, it should be null or empty and just show
- // "No SIM card"
- carrierText = makeCarrierStringOnEmergencyCapable(
- getContext().getText(R.string.lockscreen_missing_sim_message_short),
- plmn);
- break;
-
- case SimPermDisabled:
- carrierText = getContext().getText(
- R.string.lockscreen_permanent_disabled_sim_message_short);
- break;
-
- case SimMissingLocked:
- carrierText = makeCarrierStringOnEmergencyCapable(
- getContext().getText(R.string.lockscreen_missing_sim_message_short),
- plmn);
- break;
-
- case SimLocked:
- carrierText = makeCarrierStringOnEmergencyCapable(
- getContext().getText(R.string.lockscreen_sim_locked_message),
- plmn);
- break;
-
- case SimPukLocked:
- carrierText = makeCarrierStringOnEmergencyCapable(
- getContext().getText(R.string.lockscreen_sim_puk_locked_message),
- plmn);
- break;
- }
-
- return carrierText;
- }
-
- /*
- * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
- */
- private CharSequence makeCarrierStringOnEmergencyCapable(
- CharSequence simMessage, CharSequence emergencyCallMessage) {
- if (mLockPatternUtils.isEmergencyCallCapable()) {
- return concatenate(simMessage, emergencyCallMessage);
- }
- return simMessage;
- }
-
- /**
- * Determine the current status of the lock screen given the SIM state and other stuff.
- */
- private StatusMode getStatusForIccState(IccCardConstants.State simState) {
- // Since reading the SIM may take a while, we assume it is present until told otherwise.
- if (simState == null) {
- return StatusMode.Normal;
- }
-
- final boolean missingAndNotProvisioned =
- !KeyguardUpdateMonitor.getInstance(mContext).isDeviceProvisioned()
- && (simState == IccCardConstants.State.ABSENT ||
- simState == IccCardConstants.State.PERM_DISABLED);
-
- // Assume we're NETWORK_LOCKED if not provisioned
- simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState;
- switch (simState) {
- case ABSENT:
- return StatusMode.SimMissing;
- case NETWORK_LOCKED:
- return StatusMode.SimMissingLocked;
- case NOT_READY:
- return StatusMode.SimNotReady;
- case PIN_REQUIRED:
- return StatusMode.SimLocked;
- case PUK_REQUIRED:
- return StatusMode.SimPukLocked;
- case READY:
- return StatusMode.Normal;
- case PERM_DISABLED:
- return StatusMode.SimPermDisabled;
- case UNKNOWN:
- return StatusMode.SimMissing;
- }
- return StatusMode.SimMissing;
- }
-
- private static CharSequence concatenate(CharSequence plmn, CharSequence spn) {
- final boolean plmnValid = !TextUtils.isEmpty(plmn);
- final boolean spnValid = !TextUtils.isEmpty(spn);
- if (plmnValid && spnValid) {
- return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
- } else if (plmnValid) {
- return plmn;
- } else if (spnValid) {
- return spn;
- } else {
- return "";
- }
- }
-
- private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simState,
- String plmn, String spn) {
- int carrierHelpTextId = 0;
- StatusMode status = getStatusForIccState(simState);
- switch (status) {
- case NetworkLocked:
- carrierHelpTextId = R.string.lockscreen_instructions_when_pattern_disabled;
- break;
-
- case SimMissing:
- carrierHelpTextId = R.string.lockscreen_missing_sim_instructions_long;
- break;
-
- case SimPermDisabled:
- carrierHelpTextId = R.string.lockscreen_permanent_disabled_sim_instructions;
- break;
-
- case SimMissingLocked:
- carrierHelpTextId = R.string.lockscreen_missing_sim_instructions;
- break;
-
- case Normal:
- case SimLocked:
- case SimPukLocked:
- break;
- }
-
- return mContext.getText(carrierHelpTextId);
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
deleted file mode 100644
index 8ece559..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-/**
- * Interface implemented by ViewGroup-derived layouts that implement
- * special logic for presenting security challenges to the user.
- */
-public interface ChallengeLayout {
- /**
- * @return true if the security challenge area of this layout is currently visible
- */
- boolean isChallengeShowing();
-
- /**
- * @return true if the challenge area significantly overlaps other content
- */
- boolean isChallengeOverlapping();
-
- /**
- * Show or hide the challenge layout.
- *
- * If you want to show the challenge layout in bouncer mode where applicable,
- * use {@link #showBouncer()} instead.
- *
- * @param b true to show, false to hide
- */
- void showChallenge(boolean b);
-
- /**
- * Show the bouncer challenge. This may block access to other child views.
- */
- void showBouncer();
-
- /**
- * Hide the bouncer challenge if it is currently showing.
- * This may restore previously blocked access to other child views.
- */
- void hideBouncer();
-
- /**
- * Returns true if the challenge is currently in bouncer mode,
- * potentially blocking access to other child views.
- */
- boolean isBouncing();
-
- /**
- * Returns the duration of the bounce animation.
- */
- int getBouncerAnimationDuration();
-
- /**
- * Set a listener that will respond to changes in bouncer state.
- *
- * @param listener listener to register
- */
- void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener);
-
- /**
- * Listener interface that reports changes in bouncer state.
- * The bouncer is
- */
- public interface OnBouncerStateChangedListener {
- /**
- * Called when the bouncer state changes.
- * The bouncer is activated when the user must pass a security challenge
- * to proceed with the requested action.
- *
- * <p>This differs from simply showing or hiding the security challenge
- * as the bouncer will prevent interaction with other elements of the UI.
- * If the user attempts to escape from the bouncer, it will be dismissed,
- * this method will be called with false as the parameter, and the action
- * should be canceled. If the security component reports a successful
- * authentication and the containing code calls hideBouncer() as a result,
- * this method will also be called with a false parameter. It is up to the
- * caller of hideBouncer to be ready for this.</p>
- *
- * @param bouncerActive true if the bouncer is now active,
- * false if the bouncer was dismissed.
- */
- public void onBouncerStateChanged(boolean bouncerActive);
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java b/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java
deleted file mode 100644
index 4825e23..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-public class CheckLongPressHelper {
- private View mView;
- private boolean mHasPerformedLongPress;
- private CheckForLongPress mPendingCheckForLongPress;
- private float mDownX, mDownY;
- private int mLongPressTimeout;
- private int mScaledTouchSlop;
-
- class CheckForLongPress implements Runnable {
- public void run() {
- if ((mView.getParent() != null) && mView.hasWindowFocus()
- && !mHasPerformedLongPress) {
- if (mView.performLongClick()) {
- mView.setPressed(false);
- mHasPerformedLongPress = true;
- }
- }
- }
- }
-
- public CheckLongPressHelper(View v) {
- mScaledTouchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
- mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
- mView = v;
- }
-
- public void postCheckForLongPress(MotionEvent ev) {
- mDownX = ev.getX();
- mDownY = ev.getY();
- mHasPerformedLongPress = false;
-
- if (mPendingCheckForLongPress == null) {
- mPendingCheckForLongPress = new CheckForLongPress();
- }
- mView.postDelayed(mPendingCheckForLongPress, mLongPressTimeout);
- }
-
- public void onMove(MotionEvent ev) {
- float x = ev.getX();
- float y = ev.getY();
- boolean xMoved = Math.abs(mDownX - x) > mScaledTouchSlop;
- boolean yMoved = Math.abs(mDownY - y) > mScaledTouchSlop;
-
- if (xMoved || yMoved) {
- cancelLongPress();
- }
- }
-
- public void cancelLongPress() {
- mHasPerformedLongPress = false;
- if (mPendingCheckForLongPress != null) {
- mView.removeCallbacks(mPendingCheckForLongPress);
- mPendingCheckForLongPress = null;
- }
- }
-
- public boolean hasPerformedLongPress() {
- return mHasPerformedLongPress;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ClockView.java b/policy/src/com/android/internal/policy/impl/keyguard/ClockView.java
deleted file mode 100644
index 34bf6e7..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/ClockView.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.graphics.Typeface;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.format.DateFormat;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import java.lang.ref.WeakReference;
-import java.text.DateFormatSymbols;
-import java.util.Calendar;
-import com.android.internal.R;
-
-/**
- * Displays the time
- */
-public class ClockView extends RelativeLayout {
- private static final String ANDROID_CLOCK_FONT_FILE = "/system/fonts/AndroidClock.ttf";
- private final static String M12 = "h:mm";
- private final static String M24 = "HH:mm";
-
- private Calendar mCalendar;
- private String mFormat;
- private TextView mTimeView;
- private AmPm mAmPm;
- private ContentObserver mFormatChangeObserver;
- private int mAttached = 0; // for debugging - tells us whether attach/detach is unbalanced
-
- /* called by system on minute ticks */
- private final Handler mHandler = new Handler();
- private BroadcastReceiver mIntentReceiver;
-
- private static class TimeChangedReceiver extends BroadcastReceiver {
- private WeakReference<ClockView> mClock;
- private Context mContext;
-
- public TimeChangedReceiver(ClockView clock) {
- mClock = new WeakReference<ClockView>(clock);
- mContext = clock.getContext();
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- // Post a runnable to avoid blocking the broadcast.
- final boolean timezoneChanged =
- intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED);
- final ClockView clock = mClock.get();
- if (clock != null) {
- clock.mHandler.post(new Runnable() {
- public void run() {
- if (timezoneChanged) {
- clock.mCalendar = Calendar.getInstance();
- }
- clock.updateTime();
- }
- });
- } else {
- try {
- mContext.unregisterReceiver(this);
- } catch (RuntimeException e) {
- // Shouldn't happen
- }
- }
- }
- };
-
- static class AmPm {
- private TextView mAmPmTextView;
- private String mAmString, mPmString;
-
- AmPm(View parent, Typeface tf) {
- // No longer used, uncomment if we decide to use AM/PM indicator again
- // mAmPmTextView = (TextView) parent.findViewById(R.id.am_pm);
- if (mAmPmTextView != null && tf != null) {
- mAmPmTextView.setTypeface(tf);
- }
-
- String[] ampm = new DateFormatSymbols().getAmPmStrings();
- mAmString = ampm[0];
- mPmString = ampm[1];
- }
-
- void setShowAmPm(boolean show) {
- if (mAmPmTextView != null) {
- mAmPmTextView.setVisibility(show ? View.VISIBLE : View.GONE);
- }
- }
-
- void setIsMorning(boolean isMorning) {
- if (mAmPmTextView != null) {
- mAmPmTextView.setText(isMorning ? mAmString : mPmString);
- }
- }
- }
-
- private static class FormatChangeObserver extends ContentObserver {
- private WeakReference<ClockView> mClock;
- private Context mContext;
- public FormatChangeObserver(ClockView clock) {
- super(new Handler());
- mClock = new WeakReference<ClockView>(clock);
- mContext = clock.getContext();
- }
- @Override
- public void onChange(boolean selfChange) {
- ClockView digitalClock = mClock.get();
- if (digitalClock != null) {
- digitalClock.setDateFormat();
- digitalClock.updateTime();
- } else {
- try {
- mContext.getContentResolver().unregisterContentObserver(this);
- } catch (RuntimeException e) {
- // Shouldn't happen
- }
- }
- }
- }
-
- public ClockView(Context context) {
- this(context, null);
- }
-
- public ClockView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mTimeView = (TextView) findViewById(R.id.clock_text);
- mTimeView.setTypeface(Typeface.createFromFile(ANDROID_CLOCK_FONT_FILE));
- mAmPm = new AmPm(this, null);
- mCalendar = Calendar.getInstance();
- setDateFormat();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- mAttached++;
-
- /* monitor time ticks, time changed, timezone */
- if (mIntentReceiver == null) {
- mIntentReceiver = new TimeChangedReceiver(this);
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_TIME_TICK);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.OWNER, filter, null, null );
- }
-
- /* monitor 12/24-hour display preference */
- if (mFormatChangeObserver == null) {
- mFormatChangeObserver = new FormatChangeObserver(this);
- mContext.getContentResolver().registerContentObserver(
- Settings.System.CONTENT_URI, true, mFormatChangeObserver);
- }
-
- updateTime();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- mAttached--;
-
- if (mIntentReceiver != null) {
- mContext.unregisterReceiver(mIntentReceiver);
- }
- if (mFormatChangeObserver != null) {
- mContext.getContentResolver().unregisterContentObserver(
- mFormatChangeObserver);
- }
-
- mFormatChangeObserver = null;
- mIntentReceiver = null;
- }
-
- void updateTime(Calendar c) {
- mCalendar = c;
- updateTime();
- }
-
- public void updateTime() {
- mCalendar.setTimeInMillis(System.currentTimeMillis());
-
- CharSequence newTime = DateFormat.format(mFormat, mCalendar);
- mTimeView.setText(newTime);
- mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0);
- }
-
- private void setDateFormat() {
- mFormat = android.text.format.DateFormat.is24HourFormat(getContext()) ? M24 : M12;
- mAmPm.setShowAmPm(mFormat.equals(M12));
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
deleted file mode 100644
index c68bab5..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.telephony.TelephonyManager;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.widget.LockPatternUtils;
-
-/**
- * This class implements a smart emergency button that updates itself based
- * on telephony state. When the phone is idle, it is an emergency call button.
- * When there's a call in progress, it presents an appropriate message and
- * allows the user to return to the call.
- */
-public class EmergencyButton extends Button {
-
- private static final int EMERGENCY_CALL_TIMEOUT = 10000; // screen timeout after starting e.d.
- private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
-
- KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
-
- @Override
- public void onSimStateChanged(State simState) {
- int phoneState = KeyguardUpdateMonitor.getInstance(mContext).getPhoneState();
- updateEmergencyCallButton(simState, phoneState);
- }
-
- void onPhoneStateChanged(int phoneState) {
- State simState = KeyguardUpdateMonitor.getInstance(mContext).getSimState();
- updateEmergencyCallButton(simState, phoneState);
- };
- };
- private LockPatternUtils mLockPatternUtils;
- private PowerManager mPowerManager;
-
- public EmergencyButton(Context context) {
- this(context, null);
- }
-
- public EmergencyButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mLockPatternUtils = new LockPatternUtils(mContext);
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- takeEmergencyCallAction();
- }
- });
- int phoneState = KeyguardUpdateMonitor.getInstance(mContext).getPhoneState();
- State simState = KeyguardUpdateMonitor.getInstance(mContext).getSimState();
- updateEmergencyCallButton(simState, phoneState);
- }
-
- /**
- * Shows the emergency dialer or returns the user to the existing call.
- */
- public void takeEmergencyCallAction() {
- // TODO: implement a shorter timeout once new PowerManager API is ready.
- // should be the equivalent to the old userActivity(EMERGENCY_CALL_TIMEOUT)
- mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
- if (TelephonyManager.getDefault().getCallState()
- == TelephonyManager.CALL_STATE_OFFHOOK) {
- mLockPatternUtils.resumeCall();
- } else {
- Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- getContext().startActivityAsUser(intent,
- new UserHandle(mLockPatternUtils.getCurrentUser()));
- }
- }
-
- private void updateEmergencyCallButton(State simState, int phoneState) {
- boolean enabled = false;
- if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) {
- enabled = true; // always show "return to call" if phone is off-hook
- } else if (mLockPatternUtils.isEmergencyCallCapable()) {
- boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext).isSimLocked();
- if (simLocked) {
- // Some countries can't handle emergency calls while SIM is locked.
- enabled = mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked();
- } else {
- // True if we need to show a secure screen (pin/pattern/SIM pin/SIM puk);
- // hides emergency button on "Slide" screen if device is not secure.
- enabled = mLockPatternUtils.isSecure();
- }
- }
- mLockPatternUtils.updateEmergencyCallButtonState(this, phoneState, enabled,
- KeyguardViewManager.USE_UPPER_CASE, false);
- }
-
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyCarrierArea.java b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyCarrierArea.java
deleted file mode 100644
index cfe1ef4..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyCarrierArea.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.android.internal.R;
-
-public class EmergencyCarrierArea extends LinearLayout {
-
- private CarrierText mCarrierText;
- private EmergencyButton mEmergencyButton;
-
- public EmergencyCarrierArea(Context context) {
- super(context);
- }
-
- public EmergencyCarrierArea(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mCarrierText = (CarrierText) findViewById(R.id.carrier_text);
- mEmergencyButton = (EmergencyButton) findViewById(R.id.emergency_call_button);
-
- // The emergency button overlaps the carrier text, only noticeable when highlighted.
- // So temporarily hide the carrier text while the emergency button is pressed.
- mEmergencyButton.setOnTouchListener(new OnTouchListener(){
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- switch(event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mCarrierText.animate().alpha(0);
- break;
- case MotionEvent.ACTION_UP:
- mCarrierText.animate().alpha(1);
- break;
- }
- return false;
- }});
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
deleted file mode 100644
index e58eb5b..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import com.android.internal.policy.IFaceLockCallback;
-import com.android.internal.policy.IFaceLockInterface;
-import com.android.internal.widget.LockPatternUtils;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.View;
-
-public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
-
- private static final boolean DEBUG = false;
- private static final String TAG = "FULLockscreen";
-
- private final Context mContext;
- private final LockPatternUtils mLockPatternUtils;
-
- // TODO: is mServiceRunning needed or can we just use mIsRunning or check if mService is null?
- private boolean mServiceRunning = false;
- // TODO: now that the code has been restructure to do almost all operations from a handler, this
- // lock may no longer be necessary.
- private final Object mServiceRunningLock = new Object();
- private IFaceLockInterface mService;
- private boolean mBoundToService = false;
- private View mFaceUnlockView;
-
- private Handler mHandler;
- private final int MSG_SERVICE_CONNECTED = 0;
- private final int MSG_SERVICE_DISCONNECTED = 1;
- private final int MSG_UNLOCK = 2;
- private final int MSG_CANCEL = 3;
- private final int MSG_REPORT_FAILED_ATTEMPT = 4;
- private final int MSG_POKE_WAKELOCK = 5;
-
- // TODO: This was added for the purpose of adhering to what the biometric interface expects
- // the isRunning() function to return. However, it is probably not necessary to have both
- // mRunning and mServiceRunning. I'd just rather wait to change that logic.
- private volatile boolean mIsRunning = false;
-
- // So the user has a consistent amount of time when brought to the backup method from Face
- // Unlock
- private final int BACKUP_LOCK_TIMEOUT = 5000;
-
- KeyguardSecurityCallback mKeyguardScreenCallback;
-
- /**
- * Stores some of the structures that Face Unlock will need to access and creates the handler
- * will be used to execute messages on the UI thread.
- */
- public FaceUnlock(Context context) {
- mContext = context;
- mLockPatternUtils = new LockPatternUtils(context);
- mHandler = new Handler(this);
- }
-
- public void setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback) {
- mKeyguardScreenCallback = keyguardScreenCallback;
- }
-
- /**
- * Stores and displays the view that Face Unlock is allowed to draw within.
- * TODO: since the layout object will eventually be shared by multiple biometric unlock
- * methods, we will have to add our other views (background, cancel button) here.
- */
- public void initializeView(View biometricUnlockView) {
- Log.d(TAG, "initializeView()");
- mFaceUnlockView = biometricUnlockView;
- }
-
- /**
- * Indicates whether Face Unlock is currently running.
- */
- public boolean isRunning() {
- return mIsRunning;
- }
-
- /**
- * Dismisses face unlock and goes to the backup lock
- */
- public void stopAndShowBackup() {
- if (DEBUG) Log.d(TAG, "stopAndShowBackup()");
- mHandler.sendEmptyMessage(MSG_CANCEL);
- }
-
- /**
- * Binds to the Face Unlock service. Face Unlock will be started when the bind completes. The
- * Face Unlock view is displayed to hide the backup lock while the service is starting up.
- * Called on the UI thread.
- */
- public boolean start() {
- if (DEBUG) Log.d(TAG, "start()");
- if (mHandler.getLooper() != Looper.myLooper()) {
- Log.e(TAG, "start() called off of the UI thread");
- }
-
- if (mIsRunning) {
- Log.w(TAG, "start() called when already running");
- }
-
- if (!mBoundToService) {
- Log.d(TAG, "Binding to Face Unlock service for user="
- + mLockPatternUtils.getCurrentUser());
- mContext.bindServiceAsUser(new Intent(IFaceLockInterface.class.getName()),
- mConnection,
- Context.BIND_AUTO_CREATE,
- new UserHandle(mLockPatternUtils.getCurrentUser()));
- mBoundToService = true;
- } else {
- Log.w(TAG, "Attempt to bind to Face Unlock when already bound");
- }
-
- mIsRunning = true;
- return true;
- }
-
- /**
- * Stops Face Unlock and unbinds from the service. Called on the UI thread.
- */
- public boolean stop() {
- if (DEBUG) Log.d(TAG, "stop()");
- if (mHandler.getLooper() != Looper.myLooper()) {
- Log.e(TAG, "stop() called from non-UI thread");
- }
-
- // Clearing any old service connected messages.
- mHandler.removeMessages(MSG_SERVICE_CONNECTED);
-
- boolean mWasRunning = mIsRunning;
-
- stopUi();
-
- if (mBoundToService) {
- if (mService != null) {
- try {
- mService.unregisterCallback(mFaceUnlockCallback);
- } catch (RemoteException e) {
- // Not much we can do
- }
- }
- Log.d(TAG, "Unbinding from Face Unlock service");
- mContext.unbindService(mConnection);
- mBoundToService = false;
- } else {
- // This is usually not an error when this happens. Sometimes we will tell it to
- // unbind multiple times because it's called from both onWindowFocusChanged and
- // onDetachedFromWindow.
- if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound");
- }
- mIsRunning = false;
- return mWasRunning;
- }
-
- /**
- * Frees up resources used by Face Unlock and stops it if it is still running.
- */
- public void cleanUp() {
- if (DEBUG) Log.d(TAG, "cleanUp()");
- if (mService != null) {
- try {
- mService.unregisterCallback(mFaceUnlockCallback);
- } catch (RemoteException e) {
- // Not much we can do
- }
- stopUi();
- mService = null;
- }
- }
-
- /**
- * Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK.
- */
- public int getQuality() {
- return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
- }
-
- /**
- * Handles messages such that everything happens on the UI thread in a deterministic order.
- * Calls from the Face Unlock service come from binder threads. Calls from lockscreen typically
- * come from the UI thread. This makes sure there are no race conditions between those calls.
- */
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SERVICE_CONNECTED:
- handleServiceConnected();
- break;
- case MSG_SERVICE_DISCONNECTED:
- handleServiceDisconnected();
- break;
- case MSG_UNLOCK:
- handleUnlock(msg.arg1);
- break;
- case MSG_CANCEL:
- handleCancel();
- break;
- case MSG_REPORT_FAILED_ATTEMPT:
- handleReportFailedAttempt();
- break;
- case MSG_POKE_WAKELOCK:
- handlePokeWakelock(msg.arg1);
- break;
- default:
- Log.e(TAG, "Unhandled message");
- return false;
- }
- return true;
- }
-
- /**
- * Tells the service to start its UI via an AIDL interface. Called when the
- * onServiceConnected() callback is received.
- */
- void handleServiceConnected() {
- Log.d(TAG, "handleServiceConnected()");
-
- // It is possible that an unbind has occurred in the time between the bind and when this
- // function is reached. If an unbind has already occurred, proceeding on to call startUi()
- // can result in a fatal error. Note that the onServiceConnected() callback is
- // asynchronous, so this possibility would still exist if we executed this directly in
- // onServiceConnected() rather than using a handler.
- if (!mBoundToService) {
- Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound");
- return;
- }
-
- try {
- mService.registerCallback(mFaceUnlockCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString());
- mService = null;
- mBoundToService = false;
- mIsRunning = false;
- return;
- }
-
- if (mFaceUnlockView != null) {
- IBinder windowToken = mFaceUnlockView.getWindowToken();
- if (windowToken != null) {
- // When switching between portrait and landscape view while Face Unlock is running,
- // the screen will eventually go dark unless we poke the wakelock when Face Unlock
- // is restarted.
- mKeyguardScreenCallback.userActivity(0);
-
- int[] position;
- position = new int[2];
- mFaceUnlockView.getLocationInWindow(position);
- startUi(windowToken, position[0], position[1], mFaceUnlockView.getWidth(),
- mFaceUnlockView.getHeight());
- } else {
- Log.e(TAG, "windowToken is null in handleServiceConnected()");
- }
- }
- }
-
- /**
- * Called when the onServiceDisconnected() callback is received. This should not happen during
- * normal operation. It indicates an error has occurred.
- */
- void handleServiceDisconnected() {
- Log.e(TAG, "handleServiceDisconnected()");
- // TODO: this lock may no longer be needed now that everything is being called from a
- // handler
- synchronized (mServiceRunningLock) {
- mService = null;
- mServiceRunning = false;
- }
- mBoundToService = false;
- mIsRunning = false;
- }
-
- /**
- * Stops the Face Unlock service and tells the device to grant access to the user.
- */
- void handleUnlock(int authenticatedUserId) {
- if (DEBUG) Log.d(TAG, "handleUnlock()");
- stop();
- int currentUserId = mLockPatternUtils.getCurrentUser();
- if (authenticatedUserId == currentUserId) {
- if (DEBUG) Log.d(TAG, "Unlocking for user " + authenticatedUserId);
- mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
- mKeyguardScreenCallback.dismiss(true);
- } else {
- Log.d(TAG, "Ignoring unlock for authenticated user (" + authenticatedUserId +
- ") because the current user is " + currentUserId);
- }
- }
-
- /**
- * Stops the Face Unlock service and goes to the backup lock.
- */
- void handleCancel() {
- if (DEBUG) Log.d(TAG, "handleCancel()");
- // We are going to the backup method, so we don't want to see Face Unlock again until the
- // next time the user visits keyguard.
- KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
-
- mKeyguardScreenCallback.showBackupSecurity();
- stop();
- mKeyguardScreenCallback.userActivity(BACKUP_LOCK_TIMEOUT);
- }
-
- /**
- * Increments the number of failed Face Unlock attempts.
- */
- void handleReportFailedAttempt() {
- if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()");
- // We are going to the backup method, so we don't want to see Face Unlock again until the
- // next time the user visits keyguard.
- KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
-
- mKeyguardScreenCallback.reportFailedUnlockAttempt();
- }
-
- /**
- * If the screen is on, pokes the wakelock to keep the screen alive and active for a specific
- * amount of time.
- */
- void handlePokeWakelock(int millis) {
- PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isScreenOn()) {
- mKeyguardScreenCallback.userActivity(millis);
- }
- }
-
- /**
- * Implements service connection methods.
- */
- private ServiceConnection mConnection = new ServiceConnection() {
- /**
- * Called when the Face Unlock service connects after calling bind().
- */
- public void onServiceConnected(ComponentName className, IBinder iservice) {
- Log.d(TAG, "Connected to Face Unlock service");
- mService = IFaceLockInterface.Stub.asInterface(iservice);
- mHandler.sendEmptyMessage(MSG_SERVICE_CONNECTED);
- }
-
- /**
- * Called if the Face Unlock service unexpectedly disconnects. This indicates an error.
- */
- public void onServiceDisconnected(ComponentName className) {
- Log.e(TAG, "Unexpected disconnect from Face Unlock service");
- mHandler.sendEmptyMessage(MSG_SERVICE_DISCONNECTED);
- }
- };
-
- /**
- * Tells the Face Unlock service to start displaying its UI and start processing.
- */
- private void startUi(IBinder windowToken, int x, int y, int w, int h) {
- if (DEBUG) Log.d(TAG, "startUi()");
- synchronized (mServiceRunningLock) {
- if (!mServiceRunning) {
- Log.d(TAG, "Starting Face Unlock");
- try {
- mService.startUi(windowToken, x, y, w, h,
- mLockPatternUtils.isBiometricWeakLivelinessEnabled());
- } catch (RemoteException e) {
- Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString());
- return;
- }
- mServiceRunning = true;
- } else {
- Log.w(TAG, "startUi() attempted while running");
- }
- }
- }
-
- /**
- * Tells the Face Unlock service to stop displaying its UI and stop processing.
- */
- private void stopUi() {
- if (DEBUG) Log.d(TAG, "stopUi()");
- // Note that attempting to stop Face Unlock when it's not running is not an issue.
- // Face Unlock can return, which stops it and then we try to stop it when the
- // screen is turned off. That's why we check.
- synchronized (mServiceRunningLock) {
- if (mServiceRunning) {
- Log.d(TAG, "Stopping Face Unlock");
- try {
- mService.stopUi();
- } catch (RemoteException e) {
- Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString());
- }
- mServiceRunning = false;
- } else {
- // This is usually not an error when this happens. Sometimes we will tell it to
- // stop multiple times because it's called from both onWindowFocusChanged and
- // onDetachedFromWindow.
- if (DEBUG) Log.d(TAG, "stopUi() attempted while not running");
- }
- }
- }
-
- /**
- * Implements the AIDL biometric unlock service callback interface.
- */
- private final IFaceLockCallback mFaceUnlockCallback = new IFaceLockCallback.Stub() {
- /**
- * Called when Face Unlock wants to grant access to the user.
- */
- public void unlock() {
- if (DEBUG) Log.d(TAG, "unlock()");
- Message message = mHandler.obtainMessage(MSG_UNLOCK, UserHandle.getCallingUserId(), -1);
- mHandler.sendMessage(message);
- }
-
- /**
- * Called when Face Unlock wants to go to the backup.
- */
- public void cancel() {
- if (DEBUG) Log.d(TAG, "cancel()");
- mHandler.sendEmptyMessage(MSG_CANCEL);
- }
-
- /**
- * Called when Face Unlock wants to increment the number of failed attempts.
- */
- public void reportFailedAttempt() {
- if (DEBUG) Log.d(TAG, "reportFailedAttempt()");
- mHandler.sendEmptyMessage(MSG_REPORT_FAILED_ATTEMPT);
- }
-
- /**
- * Called when Face Unlock wants to keep the screen alive and active for a specific amount
- * of time.
- */
- public void pokeWakelock(int millis) {
- if (DEBUG) Log.d(TAG, "pokeWakelock() for " + millis + "ms");
- Message message = mHandler.obtainMessage(MSG_POKE_WAKELOCK, millis, -1);
- mHandler.sendMessage(message);
- }
-
- };
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java
deleted file mode 100644
index cc520dc..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.CountDownTimer;
-import android.os.SystemClock;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.TextView.OnEditorActionListener;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
-
-/**
- * Base class for PIN and password unlock screens.
- */
-public abstract class KeyguardAbsKeyInputView extends LinearLayout
- implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
- protected KeyguardSecurityCallback mCallback;
- protected TextView mPasswordEntry;
- protected LockPatternUtils mLockPatternUtils;
- protected SecurityMessageDisplay mSecurityMessageDisplay;
- protected View mEcaView;
- private Drawable mBouncerFrame;
- protected boolean mEnableHaptics;
-
- // To avoid accidental lockout due to events while the device in in the pocket, ignore
- // any passwords with length less than or equal to this length.
- protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
-
- public KeyguardAbsKeyInputView(Context context) {
- this(context, null);
- }
-
- public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- if (hasWindowFocus) {
- reset();
- }
- }
-
- public void reset() {
- // start fresh
- mPasswordEntry.setText("");
- mPasswordEntry.requestFocus();
-
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
- if (deadline != 0) {
- handleAttemptLockout(deadline);
- } else {
- resetState();
- }
- }
-
- protected abstract int getPasswordTextViewId();
- protected abstract void resetState();
-
- @Override
- protected void onFinishInflate() {
- mLockPatternUtils = new LockPatternUtils(mContext);
-
- mPasswordEntry = (TextView) findViewById(getPasswordTextViewId());
- mPasswordEntry.setOnEditorActionListener(this);
- mPasswordEntry.addTextChangedListener(this);
-
- // Set selected property on so the view can send accessibility events.
- mPasswordEntry.setSelected(true);
-
- // Poke the wakelock any time the text is selected or modified
- mPasswordEntry.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mCallback.userActivity(0); // TODO: customize timeout for text?
- }
- });
-
- mPasswordEntry.addTextChangedListener(new TextWatcher() {
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- public void afterTextChanged(Editable s) {
- if (mCallback != null) {
- mCallback.userActivity(0);
- }
- }
- });
- mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
- mEcaView = findViewById(R.id.keyguard_selector_fade_container);
- View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame);
- if (bouncerFrameView != null) {
- mBouncerFrame = bouncerFrameView.getBackground();
- }
- }
-
- @Override
- protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- // send focus to the password field
- return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
- }
-
- /*
- * Override this if you have a different string for "wrong password"
- *
- * Note that PIN/PUK have their own implementation of verifyPasswordAndUnlock and so don't need this
- */
- protected int getWrongPasswordStringId() {
- return R.string.kg_wrong_password;
- }
-
- protected void verifyPasswordAndUnlock() {
- String entry = mPasswordEntry.getText().toString();
- if (mLockPatternUtils.checkPassword(entry)) {
- mCallback.reportSuccessfulUnlockAttempt();
- mCallback.dismiss(true);
- } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
- // to avoid accidental lockout, only count attempts that are long enough to be a
- // real password. This may require some tweaking.
- mCallback.reportFailedUnlockAttempt();
- if (0 == (mCallback.getFailedAttempts()
- % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
- handleAttemptLockout(deadline);
- }
- mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true);
- }
- mPasswordEntry.setText("");
- }
-
- // Prevent user from using the PIN/Password entry until scheduled deadline.
- protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
- mPasswordEntry.setEnabled(false);
- long elapsedRealtime = SystemClock.elapsedRealtime();
- new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- int secondsRemaining = (int) (millisUntilFinished / 1000);
- mSecurityMessageDisplay.setMessage(
- R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining);
- }
-
- @Override
- public void onFinish() {
- mSecurityMessageDisplay.setMessage("", false);
- resetState();
- }
- }.start();
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- mCallback.userActivity(0);
- return false;
- }
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- // Check if this was the result of hitting the enter key
- if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT) {
- verifyPasswordAndUnlock();
- return true;
- }
- return false;
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onPause() {
-
- }
-
- @Override
- public void onResume(int reason) {
- reset();
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- if (mCallback != null) {
- mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS);
- }
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
-
- // Cause a VIRTUAL_KEY vibration
- public void doHapticKeyClick() {
- if (mEnableHaptics) {
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
- | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
- }
- }
-
- @Override
- public void showBouncer(int duration) {
- KeyguardSecurityViewHelper.
- showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
- }
-
- @Override
- public void hideBouncer(int duration) {
- KeyguardSecurityViewHelper.
- hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
- }
-}
-
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
deleted file mode 100644
index e0e7128..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.text.Editable;
-import android.text.InputFilter;
-import android.text.LoginFilter;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.R;
-
-import java.io.IOException;
-
-/**
- * When the user forgets their password a bunch of times, we fall back on their
- * account's login/password to unlock the phone (and reset their lock pattern).
- */
-public class KeyguardAccountView extends LinearLayout implements KeyguardSecurityView,
- View.OnClickListener, TextWatcher {
- private static final int AWAKE_POKE_MILLIS = 30000;
- private static final String LOCK_PATTERN_PACKAGE = "com.android.settings";
- private static final String LOCK_PATTERN_CLASS = LOCK_PATTERN_PACKAGE + ".ChooseLockGeneric";
-
- private KeyguardSecurityCallback mCallback;
- private LockPatternUtils mLockPatternUtils;
- private EditText mLogin;
- private EditText mPassword;
- private Button mOk;
- public boolean mEnableFallback;
- private SecurityMessageDisplay mSecurityMessageDisplay;
-
- /**
- * Shown while making asynchronous check of password.
- */
- private ProgressDialog mCheckingDialog;
-
- public KeyguardAccountView(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardAccountView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardAccountView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mLockPatternUtils = new LockPatternUtils(getContext());
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mLogin = (EditText) findViewById(R.id.login);
- mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } );
- mLogin.addTextChangedListener(this);
-
- mPassword = (EditText) findViewById(R.id.password);
- mPassword.addTextChangedListener(this);
-
- mOk = (Button) findViewById(R.id.ok);
- mOk.setOnClickListener(this);
-
- mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
- reset();
- }
-
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- }
-
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
-
- public void afterTextChanged(Editable s) {
- }
-
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- if (mCallback != null) {
- mCallback.userActivity(AWAKE_POKE_MILLIS);
- }
- }
-
- @Override
- protected boolean onRequestFocusInDescendants(int direction,
- Rect previouslyFocusedRect) {
- // send focus to the login field
- return mLogin.requestFocus(direction, previouslyFocusedRect);
- }
-
- public boolean needsInput() {
- return true;
- }
-
- public void reset() {
- // start fresh
- mLogin.setText("");
- mPassword.setText("");
- mLogin.requestFocus();
- boolean permLocked = mLockPatternUtils.isPermanentlyLocked();
- mSecurityMessageDisplay.setMessage(permLocked ? R.string.kg_login_too_many_attempts :
- R.string.kg_login_instructions, permLocked ? true : false);
- }
-
- /** {@inheritDoc} */
- public void cleanUp() {
- if (mCheckingDialog != null) {
- mCheckingDialog.hide();
- }
- mCallback = null;
- mLockPatternUtils = null;
- }
-
- public void onClick(View v) {
- mCallback.userActivity(0);
- if (v == mOk) {
- asyncCheckPassword();
- }
- }
-
- private void postOnCheckPasswordResult(final boolean success) {
- // ensure this runs on UI thread
- mLogin.post(new Runnable() {
- public void run() {
- if (success) {
- // clear out forgotten password
- mLockPatternUtils.setPermanentlyLocked(false);
- mLockPatternUtils.setLockPatternEnabled(false);
- mLockPatternUtils.saveLockPattern(null);
-
- // launch the 'choose lock pattern' activity so
- // the user can pick a new one if they want to
- Intent intent = new Intent();
- intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivityAsUser(intent,
- new UserHandle(mLockPatternUtils.getCurrentUser()));
- mCallback.reportSuccessfulUnlockAttempt();
-
- // dismiss keyguard
- mCallback.dismiss(true);
- } else {
- mSecurityMessageDisplay.setMessage(R.string.kg_login_invalid_input, true);
- mPassword.setText("");
- mCallback.reportFailedUnlockAttempt();
- }
- }
- });
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN
- && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
- if (mLockPatternUtils.isPermanentlyLocked()) {
- mCallback.dismiss(false);
- } else {
- // TODO: mCallback.forgotPattern(false);
- }
- return true;
- }
- return super.dispatchKeyEvent(event);
- }
-
- /**
- * Given the string the user entered in the 'username' field, find
- * the stored account that they probably intended. Prefer, in order:
- *
- * - an exact match for what was typed, or
- * - a case-insensitive match for what was typed, or
- * - if they didn't include a domain, an exact match of the username, or
- * - if they didn't include a domain, a case-insensitive
- * match of the username.
- *
- * If there is a tie for the best match, choose neither --
- * the user needs to be more specific.
- *
- * @return an account name from the database, or null if we can't
- * find a single best match.
- */
- private Account findIntendedAccount(String username) {
- Account[] accounts = AccountManager.get(mContext).getAccountsByTypeAsUser("com.google",
- new UserHandle(mLockPatternUtils.getCurrentUser()));
-
- // Try to figure out which account they meant if they
- // typed only the username (and not the domain), or got
- // the case wrong.
-
- Account bestAccount = null;
- int bestScore = 0;
- for (Account a: accounts) {
- int score = 0;
- if (username.equals(a.name)) {
- score = 4;
- } else if (username.equalsIgnoreCase(a.name)) {
- score = 3;
- } else if (username.indexOf('@') < 0) {
- int i = a.name.indexOf('@');
- if (i >= 0) {
- String aUsername = a.name.substring(0, i);
- if (username.equals(aUsername)) {
- score = 2;
- } else if (username.equalsIgnoreCase(aUsername)) {
- score = 1;
- }
- }
- }
- if (score > bestScore) {
- bestAccount = a;
- bestScore = score;
- } else if (score == bestScore) {
- bestAccount = null;
- }
- }
- return bestAccount;
- }
-
- private void asyncCheckPassword() {
- mCallback.userActivity(AWAKE_POKE_MILLIS);
- final String login = mLogin.getText().toString();
- final String password = mPassword.getText().toString();
- Account account = findIntendedAccount(login);
- if (account == null) {
- postOnCheckPasswordResult(false);
- return;
- }
- getProgressDialog().show();
- Bundle options = new Bundle();
- options.putString(AccountManager.KEY_PASSWORD, password);
- AccountManager.get(mContext).confirmCredentialsAsUser(account, options, null /* activity */,
- new AccountManagerCallback<Bundle>() {
- public void run(AccountManagerFuture<Bundle> future) {
- try {
- mCallback.userActivity(AWAKE_POKE_MILLIS);
- final Bundle result = future.getResult();
- final boolean verified = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
- postOnCheckPasswordResult(verified);
- } catch (OperationCanceledException e) {
- postOnCheckPasswordResult(false);
- } catch (IOException e) {
- postOnCheckPasswordResult(false);
- } catch (AuthenticatorException e) {
- postOnCheckPasswordResult(false);
- } finally {
- mLogin.post(new Runnable() {
- public void run() {
- getProgressDialog().hide();
- }
- });
- }
- }
- }, null /* handler */, new UserHandle(mLockPatternUtils.getCurrentUser()));
- }
-
- private Dialog getProgressDialog() {
- if (mCheckingDialog == null) {
- mCheckingDialog = new ProgressDialog(mContext);
- mCheckingDialog.setMessage(
- mContext.getString(R.string.kg_login_checking_password));
- mCheckingDialog.setIndeterminate(true);
- mCheckingDialog.setCancelable(false);
- mCheckingDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- return mCheckingDialog;
- }
-
- @Override
- public void onPause() {
-
- }
-
- @Override
- public void onResume(int reason) {
- reset();
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public void showBouncer(int duration) {
- }
-
- @Override
- public void hideBouncer(int duration) {
- }
-}
-
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java
deleted file mode 100644
index 6539db3..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.ActivityManagerNative;
-import android.app.ActivityOptions;
-import android.app.IActivityManager.WaitResult;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.MediaStore;
-import android.util.Log;
-import android.view.WindowManager;
-
-import com.android.internal.policy.impl.keyguard.KeyguardHostView.OnDismissAction;
-import com.android.internal.widget.LockPatternUtils;
-
-import java.util.List;
-
-public abstract class KeyguardActivityLauncher {
- private static final String TAG = KeyguardActivityLauncher.class.getSimpleName();
- private static final boolean DEBUG = KeyguardHostView.DEBUG;
- private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
- private static final Intent SECURE_CAMERA_INTENT =
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
- .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- private static final Intent INSECURE_CAMERA_INTENT =
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
-
- abstract Context getContext();
-
- abstract KeyguardSecurityCallback getCallback();
-
- abstract LockPatternUtils getLockPatternUtils();
-
- public static class CameraWidgetInfo {
- public String contextPackage;
- public int layoutId;
- }
-
- public CameraWidgetInfo getCameraWidgetInfo() {
- CameraWidgetInfo info = new CameraWidgetInfo();
- Intent intent = getCameraIntent();
- PackageManager packageManager = getContext().getPackageManager();
- final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
- intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
- if (appList.size() == 0) {
- if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found");
- return null;
- }
- ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
- getLockPatternUtils().getCurrentUser());
- if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved);
- if (wouldLaunchResolverActivity(resolved, appList)) {
- if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver");
- return info;
- }
- if (resolved == null || resolved.activityInfo == null) {
- return null;
- }
- if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
- if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found");
- return info;
- }
- int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
- if (layoutId == 0) {
- if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified");
- return info;
- }
- info.contextPackage = resolved.activityInfo.packageName;
- info.layoutId = layoutId;
- return info;
- }
-
- public void launchCamera(Handler worker, Runnable onSecureCameraStarted) {
- LockPatternUtils lockPatternUtils = getLockPatternUtils();
- if (lockPatternUtils.isSecure()) {
- // Launch the secure version of the camera
- if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
- // TODO: Show disambiguation dialog instead.
- // For now, we'll treat this like launching any other app from secure keyguard.
- // When they do, user sees the system's ResolverActivity which lets them choose
- // which secure camera to use.
- launchActivity(SECURE_CAMERA_INTENT, false, false, null, null);
- } else {
- launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted);
- }
- } else {
- // Launch the normal camera
- launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null);
- }
- }
-
- public void launchWidgetPicker(int appWidgetId) {
- Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK);
-
- pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
- pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false);
- pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER,
- AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
-
- Bundle options = new Bundle();
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
- AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
- pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
- pickIntent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_SINGLE_TOP
- | Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-
- launchActivity(pickIntent, false, false, null, null);
- }
-
- /**
- * Launches the said intent for the current foreground user.
- *
- * @param intent
- * @param showsWhileLocked true if the activity can be run on top of keyguard.
- * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
- * @param useDefaultAnimations true if default transitions should be used, else suppressed.
- * @param worker if supplied along with onStarted, used to launch the blocking activity call.
- * @param onStarted if supplied along with worker, called after activity is started.
- */
- public void launchActivity(final Intent intent,
- boolean showsWhileLocked,
- boolean useDefaultAnimations,
- final Handler worker,
- final Runnable onStarted) {
-
- final Context context = getContext();
- final Bundle animation = useDefaultAnimations ? null
- : ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle();
- launchActivityWithAnimation(intent, showsWhileLocked, animation, worker, onStarted);
- }
-
- public void launchActivityWithAnimation(final Intent intent,
- boolean showsWhileLocked,
- final Bundle animation,
- final Handler worker,
- final Runnable onStarted) {
-
- LockPatternUtils lockPatternUtils = getLockPatternUtils();
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_SINGLE_TOP
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- boolean isSecure = lockPatternUtils.isSecure();
- if (!isSecure || showsWhileLocked) {
- if (!isSecure) {
- dismissKeyguardOnNextActivity();
- }
- try {
- if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s",
- intent, SystemClock.uptimeMillis()));
- startActivityForCurrentUser(intent, animation, worker, onStarted);
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "Activity not found for intent + " + intent.getAction());
- }
- } else {
- // Create a runnable to start the activity and ask the user to enter their
- // credentials.
- KeyguardSecurityCallback callback = getCallback();
- callback.setOnDismissAction(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- dismissKeyguardOnNextActivity();
- startActivityForCurrentUser(intent, animation, worker, onStarted);
- return true;
- }
- });
- callback.dismiss(false);
- }
- }
-
- private void dismissKeyguardOnNextActivity() {
- try {
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
- } catch (RemoteException e) {
- Log.w(TAG, "can't dismiss keyguard on launch");
- }
- }
-
- private void startActivityForCurrentUser(final Intent intent, final Bundle options,
- Handler worker, final Runnable onStarted) {
- final UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
- if (worker == null || onStarted == null) {
- getContext().startActivityAsUser(intent, options, user);
- return;
- }
- // if worker + onStarted are supplied, run blocking activity launch call in the background
- worker.post(new Runnable(){
- @Override
- public void run() {
- try {
- WaitResult result = ActivityManagerNative.getDefault().startActivityAndWait(
- null /*caller*/,
- null /*caller pkg*/,
- intent,
- intent.resolveTypeIfNeeded(getContext().getContentResolver()),
- null /*resultTo*/,
- null /*resultWho*/,
- 0 /*requestCode*/,
- Intent.FLAG_ACTIVITY_NEW_TASK,
- null /*profileFile*/,
- null /*profileFd*/,
- options,
- user.getIdentifier());
- if (DEBUG) Log.d(TAG, String.format("waitResult[%s,%s,%s,%s] at %s",
- result.result, result.thisTime, result.totalTime, result.who,
- SystemClock.uptimeMillis()));
- } catch (RemoteException e) {
- Log.w(TAG, "Error starting activity", e);
- return;
- }
- try {
- onStarted.run();
- } catch (Throwable t) {
- Log.w(TAG, "Error running onStarted callback", t);
- }
- }});
- }
-
- private Intent getCameraIntent() {
- return getLockPatternUtils().isSecure() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
- }
-
- private boolean wouldLaunchResolverActivity(Intent intent) {
- PackageManager packageManager = getContext().getPackageManager();
- ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
- List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
- intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
- return wouldLaunchResolverActivity(resolved, appList);
- }
-
- private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
- // If the list contains the above resolved activity, then it can't be
- // ResolverActivity itself.
- for (int i = 0; i < appList.size(); i++) {
- ResolveInfo tmp = appList.get(i);
- if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
- && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
deleted file mode 100644
index fe32099..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-
-import android.util.Log;
-
-class KeyguardCircleFramedDrawable extends Drawable {
-
- private final Bitmap mBitmap;
- private final int mSize;
- private final Paint mPaint;
- private final float mShadowRadius;
- private final float mStrokeWidth;
- private final int mFrameColor;
- private final int mHighlightColor;
- private final int mFrameShadowColor;
-
- private float mScale;
- private Path mFramePath;
- private Rect mSrcRect;
- private RectF mDstRect;
- private RectF mFrameRect;
- private boolean mPressed;
-
- public KeyguardCircleFramedDrawable(Bitmap bitmap, int size,
- int frameColor, float strokeWidth,
- int frameShadowColor, float shadowRadius,
- int highlightColor) {
- super();
- mSize = size;
- mShadowRadius = shadowRadius;
- mFrameColor = frameColor;
- mFrameShadowColor = frameShadowColor;
- mStrokeWidth = strokeWidth;
- mHighlightColor = highlightColor;
-
- mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(mBitmap);
-
- final int width = bitmap.getWidth();
- final int height = bitmap.getHeight();
- final int square = Math.min(width, height);
-
- final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square);
- final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
- circleRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f);
- circleRect.inset(mShadowRadius, mShadowRadius);
-
- final Path fillPath = new Path();
- fillPath.addArc(circleRect, 0f, 360f);
-
- canvas.drawColor(0, PorterDuff.Mode.CLEAR);
-
- // opaque circle matte
- mPaint = new Paint();
- mPaint.setAntiAlias(true);
- mPaint.setColor(Color.BLACK);
- mPaint.setStyle(Paint.Style.FILL);
- canvas.drawPath(fillPath, mPaint);
-
- // mask in the icon where the bitmap is opaque
- mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
- canvas.drawBitmap(bitmap, cropRect, circleRect, mPaint);
-
- // prepare paint for frame drawing
- mPaint.setXfermode(null);
-
- mScale = 1f;
-
- mSrcRect = new Rect(0, 0, mSize, mSize);
- mDstRect = new RectF(0, 0, mSize, mSize);
- mFrameRect = new RectF(mDstRect);
- mFramePath = new Path();
- }
-
- public void reset() {
- mScale = 1f;
- mPressed = false;
- }
-
- @Override
- public void draw(Canvas canvas) {
- // clear background
- final float outside = Math.min(canvas.getWidth(), canvas.getHeight());
- final float inside = mScale * outside;
- final float pad = (outside - inside) / 2f;
-
- mDstRect.set(pad, pad, outside - pad, outside - pad);
- canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
-
- mFrameRect.set(mDstRect);
- mFrameRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f);
- mFrameRect.inset(mShadowRadius, mShadowRadius);
-
- mFramePath.reset();
- mFramePath.addArc(mFrameRect, 0f, 360f);
-
- // white frame
- if (mPressed) {
- mPaint.setStyle(Paint.Style.FILL);
- mPaint.setColor(Color.argb((int) (0.33f * 255),
- Color.red(mHighlightColor),
- Color.green(mHighlightColor),
- Color.blue(mHighlightColor)));
- canvas.drawPath(mFramePath, mPaint);
- }
- mPaint.setStrokeWidth(mStrokeWidth);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setColor(mPressed ? mHighlightColor : mFrameColor);
- mPaint.setShadowLayer(mShadowRadius, 0f, 0f, mFrameShadowColor);
- canvas.drawPath(mFramePath, mPaint);
- }
-
- public void setScale(float scale) {
- mScale = scale;
- }
-
- public float getScale() {
- return mScale;
- }
-
- public void setPressed(boolean pressed) {
- mPressed = pressed;
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- @Override
- public void setAlpha(int alpha) {
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- }
-
- public boolean verifyParams(float iconSize, int frameColor, float stroke,
- int frameShadowColor, float shadowRadius, int highlightColor) {
- return mSize == iconSize
- && mFrameColor == frameColor
- && mStrokeWidth == stroke
- && mFrameShadowColor == frameShadowColor
- && mShadowRadius == shadowRadius
- && mHighlightColor == highlightColor;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
deleted file mode 100644
index 7315aad..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.TelephonyManager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.IRotationWatcher;
-import android.view.IWindowManager;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
-
-import java.lang.Math;
-
-public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecurityView {
-
- private static final String TAG = "FULKeyguardFaceUnlockView";
- private static final boolean DEBUG = false;
- private KeyguardSecurityCallback mKeyguardSecurityCallback;
- private LockPatternUtils mLockPatternUtils;
- private BiometricSensorUnlock mBiometricUnlock;
- private View mFaceUnlockAreaView;
- private ImageButton mCancelButton;
- private SecurityMessageDisplay mSecurityMessageDisplay;
- private View mEcaView;
- private Drawable mBouncerFrame;
-
- private boolean mIsShowing = false;
- private final Object mIsShowingLock = new Object();
-
- private int mLastRotation;
- private boolean mWatchingRotation;
- private final IWindowManager mWindowManager =
- IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
-
- private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() {
- public void onRotationChanged(int rotation) {
- if (DEBUG) Log.d(TAG, "onRotationChanged(): " + mLastRotation + "->" + rotation);
-
- // If the difference between the new rotation value and the previous rotation value is
- // equal to 2, the rotation change was 180 degrees. This stops the biometric unlock
- // and starts it in the new position. This is not performed for 90 degree rotations
- // since a 90 degree rotation is a configuration change, which takes care of this for
- // us.
- if (Math.abs(rotation - mLastRotation) == 2) {
- if (mBiometricUnlock != null) {
- mBiometricUnlock.stop();
- maybeStartBiometricUnlock();
- }
- }
- mLastRotation = rotation;
- }
- };
-
- public KeyguardFaceUnlockView(Context context) {
- this(context, null);
- }
-
- public KeyguardFaceUnlockView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- initializeBiometricUnlockView();
-
- mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
- mEcaView = findViewById(R.id.keyguard_selector_fade_container);
- View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame);
- if (bouncerFrameView != null) {
- mBouncerFrame = bouncerFrameView.getBackground();
- }
- }
-
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mKeyguardSecurityCallback = callback;
- // TODO: formalize this in the interface or factor it out
- ((FaceUnlock)mBiometricUnlock).setKeyguardCallback(callback);
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- }
-
- @Override
- public void reset() {
-
- }
-
- @Override
- public void onDetachedFromWindow() {
- if (DEBUG) Log.d(TAG, "onDetachedFromWindow()");
- if (mBiometricUnlock != null) {
- mBiometricUnlock.stop();
- }
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback);
- if (mWatchingRotation) {
- try {
- mWindowManager.removeRotationWatcher(mRotationWatcher);
- mWatchingRotation = false;
- } catch (RemoteException e) {
- Log.e(TAG, "Remote exception when removing rotation watcher");
- }
- }
- }
-
- @Override
- public void onPause() {
- if (DEBUG) Log.d(TAG, "onPause()");
- if (mBiometricUnlock != null) {
- mBiometricUnlock.stop();
- }
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback);
- if (mWatchingRotation) {
- try {
- mWindowManager.removeRotationWatcher(mRotationWatcher);
- mWatchingRotation = false;
- } catch (RemoteException e) {
- Log.e(TAG, "Remote exception when removing rotation watcher");
- }
- }
- }
-
- @Override
- public void onResume(int reason) {
- if (DEBUG) Log.d(TAG, "onResume()");
- mIsShowing = KeyguardUpdateMonitor.getInstance(mContext).isKeyguardVisible();
- if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
- maybeStartBiometricUnlock();
- }
- KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
-
- // Registers a callback which handles stopping the biometric unlock and restarting it in
- // the new position for a 180 degree rotation change.
- if (!mWatchingRotation) {
- try {
- mLastRotation = mWindowManager.watchRotation(mRotationWatcher);
- mWatchingRotation = true;
- } catch (RemoteException e) {
- Log.e(TAG, "Remote exception when adding rotation watcher");
- }
- }
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mKeyguardSecurityCallback;
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- mBiometricUnlock.initializeView(mFaceUnlockAreaView);
- }
-
- private void initializeBiometricUnlockView() {
- if (DEBUG) Log.d(TAG, "initializeBiometricUnlockView()");
- mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view);
- if (mFaceUnlockAreaView != null) {
- mBiometricUnlock = new FaceUnlock(mContext);
-
- mCancelButton = (ImageButton) findViewById(R.id.face_unlock_cancel_button);
- mCancelButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mBiometricUnlock.stopAndShowBackup();
- }
- });
- } else {
- Log.w(TAG, "Couldn't find biometric unlock view");
- }
- }
-
- /**
- * Starts the biometric unlock if it should be started based on a number of factors. If it
- * should not be started, it either goes to the back up, or remains showing to prepare for
- * it being started later.
- */
- private void maybeStartBiometricUnlock() {
- if (DEBUG) Log.d(TAG, "maybeStartBiometricUnlock()");
- if (mBiometricUnlock != null) {
- KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
- final boolean backupIsTimedOut = (
- monitor.getFailedUnlockAttempts() >=
- LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
- PowerManager powerManager = (PowerManager) mContext.getSystemService(
- Context.POWER_SERVICE);
-
- boolean isShowing;
- synchronized(mIsShowingLock) {
- isShowing = mIsShowing;
- }
-
- // Don't start it if the screen is off or if it's not showing, but keep this view up
- // because we want it here and ready for when the screen turns on or when it does start
- // showing.
- if (!powerManager.isScreenOn() || !isShowing) {
- mBiometricUnlock.stop(); // It shouldn't be running but calling this can't hurt.
- return;
- }
-
- // Although these same conditions are handled in KeyguardSecurityModel, they are still
- // necessary here. When a tablet is rotated 90 degrees, a configuration change is
- // triggered and everything is torn down and reconstructed. That means
- // KeyguardSecurityModel gets a chance to take care of the logic and doesn't even
- // reconstruct KeyguardFaceUnlockView if the biometric unlock should be suppressed.
- // However, for a 180 degree rotation, no configuration change is triggered, so only
- // the logic here is capable of suppressing Face Unlock.
- if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
- && monitor.isAlternateUnlockEnabled()
- && !monitor.getMaxBiometricUnlockAttemptsReached()
- && !backupIsTimedOut) {
- mBiometricUnlock.start();
- } else {
- mBiometricUnlock.stopAndShowBackup();
- }
- }
- }
-
- KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
- // We need to stop the biometric unlock when a phone call comes in
- @Override
- public void onPhoneStateChanged(int phoneState) {
- if (DEBUG) Log.d(TAG, "onPhoneStateChanged(" + phoneState + ")");
- if (phoneState == TelephonyManager.CALL_STATE_RINGING) {
- if (mBiometricUnlock != null) {
- mBiometricUnlock.stopAndShowBackup();
- }
- }
- }
-
- @Override
- public void onUserSwitching(int userId) {
- if (DEBUG) Log.d(TAG, "onUserSwitched(" + userId + ")");
- if (mBiometricUnlock != null) {
- mBiometricUnlock.stop();
- }
- // No longer required; static value set by KeyguardViewMediator
- // mLockPatternUtils.setCurrentUser(userId);
- }
-
- @Override
- public void onUserSwitchComplete(int userId) {
- if (DEBUG) Log.d(TAG, "onUserSwitchComplete(" + userId + ")");
- if (mBiometricUnlock != null) {
- maybeStartBiometricUnlock();
- }
- }
-
- @Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
- boolean wasShowing = false;
- synchronized(mIsShowingLock) {
- wasShowing = mIsShowing;
- mIsShowing = showing;
- }
- PowerManager powerManager = (PowerManager) mContext.getSystemService(
- Context.POWER_SERVICE);
- if (mBiometricUnlock != null) {
- if (!showing && wasShowing) {
- mBiometricUnlock.stop();
- } else if (showing && powerManager.isScreenOn() && !wasShowing) {
- maybeStartBiometricUnlock();
- }
- }
- }
- };
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public void showBouncer(int duration) {
- KeyguardSecurityViewHelper.
- showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
- }
-
- @Override
- public void hideBouncer(int duration) {
- KeyguardSecurityViewHelper.
- hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
- }
-
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardGlowStripView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardGlowStripView.java
deleted file mode 100644
index e1c95f0..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardGlowStripView.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.widget.LinearLayout;
-
-import com.android.internal.R;
-
-/**
- * A layout which animates a strip of horizontal, pulsing dots on request. This is used
- * to indicate the presence of pages to the left / right.
- */
-public class KeyguardGlowStripView extends LinearLayout {
- private static final int DURATION = 500;
-
- private static final float SLIDING_WINDOW_SIZE = 0.4f;
- private int mDotStripTop;
- private int mHorizontalDotGap;
-
- private int mDotSize;
- private int mNumDots;
- private Drawable mDotDrawable;
- private boolean mLeftToRight = true;
-
- private float mAnimationProgress = 0f;
- private boolean mDrawDots = false;
- private ValueAnimator mAnimator;
- private Interpolator mDotAlphaInterpolator = new DecelerateInterpolator(0.5f);
-
- public KeyguardGlowStripView(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardGlowStripView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardGlowStripView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyguardGlowStripView);
- mDotSize = a.getDimensionPixelSize(R.styleable.KeyguardGlowStripView_dotSize, mDotSize);
- mNumDots = a.getInt(R.styleable.KeyguardGlowStripView_numDots, mNumDots);
- mDotDrawable = a.getDrawable(R.styleable.KeyguardGlowStripView_glowDot);
- mLeftToRight = a.getBoolean(R.styleable.KeyguardGlowStripView_leftToRight, mLeftToRight);
- }
-
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- int availableWidth = w - getPaddingLeft() - getPaddingRight();
- mHorizontalDotGap = (availableWidth - mDotSize * mNumDots) / (mNumDots - 1);
- mDotStripTop = getPaddingTop();
- invalidate();
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
-
- if (!mDrawDots) return;
-
- int xOffset = getPaddingLeft();
- mDotDrawable.setBounds(0, 0, mDotSize, mDotSize);
-
- for (int i = 0; i < mNumDots; i++) {
- // We fudge the relative position to provide a fade in of the first dot and a fade
- // out of the final dot.
- float relativeDotPosition = SLIDING_WINDOW_SIZE / 2 + ((1.0f * i) / (mNumDots - 1)) *
- (1 - SLIDING_WINDOW_SIZE);
- float distance = Math.abs(relativeDotPosition - mAnimationProgress);
- float alpha = Math.max(0, 1 - distance / (SLIDING_WINDOW_SIZE / 2));
-
- alpha = mDotAlphaInterpolator.getInterpolation(alpha);
-
- canvas.save();
- canvas.translate(xOffset, mDotStripTop);
- mDotDrawable.setAlpha((int) (alpha * 255));
- mDotDrawable.draw(canvas);
- canvas.restore();
- xOffset += mDotSize + mHorizontalDotGap;
- }
- }
-
- public void makeEmGo() {
- if (mAnimator != null) {
- mAnimator.cancel();
- }
- float from = mLeftToRight ? 0f : 1f;
- float to = mLeftToRight ? 1f : 0f;
- mAnimator = ValueAnimator.ofFloat(from, to);
- mAnimator.setDuration(DURATION);
- mAnimator.setInterpolator(new LinearInterpolator());
- mAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mDrawDots = false;
- // make sure we draw one frame at the end with everything gone.
- invalidate();
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- mDrawDots = true;
- }
- });
- mAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mAnimationProgress = (Float) animation.getAnimatedValue();
- invalidate();
- }
- });
- mAnimator.start();
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
deleted file mode 100644
index c3077c7..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ /dev/null
@@ -1,1684 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.AlertDialog;
-import android.app.SearchManager;
-import android.app.admin.DevicePolicyManager;
-import android.appwidget.AppWidgetHost;
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.media.RemoteControlClient;
-import android.os.Looper;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Slog;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.animation.AnimationUtils;
-import android.widget.RemoteViews.OnClickHandler;
-
-import com.android.internal.R;
-import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.internal.policy.impl.keyguard.KeyguardUpdateMonitor.DisplayClientState;
-import com.android.internal.widget.LockPatternUtils;
-
-import java.io.File;
-import java.util.List;
-
-public class KeyguardHostView extends KeyguardViewBase {
- private static final String TAG = "KeyguardHostView";
- // Transport control states.
- static final int TRANSPORT_GONE = 0;
- static final int TRANSPORT_INVISIBLE = 1;
- static final int TRANSPORT_VISIBLE = 2;
-
- private int mTransportState = TRANSPORT_GONE;
-
- // Use this to debug all of keyguard
- public static boolean DEBUG = KeyguardViewMediator.DEBUG;
- public static boolean DEBUGXPORT = true; // debug music transport control
-
- // Found in KeyguardAppWidgetPickActivity.java
- static final int APPWIDGET_HOST_ID = 0x4B455947;
-
- private final int MAX_WIDGETS = 5;
-
- private AppWidgetHost mAppWidgetHost;
- private AppWidgetManager mAppWidgetManager;
- private KeyguardWidgetPager mAppWidgetContainer;
- private KeyguardSecurityViewFlipper mSecurityViewContainer;
- private KeyguardSelectorView mKeyguardSelectorView;
- private KeyguardTransportControlView mTransportControl;
- private boolean mIsVerifyUnlockOnly;
- private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
- private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
- private int mAppWidgetToShow;
-
- private boolean mCheckAppWidgetConsistencyOnBootCompleted = false;
- private boolean mCleanupAppWidgetsOnBootCompleted = false;
-
- protected OnDismissAction mDismissAction;
-
- protected int mFailedAttempts;
- private LockPatternUtils mLockPatternUtils;
-
- private KeyguardSecurityModel mSecurityModel;
- private KeyguardViewStateManager mViewStateManager;
-
- private Rect mTempRect = new Rect();
-
- private int mDisabledFeatures;
-
- private boolean mCameraDisabled;
-
- private boolean mSafeModeEnabled;
-
- private boolean mUserSetupCompleted;
-
- // User for whom this host view was created. Final because we should never change the
- // id without reconstructing an instance of KeyguardHostView. See note below...
- private final int mUserId;
-
- private KeyguardMultiUserSelectorView mKeyguardMultiUserSelectorView;
-
- protected int mClientGeneration;
-
- /*package*/ interface UserSwitcherCallback {
- void hideSecurityView(int duration);
- void showSecurityView();
- void showUnlockHint();
- void userActivity();
- }
-
- /*package*/ interface OnDismissAction {
- /* returns true if the dismiss should be deferred */
- boolean onDismiss();
- }
-
- public KeyguardHostView(Context context) {
- this(context, null);
- }
-
- public KeyguardHostView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- if (DEBUG) Log.e(TAG, "KeyguardHostView()");
-
- mLockPatternUtils = new LockPatternUtils(context);
-
- // Note: This depends on KeyguardHostView getting reconstructed every time the
- // user switches, since mUserId will be used for the entire session.
- // Once created, keyguard should *never* re-use this instance with another user.
- // In other words, mUserId should never change - hence it's marked final.
- mUserId = mLockPatternUtils.getCurrentUser();
-
- DevicePolicyManager dpm =
- (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- if (dpm != null) {
- mDisabledFeatures = getDisabledFeatures(dpm);
- mCameraDisabled = dpm.getCameraDisabled(null);
- }
-
- mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
-
- // These need to be created with the user context...
- Context userContext = null;
- try {
- final String packageName = "system";
- userContext = mContext.createPackageContextAsUser(packageName, 0,
- new UserHandle(mUserId));
-
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- // This should never happen, but it's better to have no widgets than to crash.
- userContext = context;
- }
-
- mAppWidgetHost = new AppWidgetHost(userContext, APPWIDGET_HOST_ID, mOnClickHandler,
- Looper.myLooper());
-
- cleanupAppWidgetIds();
-
- mAppWidgetManager = AppWidgetManager.getInstance(userContext);
-
- mSecurityModel = new KeyguardSecurityModel(context);
-
- mViewStateManager = new KeyguardViewStateManager(this);
-
- mUserSetupCompleted = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
-
- // Ensure we have the current state *before* we call showAppropriateWidgetPage()
- getInitialTransportState();
-
- if (mSafeModeEnabled) {
- Log.v(TAG, "Keyguard widgets disabled by safe mode");
- }
- if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
- Log.v(TAG, "Keyguard widgets disabled by DPM");
- }
- if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) {
- Log.v(TAG, "Keyguard secure camera disabled by DPM");
- }
- }
-
- private void getInitialTransportState() {
- DisplayClientState dcs = KeyguardUpdateMonitor.getInstance(mContext)
- .getCachedDisplayClientState();
- mTransportState = (dcs.clearing ? TRANSPORT_GONE :
- (isMusicPlaying(dcs.playbackState) ? TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
-
- if (DEBUG) Log.v(TAG, "Initial transport state: "
- + mTransportState + ", pbstate=" + dcs.playbackState);
- }
-
- private void cleanupAppWidgetIds() {
- // Since this method may delete a widget (which we can't do until boot completed) we
- // may have to defer it until after boot complete.
- if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
- mCleanupAppWidgetsOnBootCompleted = true;
- return;
- }
- if (!mSafeModeEnabled && !widgetsDisabledByDpm()) {
- // Clean up appWidgetIds that are bound to lockscreen, but not actually used
- // This is only to clean up after another bug: we used to not call
- // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code
- // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks
- // that are triggered by deleteAppWidgetId, which is why we're doing this
- int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets();
- int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds();
- for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) {
- int appWidgetId = appWidgetIdsBoundToHost[i];
- if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) {
- Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id "
- + appWidgetId);
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
- }
- }
- }
- }
-
- private static boolean contains(int[] array, int target) {
- for (int value : array) {
- if (value == target) {
- return true;
- }
- }
- return false;
- }
-
- private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onBootCompleted() {
- if (mCheckAppWidgetConsistencyOnBootCompleted) {
- checkAppWidgetConsistency();
- mSwitchPageRunnable.run();
- mCheckAppWidgetConsistencyOnBootCompleted = false;
- }
- if (mCleanupAppWidgetsOnBootCompleted) {
- cleanupAppWidgetIds();
- mCleanupAppWidgetsOnBootCompleted = false;
- }
- }
- @Override
- public void onUserSwitchComplete(int userId) {
- if (mKeyguardMultiUserSelectorView != null) {
- mKeyguardMultiUserSelectorView.finalizeActiveUserView(true);
- }
- }
- @Override
- void onMusicClientIdChanged(
- int clientGeneration, boolean clearing, android.app.PendingIntent intent) {
- // Set transport state to invisible until we know music is playing (below)
- if (DEBUGXPORT && (mClientGeneration != clientGeneration || clearing)) {
- Log.v(TAG, (clearing ? "hide" : "show") + " transport, gen:" + clientGeneration);
- }
- mClientGeneration = clientGeneration;
- final int newState = (clearing ? TRANSPORT_GONE
- : (mTransportState == TRANSPORT_VISIBLE ?
- TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
- if (newState != mTransportState) {
- mTransportState = newState;
- if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
- KeyguardHostView.this.post(mSwitchPageRunnable);
- }
- }
- @Override
- public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
- if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
- if (mTransportState != TRANSPORT_GONE) {
- final int newState = (isMusicPlaying(playbackState) ?
- TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
- if (newState != mTransportState) {
- mTransportState = newState;
- if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
- KeyguardHostView.this.post(mSwitchPageRunnable);
- }
- }
- }
- };
-
- private static final boolean isMusicPlaying(int playbackState) {
- // This should agree with the list in AudioService.isPlaystateActive()
- switch (playbackState) {
- case RemoteControlClient.PLAYSTATE_PLAYING:
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- case RemoteControlClient.PLAYSTATE_REWINDING:
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- return true;
- default:
- return false;
- }
- }
-
- private SlidingChallengeLayout mSlidingChallengeLayout;
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- boolean result = super.onTouchEvent(ev);
- mTempRect.set(0, 0, 0, 0);
- offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
- ev.offsetLocation(mTempRect.left, mTempRect.top);
- result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
- ev.offsetLocation(-mTempRect.left, -mTempRect.top);
- return result;
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.keyguardDoneDrawing();
- }
- }
-
- private int getWidgetPosition(int id) {
- final KeyguardWidgetPager appWidgetContainer = mAppWidgetContainer;
- final int children = appWidgetContainer.getChildCount();
- for (int i = 0; i < children; i++) {
- final View content = appWidgetContainer.getWidgetPageAt(i).getContent();
- if (content != null && content.getId() == id) {
- return i;
- } else if (content == null) {
- // Attempt to track down bug #8886916
- Log.w(TAG, "*** Null content at " + "i=" + i + ",id=" + id + ",N=" + children);
- }
- }
- return -1;
- }
-
- @Override
- protected void onFinishInflate() {
- // Grab instances of and make any necessary changes to the main layouts. Create
- // view state manager and wire up necessary listeners / callbacks.
- View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target);
- mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
- mAppWidgetContainer.setVisibility(VISIBLE);
- mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
- mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget);
- mAppWidgetContainer.setMinScale(0.5f);
-
- mSlidingChallengeLayout = (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
- if (mSlidingChallengeLayout != null) {
- mSlidingChallengeLayout.setOnChallengeScrolledListener(mViewStateManager);
- }
- mAppWidgetContainer.setViewStateManager(mViewStateManager);
- mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
-
- ChallengeLayout challenge = mSlidingChallengeLayout != null ? mSlidingChallengeLayout :
- (ChallengeLayout) findViewById(R.id.multi_pane_challenge);
- challenge.setOnBouncerStateChangedListener(mViewStateManager);
- mAppWidgetContainer.setBouncerAnimationDuration(challenge.getBouncerAnimationDuration());
- mViewStateManager.setPagedView(mAppWidgetContainer);
- mViewStateManager.setChallengeLayout(challenge);
- mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
- mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
- mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
-
- setBackButtonEnabled(false);
-
- addDefaultWidgets();
-
- addWidgetsFromSettings();
- if (!shouldEnableAddWidget()) {
- mAppWidgetContainer.setAddWidgetEnabled(false);
- }
- checkAppWidgetConsistency();
- mSwitchPageRunnable.run();
- // This needs to be called after the pages are all added.
- mViewStateManager.showUsabilityHints();
-
- showPrimarySecurityScreen(false);
- updateSecurityViews();
- }
-
- private void setBackButtonEnabled(boolean enabled) {
- if (mContext instanceof Activity) return; // always enabled in activity mode
- setSystemUiVisibility(enabled ?
- getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_BACK :
- getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
- }
-
- private boolean shouldEnableAddWidget() {
- return numWidgets() < MAX_WIDGETS && mUserSetupCompleted;
- }
-
- private int getDisabledFeatures(DevicePolicyManager dpm) {
- int disabledFeatures = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
- if (dpm != null) {
- final int currentUser = mLockPatternUtils.getCurrentUser();
- disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
- }
- return disabledFeatures;
- }
-
- private boolean widgetsDisabledByDpm() {
- return (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
- }
-
- private boolean cameraDisabledByDpm() {
- return mCameraDisabled
- || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
- }
-
- private void updateSecurityViews() {
- int children = mSecurityViewContainer.getChildCount();
- for (int i = 0; i < children; i++) {
- updateSecurityView(mSecurityViewContainer.getChildAt(i));
- }
- }
-
- private void updateSecurityView(View view) {
- if (view instanceof KeyguardSecurityView) {
- KeyguardSecurityView ksv = (KeyguardSecurityView) view;
- ksv.setKeyguardCallback(mCallback);
- ksv.setLockPatternUtils(mLockPatternUtils);
- if (mViewStateManager.isBouncing()) {
- ksv.showBouncer(0);
- } else {
- ksv.hideBouncer(0);
- }
- } else {
- Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
- }
- }
-
- void setLockPatternUtils(LockPatternUtils utils) {
- mSecurityModel.setLockPatternUtils(utils);
- mLockPatternUtils = utils;
- updateSecurityViews();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mAppWidgetHost.startListening();
- KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mAppWidgetHost.stopListening();
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
- }
-
- void addWidget(AppWidgetHostView view, int pageIndex) {
- mAppWidgetContainer.addWidget(view, pageIndex);
- }
-
- private KeyguardWidgetPager.Callbacks mWidgetCallbacks
- = new KeyguardWidgetPager.Callbacks() {
- @Override
- public void userActivity() {
- KeyguardHostView.this.userActivity();
- }
-
- @Override
- public void onUserActivityTimeoutChanged() {
- KeyguardHostView.this.onUserActivityTimeoutChanged();
- }
-
- @Override
- public void onAddView(View v) {
- if (!shouldEnableAddWidget()) {
- mAppWidgetContainer.setAddWidgetEnabled(false);
- }
- }
-
- @Override
- public void onRemoveView(View v, boolean deletePermanently) {
- if (deletePermanently) {
- final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
- if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID &&
- appWidgetId != LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
- }
- }
- }
-
- @Override
- public void onRemoveViewAnimationCompleted() {
- if (shouldEnableAddWidget()) {
- mAppWidgetContainer.setAddWidgetEnabled(true);
- }
- }
- };
-
- public void initializeSwitchingUserState(boolean switching) {
- if (!switching && mKeyguardMultiUserSelectorView != null) {
- mKeyguardMultiUserSelectorView.finalizeActiveUserView(false);
- }
- }
-
- public void userActivity() {
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.userActivity();
- }
- }
-
- public void onUserActivityTimeoutChanged() {
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.onUserActivityTimeoutChanged();
- }
- }
-
- @Override
- public long getUserActivityTimeout() {
- // Currently only considering user activity timeouts needed by widgets.
- // Could also take into account longer timeouts for certain security views.
- if (mAppWidgetContainer != null) {
- return mAppWidgetContainer.getUserActivityTimeout();
- }
- return -1;
- }
-
- private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
-
- public void userActivity(long timeout) {
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.userActivity(timeout);
- }
- }
-
- public void dismiss(boolean authenticated) {
- showNextSecurityScreenOrFinish(authenticated);
- }
-
- public boolean isVerifyUnlockOnly() {
- return mIsVerifyUnlockOnly;
- }
-
- public void reportSuccessfulUnlockAttempt() {
- KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
- mLockPatternUtils.reportSuccessfulPasswordAttempt();
- }
-
- public void reportFailedUnlockAttempt() {
- if (mCurrentSecuritySelection == SecurityMode.Biometric) {
- KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt();
- } else {
- KeyguardHostView.this.reportFailedUnlockAttempt();
- }
- }
-
- public int getFailedAttempts() {
- return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
- }
-
- @Override
- public void showBackupSecurity() {
- KeyguardHostView.this.showBackupSecurityScreen();
- }
-
- @Override
- public void setOnDismissAction(OnDismissAction action) {
- KeyguardHostView.this.setOnDismissAction(action);
- }
-
- };
-
- private void showDialog(String title, String message) {
- final AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(title)
- .setMessage(message)
- .setNeutralButton(com.android.internal.R.string.ok, null)
- .create();
- if (!(mContext instanceof Activity)) {
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- dialog.show();
- }
-
- private void showTimeoutDialog() {
- int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
- int messageId = 0;
-
- switch (mSecurityModel.getSecurityMode()) {
- case Pattern:
- messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
- break;
- case PIN:
- messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
- break;
- case Password:
- messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
- break;
- }
-
- if (messageId != 0) {
- final String message = mContext.getString(messageId,
- KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
- timeoutInSeconds);
- showDialog(null, message);
- }
- }
-
- private void showAlmostAtWipeDialog(int attempts, int remaining) {
- int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
- String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
- attempts, remaining);
- showDialog(null, message);
- }
-
- private void showWipeDialog(int attempts) {
- String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
- showDialog(null, message);
- }
-
- private void showAlmostAtAccountLoginDialog() {
- final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
- final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
- - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
- String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
- count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
- showDialog(null, message);
- }
-
- private void reportFailedUnlockAttempt() {
- final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
- final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
-
- if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
-
- SecurityMode mode = mSecurityModel.getSecurityMode();
- final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
-
- final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
- .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
-
- final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
- - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
-
- final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
- (failedAttemptsBeforeWipe - failedAttempts)
- : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
-
- boolean showTimeout = false;
- if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
- // If we reach this code, it means the user has installed a DevicePolicyManager
- // that requests device wipe after N attempts. Once we get below the grace
- // period, we'll post this dialog every time as a clear warning until the
- // bombshell hits and the device is wiped.
- if (remainingBeforeWipe > 0) {
- showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
- } else {
- // Too many attempts. The device will be wiped shortly.
- Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
- showWipeDialog(failedAttempts);
- }
- } else {
- showTimeout =
- (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
- if (usingPattern && mEnableFallback) {
- if (failedAttempts == failedAttemptWarning) {
- showAlmostAtAccountLoginDialog();
- showTimeout = false; // don't show both dialogs
- } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
- mLockPatternUtils.setPermanentlyLocked(true);
- showSecurityScreen(SecurityMode.Account);
- // don't show timeout dialog because we show account unlock screen next
- showTimeout = false;
- }
- }
- }
- monitor.reportFailedUnlockAttempt();
- mLockPatternUtils.reportFailedPasswordAttempt();
- if (showTimeout) {
- showTimeoutDialog();
- }
- }
-
- /**
- * Shows the primary security screen for the user. This will be either the multi-selector
- * or the user's security method.
- * @param turningOff true if the device is being turned off
- */
- void showPrimarySecurityScreen(boolean turningOff) {
- SecurityMode securityMode = mSecurityModel.getSecurityMode();
- if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
- if (!turningOff &&
- KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
- // If we're not turning off, then allow biometric alternate.
- // We'll reload it when the device comes back on.
- securityMode = mSecurityModel.getAlternateFor(securityMode);
- }
- showSecurityScreen(securityMode);
- }
-
- /**
- * Shows the backup security screen for the current security mode. This could be used for
- * password recovery screens but is currently only used for pattern unlock to show the
- * account unlock screen and biometric unlock to show the user's normal unlock.
- */
- private void showBackupSecurityScreen() {
- if (DEBUG) Log.d(TAG, "showBackupSecurity()");
- SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
- showSecurityScreen(backup);
- }
-
- public boolean showNextSecurityScreenIfPresent() {
- SecurityMode securityMode = mSecurityModel.getSecurityMode();
- // Allow an alternate, such as biometric unlock
- securityMode = mSecurityModel.getAlternateFor(securityMode);
- if (SecurityMode.None == securityMode) {
- return false;
- } else {
- showSecurityScreen(securityMode); // switch to the alternate security view
- return true;
- }
- }
-
- private void showNextSecurityScreenOrFinish(boolean authenticated) {
- if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
- boolean finish = false;
- if (SecurityMode.None == mCurrentSecuritySelection) {
- SecurityMode securityMode = mSecurityModel.getSecurityMode();
- // Allow an alternate, such as biometric unlock
- securityMode = mSecurityModel.getAlternateFor(securityMode);
- if (SecurityMode.None == securityMode) {
- finish = true; // no security required
- } else {
- showSecurityScreen(securityMode); // switch to the alternate security view
- }
- } else if (authenticated) {
- switch (mCurrentSecuritySelection) {
- case Pattern:
- case Password:
- case PIN:
- case Account:
- case Biometric:
- finish = true;
- break;
-
- case SimPin:
- case SimPuk:
- // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
- SecurityMode securityMode = mSecurityModel.getSecurityMode();
- if (securityMode != SecurityMode.None) {
- showSecurityScreen(securityMode);
- } else {
- finish = true;
- }
- break;
-
- default:
- Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
- showPrimarySecurityScreen(false);
- break;
- }
- } else {
- showPrimarySecurityScreen(false);
- }
- if (finish) {
- // If the alternate unlock was suppressed, it can now be safely
- // enabled because the user has left keyguard.
- KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
-
- // If there's a pending runnable because the user interacted with a widget
- // and we're leaving keyguard, then run it.
- boolean deferKeyguardDone = false;
- if (mDismissAction != null) {
- deferKeyguardDone = mDismissAction.onDismiss();
- mDismissAction = null;
- }
- if (mViewMediatorCallback != null) {
- if (deferKeyguardDone) {
- mViewMediatorCallback.keyguardDonePending();
- } else {
- mViewMediatorCallback.keyguardDone(true);
- }
- }
- } else {
- mViewStateManager.showBouncer(true);
- }
- }
-
- private OnClickHandler mOnClickHandler = new OnClickHandler() {
- @Override
- public boolean onClickHandler(final View view,
- final android.app.PendingIntent pendingIntent,
- final Intent fillInIntent) {
- if (pendingIntent.isActivity()) {
- setOnDismissAction(new OnDismissAction() {
- public boolean onDismiss() {
- try {
- // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
- Context context = view.getContext();
- ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
- 0, 0,
- view.getMeasuredWidth(), view.getMeasuredHeight());
- context.startIntentSender(
- pendingIntent.getIntentSender(), fillInIntent,
- Intent.FLAG_ACTIVITY_NEW_TASK,
- Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
- } catch (IntentSender.SendIntentException e) {
- android.util.Log.e(TAG, "Cannot send pending intent: ", e);
- } catch (Exception e) {
- android.util.Log.e(TAG, "Cannot send pending intent due to " +
- "unknown exception: ", e);
- }
- return false;
- }
- });
-
- if (mViewStateManager.isChallengeShowing()) {
- mViewStateManager.showBouncer(true);
- } else {
- mCallback.dismiss(false);
- }
- return true;
- } else {
- return super.onClickHandler(view, pendingIntent, fillInIntent);
- }
- };
- };
-
- // Used to ignore callbacks from methods that are no longer current (e.g. face unlock).
- // This avoids unwanted asynchronous events from messing with the state.
- private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
-
- @Override
- public void userActivity(long timeout) {
- }
-
- @Override
- public void showBackupSecurity() {
- }
-
- @Override
- public void setOnDismissAction(OnDismissAction action) {
- }
-
- @Override
- public void reportSuccessfulUnlockAttempt() {
- }
-
- @Override
- public void reportFailedUnlockAttempt() {
- }
-
- @Override
- public boolean isVerifyUnlockOnly() {
- return false;
- }
-
- @Override
- public int getFailedAttempts() {
- return 0;
- }
-
- @Override
- public void dismiss(boolean securityVerified) {
- }
- };
-
- protected boolean mShowSecurityWhenReturn;
-
- @Override
- public void reset() {
- mIsVerifyUnlockOnly = false;
- mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
- }
-
- /**
- * Sets an action to perform when keyguard is dismissed.
- * @param action
- */
- protected void setOnDismissAction(OnDismissAction action) {
- mDismissAction = action;
- }
-
- private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
- final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
- KeyguardSecurityView view = null;
- final int children = mSecurityViewContainer.getChildCount();
- for (int child = 0; child < children; child++) {
- if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
- view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
- break;
- }
- }
- int layoutId = getLayoutIdFor(securityMode);
- if (view == null && layoutId != 0) {
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
- View v = inflater.inflate(layoutId, mSecurityViewContainer, false);
- mSecurityViewContainer.addView(v);
- updateSecurityView(v);
- view = (KeyguardSecurityView)v;
- }
-
- if (view instanceof KeyguardSelectorView) {
- KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
- View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
- selectorView.setCarrierArea(carrierText);
- }
-
- return view;
- }
-
- /**
- * Switches to the given security view unless it's already being shown, in which case
- * this is a no-op.
- *
- * @param securityMode
- */
- private void showSecurityScreen(SecurityMode securityMode) {
- if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
-
- if (securityMode == mCurrentSecuritySelection) return;
-
- KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
- KeyguardSecurityView newView = getSecurityView(securityMode);
-
- // Enter full screen mode if we're in SIM or Account screen
- boolean fullScreenEnabled = getResources().getBoolean(
- com.android.internal.R.bool.kg_sim_puk_account_full_screen);
- boolean isSimOrAccount = securityMode == SecurityMode.SimPin
- || securityMode == SecurityMode.SimPuk
- || securityMode == SecurityMode.Account;
- mAppWidgetContainer.setVisibility(
- isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
-
- if (mSlidingChallengeLayout != null) {
- mSlidingChallengeLayout.setChallengeInteractive(!fullScreenEnabled);
- }
-
- // Emulate Activity life cycle
- if (oldView != null) {
- oldView.onPause();
- oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
- }
- newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
- newView.setKeyguardCallback(mCallback);
-
- final boolean needsInput = newView.needsInput();
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.setNeedsInput(needsInput);
- }
-
- // Find and show this child.
- final int childCount = mSecurityViewContainer.getChildCount();
-
- mSecurityViewContainer.setInAnimation(
- AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_in));
- mSecurityViewContainer.setOutAnimation(
- AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_out));
- final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
- for (int i = 0; i < childCount; i++) {
- if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
- mSecurityViewContainer.setDisplayedChild(i);
- break;
- }
- }
-
- if (securityMode == SecurityMode.None) {
- // Discard current runnable if we're switching back to the selector view
- setOnDismissAction(null);
- }
- if (securityMode == SecurityMode.Account && !mLockPatternUtils.isPermanentlyLocked()) {
- // we're showing account as a backup, provide a way to get back to primary
- setBackButtonEnabled(true);
- }
- mCurrentSecuritySelection = securityMode;
- }
-
- @Override
- public void onScreenTurnedOn() {
- if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
- showPrimarySecurityScreen(false);
- getSecurityView(mCurrentSecuritySelection).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();
-
- if (mViewStateManager != null) {
- mViewStateManager.showUsabilityHints();
- }
- requestFocus();
- }
-
- @Override
- public void onScreenTurnedOff() {
- if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
- Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
- // Once the screen turns off, we no longer consider this to be first boot and we want the
- // biometric unlock to start next time keyguard is shown.
- KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
- // We use mAppWidgetToShow to show a particular widget after you add it-- once the screen
- // turns off we reset that behavior
- clearAppWidgetToShow();
- checkAppWidgetConsistency();
- showPrimarySecurityScreen(true);
- getSecurityView(mCurrentSecuritySelection).onPause();
- CameraWidgetFrame cameraPage = findCameraPage();
- if (cameraPage != null) {
- cameraPage.onScreenTurnedOff();
- }
- clearFocus();
- }
-
- public void clearAppWidgetToShow() {
- mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
- }
-
- @Override
- public void show() {
- if (DEBUG) Log.d(TAG, "show()");
- showPrimarySecurityScreen(false);
- }
-
- private boolean isSecure() {
- SecurityMode mode = mSecurityModel.getSecurityMode();
- switch (mode) {
- case Pattern:
- return mLockPatternUtils.isLockPatternEnabled();
- case Password:
- case PIN:
- return mLockPatternUtils.isLockPasswordEnabled();
- case SimPin:
- case SimPuk:
- case Account:
- return true;
- case None:
- return false;
- default:
- throw new IllegalStateException("Unknown security mode " + mode);
- }
- }
-
- @Override
- public void wakeWhenReadyTq(int keyCode) {
- if (DEBUG) Log.d(TAG, "onWakeKey");
- if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
- if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
- showSecurityScreen(SecurityMode.None);
- } else {
- if (DEBUG) Log.d(TAG, "poking wake lock immediately");
- }
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.wakeUp();
- }
- }
-
- @Override
- public void verifyUnlock() {
- SecurityMode securityMode = mSecurityModel.getSecurityMode();
- if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.keyguardDone(true);
- }
- } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
- && securityMode != KeyguardSecurityModel.SecurityMode.PIN
- && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
- // can only verify unlock when in pattern/password mode
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.keyguardDone(false);
- }
- } else {
- // otherwise, go to the unlock screen, see if they can verify it
- mIsVerifyUnlockOnly = true;
- showSecurityScreen(securityMode);
- }
- }
-
- private int getSecurityViewIdForMode(SecurityMode securityMode) {
- switch (securityMode) {
- case None: return R.id.keyguard_selector_view;
- case Pattern: return R.id.keyguard_pattern_view;
- case PIN: return R.id.keyguard_pin_view;
- case Password: return R.id.keyguard_password_view;
- case Biometric: return R.id.keyguard_face_unlock_view;
- case Account: return R.id.keyguard_account_view;
- case SimPin: return R.id.keyguard_sim_pin_view;
- case SimPuk: return R.id.keyguard_sim_puk_view;
- }
- return 0;
- }
-
- private int getLayoutIdFor(SecurityMode securityMode) {
- switch (securityMode) {
- case None: return R.layout.keyguard_selector_view;
- case Pattern: return R.layout.keyguard_pattern_view;
- case PIN: return R.layout.keyguard_pin_view;
- case Password: return R.layout.keyguard_password_view;
- case Biometric: return R.layout.keyguard_face_unlock_view;
- case Account: return R.layout.keyguard_account_view;
- case SimPin: return R.layout.keyguard_sim_pin_view;
- case SimPuk: return R.layout.keyguard_sim_puk_view;
- default:
- return 0;
- }
- }
-
- private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) {
- AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId);
- if (appWidgetInfo != null) {
- AppWidgetHostView view = mAppWidgetHost.createView(mContext, appId, appWidgetInfo);
- addWidget(view, pageIndex);
- return true;
- } else {
- if (updateDbIfFailed) {
- Log.w(TAG, "*** AppWidgetInfo for app widget id " + appId + " was null for user"
- + mUserId + ", deleting");
- mAppWidgetHost.deleteAppWidgetId(appId);
- mLockPatternUtils.removeAppWidget(appId);
- }
- return false;
- }
- }
-
- private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
- new CameraWidgetFrame.Callbacks() {
- @Override
- public void onLaunchingCamera() {
- setSliderHandleAlpha(0);
- }
-
- @Override
- public void onCameraLaunchedSuccessfully() {
- if (mAppWidgetContainer.isCameraPage(mAppWidgetContainer.getCurrentPage())) {
- mAppWidgetContainer.scrollLeft();
- }
- setSliderHandleAlpha(1);
- mShowSecurityWhenReturn = true;
- }
-
- @Override
- public void onCameraLaunchedUnsuccessfully() {
- setSliderHandleAlpha(1);
- }
-
- private void setSliderHandleAlpha(float alpha) {
- SlidingChallengeLayout slider =
- (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
- if (slider != null) {
- slider.setHandleAlpha(alpha);
- }
- }
- };
-
- private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
- @Override
- Context getContext() {
- return mContext;
- }
-
- @Override
- KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- LockPatternUtils getLockPatternUtils() {
- return mLockPatternUtils;
- }
- };
-
- private int numWidgets() {
- final int childCount = mAppWidgetContainer.getChildCount();
- int widgetCount = 0;
- for (int i = 0; i < childCount; i++) {
- if (mAppWidgetContainer.isWidgetPage(i)) {
- widgetCount++;
- }
- }
- return widgetCount;
- }
-
- private void addDefaultWidgets() {
- if (!mSafeModeEnabled && !widgetsDisabledByDpm()) {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false);
- mAppWidgetContainer.addWidget(addWidget, 0);
- View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
- addWidgetButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // Pass in an invalid widget id... the picker will allocate an ID for us
- mActivityLauncher.launchWidgetPicker(AppWidgetManager.INVALID_APPWIDGET_ID);
- }
- });
- }
-
- // We currently disable cameras in safe mode because we support loading 3rd party
- // cameras we can't trust. TODO: plumb safe mode into camera creation code and only
- // inflate system-provided camera?
- if (!mSafeModeEnabled && !cameraDisabledByDpm() && mUserSetupCompleted
- && mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
- View cameraWidget =
- CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
- if (cameraWidget != null) {
- mAppWidgetContainer.addWidget(cameraWidget);
- }
- }
-
- enableUserSelectorIfNecessary();
- }
-
- /**
- * Create KeyguardTransportControlView on demand.
- * @return
- */
- private KeyguardTransportControlView getOrCreateTransportControl() {
- if (mTransportControl == null) {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- mTransportControl = (KeyguardTransportControlView)
- inflater.inflate(R.layout.keyguard_transport_control_view, this, false);
- }
- return mTransportControl;
- }
-
- private int getInsertPageIndex() {
- View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
- int insertionIndex = mAppWidgetContainer.indexOfChild(addWidget);
- if (insertionIndex < 0) {
- insertionIndex = 0; // no add widget page found
- } else {
- insertionIndex++; // place after add widget
- }
- return insertionIndex;
- }
-
- private void addDefaultStatusWidget(int index) {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
- mAppWidgetContainer.addWidget(statusWidget, index);
- }
-
- private void addWidgetsFromSettings() {
- if (mSafeModeEnabled || widgetsDisabledByDpm()) {
- return;
- }
-
- int insertionIndex = getInsertPageIndex();
-
- // Add user-selected widget
- final int[] widgets = mLockPatternUtils.getAppWidgets();
-
- if (widgets == null) {
- Log.d(TAG, "Problem reading widgets");
- } else {
- for (int i = widgets.length -1; i >= 0; i--) {
- if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
- addDefaultStatusWidget(insertionIndex);
- } else {
- // We add the widgets from left to right, starting after the first page after
- // the add page. We count down, since the order will be persisted from right
- // to left, starting after camera.
- addWidget(widgets[i], insertionIndex, true);
- }
- }
- }
- }
-
- private int allocateIdForDefaultAppWidget() {
- int appWidgetId;
- Resources res = getContext().getResources();
- ComponentName defaultAppWidget = new ComponentName(
- res.getString(R.string.widget_default_package_name),
- res.getString(R.string.widget_default_class_name));
-
- // Note: we don't support configuring the widget
- appWidgetId = mAppWidgetHost.allocateAppWidgetId();
-
- try {
- mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
-
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
- appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
- }
- return appWidgetId;
- }
- public void checkAppWidgetConsistency() {
- // Since this method may bind a widget (which we can't do until boot completed) we
- // may have to defer it until after boot complete.
- if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
- mCheckAppWidgetConsistencyOnBootCompleted = true;
- return;
- }
- final int childCount = mAppWidgetContainer.getChildCount();
- boolean widgetPageExists = false;
- for (int i = 0; i < childCount; i++) {
- if (mAppWidgetContainer.isWidgetPage(i)) {
- widgetPageExists = true;
- break;
- }
- }
- if (!widgetPageExists) {
- final int insertPageIndex = getInsertPageIndex();
-
- final boolean userAddedWidgetsEnabled = !widgetsDisabledByDpm();
- boolean addedDefaultAppWidget = false;
-
- if (!mSafeModeEnabled) {
- if (userAddedWidgetsEnabled) {
- int appWidgetId = allocateIdForDefaultAppWidget();
- if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
- addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, true);
- }
- } else {
- // note: even if widgetsDisabledByDpm() returns true, we still bind/create
- // the default appwidget if possible
- int appWidgetId = mLockPatternUtils.getFallbackAppWidgetId();
- if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
- appWidgetId = allocateIdForDefaultAppWidget();
- if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
- mLockPatternUtils.writeFallbackAppWidgetId(appWidgetId);
- }
- }
- if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
- addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, false);
- if (!addedDefaultAppWidget) {
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
- mLockPatternUtils.writeFallbackAppWidgetId(
- AppWidgetManager.INVALID_APPWIDGET_ID);
- }
- }
- }
- }
-
- // Use the built-in status/clock view if we can't inflate the default widget
- if (!addedDefaultAppWidget) {
- addDefaultStatusWidget(insertPageIndex);
- }
-
- // trigger DB updates only if user-added widgets are enabled
- if (!mSafeModeEnabled && userAddedWidgetsEnabled) {
- mAppWidgetContainer.onAddView(
- mAppWidgetContainer.getChildAt(insertPageIndex), insertPageIndex);
- }
- }
- }
-
- Runnable mSwitchPageRunnable = new Runnable() {
- @Override
- public void run() {
- showAppropriateWidgetPage();
- }
- };
-
- static class SavedState extends BaseSavedState {
- int transportState;
- int appWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
-
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- private SavedState(Parcel in) {
- super(in);
- this.transportState = in.readInt();
- this.appWidgetToShow = in.readInt();
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(this.transportState);
- out.writeInt(this.appWidgetToShow);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- if (DEBUG) Log.d(TAG, "onSaveInstanceState, tstate=" + mTransportState);
- Parcelable superState = super.onSaveInstanceState();
- SavedState ss = new SavedState(superState);
- // If the transport is showing, force it to show it on restore.
- final boolean showing = mTransportControl != null
- && mAppWidgetContainer.getWidgetPageIndex(mTransportControl) >= 0;
- ss.transportState = showing ? TRANSPORT_VISIBLE : mTransportState;
- ss.appWidgetToShow = mAppWidgetToShow;
- return ss;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- if (!(state instanceof SavedState)) {
- super.onRestoreInstanceState(state);
- return;
- }
- SavedState ss = (SavedState) state;
- super.onRestoreInstanceState(ss.getSuperState());
- mTransportState = (ss.transportState);
- mAppWidgetToShow = ss.appWidgetToShow;
- if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
- post(mSwitchPageRunnable);
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
- if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
- if (hasWindowFocus && mShowSecurityWhenReturn) {
- SlidingChallengeLayout slider =
- (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
- if (slider != null) {
- slider.setHandleAlpha(1);
- slider.showChallenge(true);
- }
- mShowSecurityWhenReturn = false;
- }
- }
-
- private void showAppropriateWidgetPage() {
- int state = mTransportState;
- ensureTransportPresentOrRemoved(state);
- int pageToShow = getAppropriateWidgetPage(state);
- mAppWidgetContainer.setCurrentPage(pageToShow);
- }
-
- /**
- * Examines the current state and adds the transport to the widget pager when the state changes.
- *
- * Showing the initial transport and keeping it around is a bit tricky because the signals
- * coming from music players aren't always clear. Here's how the states are handled:
- *
- * {@link TRANSPORT_GONE} means we have no reason to show the transport - remove it if present.
- *
- * {@link TRANSPORT_INVISIBLE} means we have potential to show the transport because a music
- * player is registered but not currently playing music (or we don't know the state yet). The
- * code adds it conditionally on play state.
- *
- * {@link #TRANSPORT_VISIBLE} means a music player is active and transport should be showing.
- *
- * Once the transport is showing, we always show it until keyguard is dismissed. This state is
- * maintained by onSave/RestoreInstanceState(). This state is cleared in
- * {@link KeyguardViewManager#hide} when keyguard is dismissed, which causes the transport to be
- * gone when keyguard is restarted until we get an update with the current state.
- *
- * @param state
- */
- private void ensureTransportPresentOrRemoved(int state) {
- final boolean showing = getWidgetPosition(R.id.keyguard_transport_control) != -1;
- final boolean visible = state == TRANSPORT_VISIBLE;
- final boolean shouldBeVisible = state == TRANSPORT_INVISIBLE && isMusicPlaying(state);
- if (!showing && (visible || shouldBeVisible)) {
- if (DEBUGXPORT) Log.v(TAG, "add transport");
- // insert to left of camera if it exists, otherwise after right-most widget
- int lastWidget = mAppWidgetContainer.getChildCount() - 1;
- int position = 0; // handle no widget case
- if (lastWidget >= 0) {
- position = mAppWidgetContainer.isCameraPage(lastWidget) ?
- lastWidget : lastWidget + 1;
- }
- mAppWidgetContainer.addWidget(getOrCreateTransportControl(), position);
- } else if (showing && state == TRANSPORT_GONE) {
- if (DEBUGXPORT) Log.v(TAG, "remove transport");
- mAppWidgetContainer.removeWidget(getOrCreateTransportControl());
- mTransportControl = null;
- }
- }
-
- private CameraWidgetFrame findCameraPage() {
- for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) {
- if (mAppWidgetContainer.isCameraPage(i)) {
- return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i);
- }
- }
- return null;
- }
-
- boolean isMusicPage(int pageIndex) {
- return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
- }
-
- private int getAppropriateWidgetPage(int musicTransportState) {
- // assumes at least one widget (besides camera + add)
- if (mAppWidgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
- final int childCount = mAppWidgetContainer.getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (mAppWidgetContainer.getWidgetPageAt(i).getContentAppWidgetId()
- == mAppWidgetToShow) {
- return i;
- }
- }
- mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
- }
- // if music playing, show transport
- if (musicTransportState == TRANSPORT_VISIBLE) {
- if (DEBUG) Log.d(TAG, "Music playing, show transport");
- return mAppWidgetContainer.getWidgetPageIndex(getOrCreateTransportControl());
- }
-
- // else show the right-most widget (except for camera)
- int rightMost = mAppWidgetContainer.getChildCount() - 1;
- if (mAppWidgetContainer.isCameraPage(rightMost)) {
- rightMost--;
- }
- if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
- return rightMost;
- }
-
- private void enableUserSelectorIfNecessary() {
- if (!UserManager.supportsMultipleUsers()) {
- return; // device doesn't support multi-user mode
- }
- final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (um == null) {
- Throwable t = new Throwable();
- t.fillInStackTrace();
- Log.e(TAG, "user service is null.", t);
- return;
- }
-
- // if there are multiple users, we need to enable to multi-user switcher
- final List<UserInfo> users = um.getUsers(true);
- if (users == null) {
- Throwable t = new Throwable();
- t.fillInStackTrace();
- Log.e(TAG, "list of users is null.", t);
- return;
- }
-
- final View multiUserView = findViewById(R.id.keyguard_user_selector);
- if (multiUserView == null) {
- Throwable t = new Throwable();
- t.fillInStackTrace();
- Log.e(TAG, "can't find user_selector in layout.", t);
- return;
- }
-
- if (users.size() > 1) {
- if (multiUserView instanceof KeyguardMultiUserSelectorView) {
- mKeyguardMultiUserSelectorView = (KeyguardMultiUserSelectorView) multiUserView;
- mKeyguardMultiUserSelectorView.setVisibility(View.VISIBLE);
- mKeyguardMultiUserSelectorView.addUsers(users);
- UserSwitcherCallback callback = new UserSwitcherCallback() {
- @Override
- public void hideSecurityView(int duration) {
- mSecurityViewContainer.animate().alpha(0).setDuration(duration);
- }
-
- @Override
- public void showSecurityView() {
- mSecurityViewContainer.setAlpha(1.0f);
- }
-
- @Override
- public void showUnlockHint() {
- if (mKeyguardSelectorView != null) {
- mKeyguardSelectorView.showUsabilityHint();
- }
- }
-
- @Override
- public void userActivity() {
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.userActivity();
- }
- }
- };
- mKeyguardMultiUserSelectorView.setCallback(callback);
- } else {
- Throwable t = new Throwable();
- t.fillInStackTrace();
- if (multiUserView == null) {
- Log.e(TAG, "could not find the user_selector.", t);
- } else {
- Log.e(TAG, "user_selector is the wrong type.", t);
- }
- }
- }
- }
-
- @Override
- public void cleanUp() {
- // Make sure we let go of all widgets and their package contexts promptly. If we don't do
- // this, and the associated application is uninstalled, it can cause a soft reboot.
- int count = mAppWidgetContainer.getChildCount();
- for (int i = 0; i < count; i++) {
- KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i);
- frame.removeAllViews();
- }
- }
-
- /**
- * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
- * some cases where we wish to disable it, notably when the menu button placement or technology
- * is prone to false positives.
- *
- * @return true if the menu key should be enabled
- */
- private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
- private boolean shouldEnableMenuKey() {
- final Resources res = getResources();
- final boolean configDisabled = res.getBoolean(
- com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
- final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
- final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
- return !configDisabled || isTestHarness || fileOverride;
- }
-
- public void goToUserSwitcher() {
- mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
- }
-
- public void goToWidget(int appWidgetId) {
- mAppWidgetToShow = appWidgetId;
- mSwitchPageRunnable.run();
- }
-
- public boolean handleMenuKey() {
- // The following enables the MENU key to work for testing automation
- if (shouldEnableMenuKey()) {
- showNextSecurityScreenOrFinish(false);
- return true;
- }
- return false;
- }
-
- public boolean handleBackKey() {
- if (mCurrentSecuritySelection == SecurityMode.Account) {
- // go back to primary screen and re-disable back
- setBackButtonEnabled(false);
- showPrimarySecurityScreen(false /*turningOff*/);
- return true;
- }
- if (mCurrentSecuritySelection != SecurityMode.None) {
- mCallback.dismiss(false);
- return true;
- }
- return false;
- }
-
- /**
- * Dismisses the keyguard by going to the next screen or making it gone.
- */
- public void dismiss() {
- showNextSecurityScreenOrFinish(false);
- }
-
- public void showAssistant() {
- final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
-
- if (intent == null) return;
-
- final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit,
- getHandler(), null);
-
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- mActivityLauncher.launchActivityWithAnimation(
- intent, false, opts.toBundle(), null, null);
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java
deleted file mode 100644
index 0fc54cd..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-
-/**
- * A layout that arranges its children into a special type of grid.
- */
-public class KeyguardLinearLayout extends LinearLayout {
- int mTopChild = 0;
-
- public KeyguardLinearLayout(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardLinearLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardLinearLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public void setTopChild(View child) {
- int top = indexOfChild(child);
- mTopChild = top;
- invalidate();
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
deleted file mode 100644
index 9b58803..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.os.BatteryManager;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
-import android.widget.TextView;
-
-import libcore.util.MutableInt;
-
-import java.lang.ref.WeakReference;
-
-import com.android.internal.R;
-import com.android.internal.widget.ILockSettings;
-import com.android.internal.widget.LockPatternUtils;
-
-/***
- * Manages a number of views inside of the given layout. See below for a list of widgets.
- */
-class KeyguardMessageArea extends TextView {
- /** Handler token posted with accessibility announcement runnables. */
- private static final Object ANNOUNCE_TOKEN = new Object();
-
- /**
- * Delay before speaking an accessibility announcement. Used to prevent
- * lift-to-type from interrupting itself.
- */
- private static final long ANNOUNCEMENT_DELAY = 250;
-
- static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
- static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
-
- static final int SECURITY_MESSAGE_DURATION = 5000;
- protected static final int FADE_DURATION = 750;
-
- private static final String TAG = "KeyguardMessageArea";
-
- // are we showing battery information?
- boolean mShowingBatteryInfo = false;
-
- // is the bouncer up?
- boolean mShowingBouncer = false;
-
- // last known plugged in state
- boolean mCharging = false;
-
- // last known battery level
- int mBatteryLevel = 100;
-
- KeyguardUpdateMonitor mUpdateMonitor;
-
- // Timeout before we reset the message to show charging/owner info
- long mTimeout = SECURITY_MESSAGE_DURATION;
-
- // Shadowed text values
- protected boolean mBatteryCharged;
- protected boolean mBatteryIsLow;
-
- private Handler mHandler;
-
- CharSequence mMessage;
- boolean mShowingMessage;
- private CharSequence mSeparator;
- private LockPatternUtils mLockPatternUtils;
-
- Runnable mClearMessageRunnable = new Runnable() {
- @Override
- public void run() {
- mMessage = null;
- mShowingMessage = false;
- if (mShowingBouncer) {
- hideMessage(FADE_DURATION, true);
- } else {
- update();
- }
- }
- };
-
- public static class Helper implements SecurityMessageDisplay {
- KeyguardMessageArea mMessageArea;
- Helper(View v) {
- mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area);
- if (mMessageArea == null) {
- throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
- }
- }
-
- public void setMessage(CharSequence msg, boolean important) {
- if (!TextUtils.isEmpty(msg) && important) {
- mMessageArea.mMessage = msg;
- mMessageArea.securityMessageChanged();
- }
- }
-
- public void setMessage(int resId, boolean important) {
- if (resId != 0 && important) {
- mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId);
- mMessageArea.securityMessageChanged();
- }
- }
-
- public void setMessage(int resId, boolean important, Object... formatArgs) {
- if (resId != 0 && important) {
- mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs);
- mMessageArea.securityMessageChanged();
- }
- }
-
- @Override
- public void showBouncer(int duration) {
- mMessageArea.hideMessage(duration, false);
- mMessageArea.mShowingBouncer = true;
- }
-
- @Override
- public void hideBouncer(int duration) {
- mMessageArea.showMessage(duration);
- mMessageArea.mShowingBouncer = false;
- }
-
- @Override
- public void setTimeout(int timeoutMs) {
- mMessageArea.mTimeout = timeoutMs;
- }
- }
-
- private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
- mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
- mCharging = status.status == BatteryManager.BATTERY_STATUS_CHARGING
- || status.status == BatteryManager.BATTERY_STATUS_FULL;
- mBatteryLevel = status.level;
- mBatteryCharged = status.isCharged();
- mBatteryIsLow = status.isBatteryLow();
- update();
- }
- };
-
- public KeyguardMessageArea(Context context) {
- this(context, null);
- }
-
- public KeyguardMessageArea(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mLockPatternUtils = new LockPatternUtils(context);
-
- // This is required to ensure marquee works
- setSelected(true);
-
- // Registering this callback immediately updates the battery state, among other things.
- mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
- mUpdateMonitor.registerCallback(mInfoCallback);
- mHandler = new Handler(Looper.myLooper());
-
- mSeparator = getResources().getString(R.string.kg_text_message_separator);
-
- update();
- }
-
- public void securityMessageChanged() {
- setAlpha(1f);
- mShowingMessage = true;
- update();
- mHandler.removeCallbacks(mClearMessageRunnable);
- if (mTimeout > 0) {
- mHandler.postDelayed(mClearMessageRunnable, mTimeout);
- }
- mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
- mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
- (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
- }
-
- /**
- * Update the status lines based on these rules:
- * AlarmStatus: Alarm state always gets it's own line.
- * Status1 is shared between help, battery status and generic unlock instructions,
- * prioritized in that order.
- * @param showStatusLines status lines are shown if true
- */
- void update() {
- MutableInt icon = new MutableInt(0);
- CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
- setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
- setText(status);
- }
-
- private CharSequence concat(CharSequence... args) {
- StringBuilder b = new StringBuilder();
- if (!TextUtils.isEmpty(args[0])) {
- b.append(args[0]);
- }
- for (int i = 1; i < args.length; i++) {
- CharSequence text = args[i];
- if (!TextUtils.isEmpty(text)) {
- if (b.length() > 0) {
- b.append(mSeparator);
- }
- b.append(text);
- }
- }
- return b.toString();
- }
-
- CharSequence getCurrentMessage() {
- return mShowingMessage ? mMessage : null;
- }
-
- String getOwnerInfo() {
- ContentResolver res = getContext().getContentResolver();
- String info = null;
- final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled();
- if (ownerInfoEnabled && !mShowingMessage) {
- info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser());
- }
- return info;
- }
-
- private CharSequence getChargeInfo(MutableInt icon) {
- CharSequence string = null;
- if (mShowingBatteryInfo && !mShowingMessage) {
- // Battery status
- if (mCharging) {
- // Charging, charged or waiting to charge.
- string = getContext().getString(mBatteryCharged
- ? com.android.internal.R.string.lockscreen_charged
- : com.android.internal.R.string.lockscreen_plugged_in, mBatteryLevel);
- icon.value = CHARGING_ICON;
- } else if (mBatteryIsLow) {
- // Battery is low
- string = getContext().getString(
- com.android.internal.R.string.lockscreen_low_battery);
- icon.value = BATTERY_LOW_ICON;
- }
- }
- return string;
- }
-
- private void hideMessage(int duration, boolean thenUpdate) {
- if (duration > 0) {
- Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
- anim.setDuration(duration);
- if (thenUpdate) {
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- update();
- }
- });
- }
- anim.start();
- } else {
- setAlpha(0f);
- if (thenUpdate) {
- update();
- }
- }
- }
-
- private void showMessage(int duration) {
- if (duration > 0) {
- Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f);
- anim.setDuration(duration);
- anim.start();
- } else {
- setAlpha(1f);
- }
- }
-
- /**
- * Runnable used to delay accessibility announcements.
- */
- private static class AnnounceRunnable implements Runnable {
- private final WeakReference<View> mHost;
- private final CharSequence mTextToAnnounce;
-
- public AnnounceRunnable(View host, CharSequence textToAnnounce) {
- mHost = new WeakReference<View>(host);
- mTextToAnnounce = textToAnnounce;
- }
-
- @Override
- public void run() {
- final View host = mHost.get();
- if (host != null) {
- host.announceForAccessibility(mTextToAnnounce);
- }
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
deleted file mode 100644
index 387e0ce..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-class KeyguardMultiUserAvatar extends FrameLayout {
- private static final String TAG = KeyguardMultiUserAvatar.class.getSimpleName();
- private static final boolean DEBUG = KeyguardHostView.DEBUG;
-
- private ImageView mUserImage;
- private TextView mUserName;
- private UserInfo mUserInfo;
- private static final float ACTIVE_ALPHA = 1.0f;
- private static final float INACTIVE_ALPHA = 1.0f;
- private static final float ACTIVE_SCALE = 1.5f;
- private static final float ACTIVE_TEXT_ALPHA = 0f;
- private static final float INACTIVE_TEXT_ALPHA = 0.5f;
- private static final int SWITCH_ANIMATION_DURATION = 150;
-
- private final float mActiveAlpha;
- private final float mActiveScale;
- private final float mActiveTextAlpha;
- private final float mInactiveAlpha;
- private final float mInactiveTextAlpha;
- private final float mShadowRadius;
- private final float mStroke;
- private final float mIconSize;
- private final int mFrameColor;
- private final int mFrameShadowColor;
- private final int mTextColor;
- private final int mHighlightColor;
-
- private boolean mTouched;
-
- private boolean mActive;
- private boolean mInit = true;
- private KeyguardMultiUserSelectorView mUserSelector;
- private KeyguardCircleFramedDrawable mFramed;
- private boolean mPressLock;
-
- public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
- KeyguardMultiUserSelectorView userSelector, UserInfo info) {
- KeyguardMultiUserAvatar icon = (KeyguardMultiUserAvatar)
- LayoutInflater.from(context).inflate(resId, userSelector, false);
-
- icon.init(info, userSelector);
- return icon;
- }
-
- public KeyguardMultiUserAvatar(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardMultiUserAvatar(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardMultiUserAvatar(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- Resources res = mContext.getResources();
- mTextColor = res.getColor(R.color.keyguard_avatar_nick_color);
- mIconSize = res.getDimension(R.dimen.keyguard_avatar_size);
- mStroke = res.getDimension(R.dimen.keyguard_avatar_frame_stroke_width);
- mShadowRadius = res.getDimension(R.dimen.keyguard_avatar_frame_shadow_radius);
- mFrameColor = res.getColor(R.color.keyguard_avatar_frame_color);
- mFrameShadowColor = res.getColor(R.color.keyguard_avatar_frame_shadow_color);
- mHighlightColor = res.getColor(R.color.keyguard_avatar_frame_pressed_color);
- mActiveTextAlpha = ACTIVE_TEXT_ALPHA;
- mInactiveTextAlpha = INACTIVE_TEXT_ALPHA;
- mActiveScale = ACTIVE_SCALE;
- mActiveAlpha = ACTIVE_ALPHA;
- mInactiveAlpha = INACTIVE_ALPHA;
-
- mTouched = false;
-
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
-
- protected String rewriteIconPath(String path) {
- if (!this.getClass().getName().contains("internal")) {
- return path.replace("system", "data");
- }
- return path;
- }
-
- public void init(UserInfo user, KeyguardMultiUserSelectorView userSelector) {
- mUserInfo = user;
- mUserSelector = userSelector;
-
- mUserImage = (ImageView) findViewById(R.id.keyguard_user_avatar);
- mUserName = (TextView) findViewById(R.id.keyguard_user_name);
-
- mFramed = (KeyguardCircleFramedDrawable)
- KeyguardViewMediator.getAvatarCache().get(user.id);
-
- // If we can't find it or the params don't match, create the drawable again
- if (mFramed == null
- || !mFramed.verifyParams(mIconSize, mFrameColor, mStroke, mFrameShadowColor,
- mShadowRadius, mHighlightColor)) {
- Bitmap icon = null;
- try {
- icon = BitmapFactory.decodeFile(rewriteIconPath(user.iconPath));
- } catch (Exception e) {
- if (DEBUG) Log.d(TAG, "failed to open profile icon " + user.iconPath, e);
- }
-
- if (icon == null) {
- icon = BitmapFactory.decodeResource(mContext.getResources(),
- com.android.internal.R.drawable.ic_contact_picture);
- }
-
- mFramed = new KeyguardCircleFramedDrawable(icon, (int) mIconSize, mFrameColor, mStroke,
- mFrameShadowColor, mShadowRadius, mHighlightColor);
- KeyguardViewMediator.getAvatarCache().put(user.id, mFramed);
- }
-
- mFramed.reset();
-
- mUserImage.setImageDrawable(mFramed);
- mUserName.setText(mUserInfo.name);
- setOnClickListener(mUserSelector);
- mInit = false;
- }
-
- public void setActive(boolean active, boolean animate, final Runnable onComplete) {
- if (mActive != active || mInit) {
- mActive = active;
-
- if (active) {
- KeyguardLinearLayout parent = (KeyguardLinearLayout) getParent();
- parent.setTopChild(this);
- // TODO: Create an appropriate asset when string changes are possible.
- setContentDescription(mUserName.getText()
- + ". " + mContext.getString(R.string.user_switched, ""));
- } else {
- setContentDescription(mUserName.getText());
- }
- }
- updateVisualsForActive(mActive, animate, SWITCH_ANIMATION_DURATION, onComplete);
- }
-
- void updateVisualsForActive(boolean active, boolean animate, int duration,
- final Runnable onComplete) {
- final float finalAlpha = active ? mActiveAlpha : mInactiveAlpha;
- final float initAlpha = active ? mInactiveAlpha : mActiveAlpha;
- final float finalScale = active ? 1f : 1f / mActiveScale;
- final float initScale = mFramed.getScale();
- final int finalTextAlpha = active ? (int) (mActiveTextAlpha * 255) :
- (int) (mInactiveTextAlpha * 255);
- final int initTextAlpha = active ? (int) (mInactiveTextAlpha * 255) :
- (int) (mActiveTextAlpha * 255);
- int textColor = mTextColor;
- mUserName.setTextColor(textColor);
-
- if (animate && mTouched) {
- ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
- va.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float r = animation.getAnimatedFraction();
- float scale = (1 - r) * initScale + r * finalScale;
- float alpha = (1 - r) * initAlpha + r * finalAlpha;
- int textAlpha = (int) ((1 - r) * initTextAlpha + r * finalTextAlpha);
- mFramed.setScale(scale);
- mUserImage.setAlpha(alpha);
- mUserName.setTextColor(Color.argb(textAlpha, 255, 255, 255));
- mUserImage.invalidate();
- }
- });
- va.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (onComplete != null) {
- onComplete.run();
- }
- }
- });
- va.setDuration(duration);
- va.start();
- } else {
- mFramed.setScale(finalScale);
- mUserImage.setAlpha(finalAlpha);
- mUserName.setTextColor(Color.argb(finalTextAlpha, 255, 255, 255));
- if (onComplete != null) {
- post(onComplete);
- }
- }
-
- mTouched = true;
- }
-
- @Override
- public void setPressed(boolean pressed) {
- if (mPressLock && !pressed) {
- return;
- }
-
- if (mPressLock || !pressed || isClickable()) {
- super.setPressed(pressed);
- mFramed.setPressed(pressed);
- mUserImage.invalidate();
- }
- }
-
- public void lockPressed(boolean pressed) {
- mPressLock = pressed;
- setPressed(pressed);
- }
-
- public UserInfo getUserInfo() {
- return mUserInfo;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
deleted file mode 100644
index f9ea5bb..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.ActivityManagerNative;
-import android.content.Context;
-import android.content.pm.UserInfo;
-import android.os.RemoteException;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-
-public class KeyguardMultiUserSelectorView extends FrameLayout implements View.OnClickListener {
- private static final String TAG = "KeyguardMultiUserSelectorView";
-
- private ViewGroup mUsersGrid;
- private KeyguardMultiUserAvatar mActiveUserAvatar;
- private KeyguardHostView.UserSwitcherCallback mCallback;
- private static final int FADE_OUT_ANIMATION_DURATION = 100;
-
- public KeyguardMultiUserSelectorView(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardMultiUserSelectorView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardMultiUserSelectorView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- protected void onFinishInflate () {
- mUsersGrid = (ViewGroup) findViewById(R.id.keyguard_users_grid);
- mUsersGrid.removeAllViews();
- setClipChildren(false);
- setClipToPadding(false);
-
- }
-
- public void setCallback(KeyguardHostView.UserSwitcherCallback callback) {
- mCallback = callback;
- }
-
- public void addUsers(Collection<UserInfo> userList) {
- UserInfo activeUser;
- try {
- activeUser = ActivityManagerNative.getDefault().getCurrentUser();
- } catch (RemoteException re) {
- activeUser = null;
- }
-
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(userList);
- Collections.sort(users, mOrderAddedComparator);
-
- for (UserInfo user: users) {
- KeyguardMultiUserAvatar uv = createAndAddUser(user);
- if (user.id == activeUser.id) {
- mActiveUserAvatar = uv;
- }
- uv.setActive(false, false, null);
- }
- mActiveUserAvatar.lockPressed(true);
- }
-
- public void finalizeActiveUserView(boolean animate) {
- if (animate) {
- getHandler().postDelayed(new Runnable() {
- @Override
- public void run() {
- finalizeActiveUserNow(true);
- }
- }, 500);
- } else {
- finalizeActiveUserNow(animate);
- }
- }
-
- void finalizeActiveUserNow(boolean animate) {
- mActiveUserAvatar.lockPressed(false);
- mActiveUserAvatar.setActive(true, animate, null);
- }
-
- Comparator<UserInfo> mOrderAddedComparator = new Comparator<UserInfo>() {
- @Override
- public int compare(UserInfo lhs, UserInfo rhs) {
- return (lhs.serialNumber - rhs.serialNumber);
- }
- };
-
- private KeyguardMultiUserAvatar createAndAddUser(UserInfo user) {
- KeyguardMultiUserAvatar uv = KeyguardMultiUserAvatar.fromXml(
- R.layout.keyguard_multi_user_avatar, mContext, this, user);
- mUsersGrid.addView(uv);
- return uv;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- if(event.getActionMasked() != MotionEvent.ACTION_CANCEL && mCallback != null) {
- mCallback.userActivity();
- }
- return false;
- }
-
- private void setAllClickable(boolean clickable)
- {
- for(int i = 0; i < mUsersGrid.getChildCount(); i++) {
- View v = mUsersGrid.getChildAt(i);
- v.setClickable(clickable);
- v.setPressed(false);
- }
- }
-
- @Override
- public void onClick(View v) {
- if (!(v instanceof KeyguardMultiUserAvatar)) return;
- final KeyguardMultiUserAvatar avatar = (KeyguardMultiUserAvatar) v;
- if (avatar.isClickable()) { // catch race conditions
- if (mActiveUserAvatar == avatar) {
- // If they click the currently active user, show the unlock hint
- mCallback.showUnlockHint();
- return;
- } else {
- // Reset the previously active user to appear inactive
- mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION);
- setAllClickable(false);
- avatar.lockPressed(true);
- mActiveUserAvatar.setActive(false, true, new Runnable() {
- @Override
- public void run() {
- mActiveUserAvatar = avatar;
- if (this.getClass().getName().contains("internal")) {
- try {
- ActivityManagerNative.getDefault()
- .switchUser(avatar.getUserInfo().id);
- } catch (RemoteException re) {
- Log.e(TAG, "Couldn't switch user " + re);
- }
- } else {
- setAllClickable(true);
- }
- }
- });
- }
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
deleted file mode 100644
index fa80352..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextWatcher;
-import android.text.method.DigitsKeyListener;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView.OnEditorActionListener;
-
-import com.android.internal.R;
-
-/**
- * Displays a PIN pad for unlocking.
- */
-public class KeyguardPINView extends KeyguardAbsKeyInputView
- implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
-
- public KeyguardPINView(Context context) {
- this(context, null);
- }
-
- public KeyguardPINView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- protected void resetState() {
- if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) {
- mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true);
- } else {
- mSecurityMessageDisplay.setMessage(R.string.kg_pin_instructions, false);
- }
- mPasswordEntry.setEnabled(true);
- }
-
- @Override
- protected int getPasswordTextViewId() {
- return R.id.pinEntry;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- final View ok = findViewById(R.id.key_enter);
- if (ok != null) {
- ok.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- doHapticKeyClick();
- if (mPasswordEntry.isEnabled()) {
- verifyPasswordAndUnlock();
- }
- }
- });
- ok.setOnHoverListener(new LiftToActivateListener(getContext()));
- }
-
- // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
- // not a separate view
- View pinDelete = findViewById(R.id.delete_button);
- if (pinDelete != null) {
- pinDelete.setVisibility(View.VISIBLE);
- pinDelete.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- CharSequence str = mPasswordEntry.getText();
- if (str.length() > 0) {
- mPasswordEntry.setText(str.subSequence(0, str.length()-1));
- }
- }
- doHapticKeyClick();
- }
- });
- pinDelete.setOnLongClickListener(new View.OnLongClickListener() {
- public boolean onLongClick(View v) {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- mPasswordEntry.setText("");
- }
- doHapticKeyClick();
- return true;
- }
- });
- }
-
- mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
- | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
-
- mPasswordEntry.requestFocus();
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public int getWrongPasswordStringId() {
- return R.string.kg_wrong_pin;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
deleted file mode 100644
index d52c993..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextWatcher;
-import android.text.method.DigitsKeyListener;
-import android.text.method.TextKeyListener;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-import android.widget.TextView.OnEditorActionListener;
-
-import com.android.internal.R;
-import com.android.internal.widget.PasswordEntryKeyboardHelper;
-import com.android.internal.widget.PasswordEntryKeyboardView;
-
-import java.util.List;
-/**
- * Displays an alphanumeric (latin-1) key entry for the user to enter
- * an unlock password
- */
-
-public class KeyguardPasswordView extends KeyguardAbsKeyInputView
- implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
-
- private final boolean mShowImeAtScreenOn;
-
- InputMethodManager mImm;
-
- public KeyguardPasswordView(Context context) {
- this(context, null);
- }
-
- public KeyguardPasswordView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mShowImeAtScreenOn = context.getResources().
- getBoolean(R.bool.kg_show_ime_at_screen_on);
- }
-
- protected void resetState() {
- mSecurityMessageDisplay.setMessage(R.string.kg_password_instructions, false);
- mPasswordEntry.setEnabled(true);
- }
-
- @Override
- protected int getPasswordTextViewId() {
- return R.id.passwordEntry;
- }
-
- @Override
- public boolean needsInput() {
- return true;
- }
-
- @Override
- public void onResume(int reason) {
- super.onResume(reason);
- mPasswordEntry.requestFocus();
- if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
- mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mImm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- boolean imeOrDeleteButtonVisible = false;
-
- mImm = (InputMethodManager) getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
-
- mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- // Poke the wakelock any time the text is selected or modified
- mPasswordEntry.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mCallback.userActivity(0); // TODO: customize timeout for text?
- }
- });
-
- mPasswordEntry.addTextChangedListener(new TextWatcher() {
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- public void afterTextChanged(Editable s) {
- if (mCallback != null) {
- mCallback.userActivity(0);
- }
- }
- });
-
- mPasswordEntry.requestFocus();
-
- // If there's more than one IME, enable the IME switcher button
- View switchImeButton = findViewById(R.id.switch_ime_button);
- if (switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(mImm, false)) {
- switchImeButton.setVisibility(View.VISIBLE);
- imeOrDeleteButtonVisible = true;
- switchImeButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mCallback.userActivity(0); // Leave the screen on a bit longer
- mImm.showInputMethodPicker();
- }
- });
- }
-
- // If no icon is visible, reset the start margin on the password field so the text is
- // still centered.
- if (!imeOrDeleteButtonVisible) {
- android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
- if (params instanceof MarginLayoutParams) {
- final MarginLayoutParams mlp = (MarginLayoutParams) params;
- mlp.setMarginStart(0);
- mPasswordEntry.setLayoutParams(params);
- }
- }
- }
-
- /**
- * Method adapted from com.android.inputmethod.latin.Utils
- *
- * @param imm The input method manager
- * @param shouldIncludeAuxiliarySubtypes
- * @return true if we have multiple IMEs to choose from
- */
- private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
- final boolean shouldIncludeAuxiliarySubtypes) {
- final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
-
- // Number of the filtered IMEs
- int filteredImisCount = 0;
-
- for (InputMethodInfo imi : enabledImis) {
- // We can return true immediately after we find two or more filtered IMEs.
- if (filteredImisCount > 1) return true;
- final List<InputMethodSubtype> subtypes =
- imm.getEnabledInputMethodSubtypeList(imi, true);
- // IMEs that have no subtypes should be counted.
- if (subtypes.isEmpty()) {
- ++filteredImisCount;
- continue;
- }
-
- int auxCount = 0;
- for (InputMethodSubtype subtype : subtypes) {
- if (subtype.isAuxiliary()) {
- ++auxCount;
- }
- }
- final int nonAuxCount = subtypes.size() - auxCount;
-
- // IMEs that have one or more non-auxiliary subtypes should be counted.
- // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
- // subtypes should be counted as well.
- if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
- ++filteredImisCount;
- continue;
- }
- }
-
- return filteredImisCount > 1
- // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
- // input method subtype (The current IME should be LatinIME.)
- || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public int getWrongPasswordStringId() {
- return R.string.kg_wrong_password;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
deleted file mode 100644
index e114b78..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.CountDownTimer;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.Button;
-import android.widget.LinearLayout;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockPatternView;
-import com.android.internal.R;
-
-import java.io.IOException;
-import java.util.List;
-
-public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView {
-
- private static final String TAG = "SecurityPatternView";
- private static final boolean DEBUG = false;
-
- // how long before we clear the wrong pattern
- private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
-
- // how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
- private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
-
- // how long we stay awake after the user hits the first dot.
- private static final int UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS = 2000;
-
- // how many cells the user has to cross before we poke the wakelock
- private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
-
- private int mFailedPatternAttemptsSinceLastTimeout = 0;
- private int mTotalFailedPatternAttempts = 0;
- private CountDownTimer mCountdownTimer = null;
- private LockPatternUtils mLockPatternUtils;
- private LockPatternView mLockPatternView;
- private Button mForgotPatternButton;
- private KeyguardSecurityCallback mCallback;
- private boolean mEnableFallback;
-
- /**
- * Keeps track of the last time we poked the wake lock during dispatching of the touch event.
- * Initialized to something guaranteed to make us poke the wakelock when the user starts
- * drawing the pattern.
- * @see #dispatchTouchEvent(android.view.MotionEvent)
- */
- private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
-
- /**
- * Useful for clearing out the wrong pattern after a delay
- */
- private Runnable mCancelPatternRunnable = new Runnable() {
- public void run() {
- mLockPatternView.clearPattern();
- }
- };
- private Rect mTempRect = new Rect();
- private SecurityMessageDisplay mSecurityMessageDisplay;
- private View mEcaView;
- private Drawable mBouncerFrame;
-
- enum FooterMode {
- Normal,
- ForgotLockPattern,
- VerifyUnlocked
- }
-
- public KeyguardPatternView(Context context) {
- this(context, null);
- }
-
- public KeyguardPatternView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mLockPatternUtils = mLockPatternUtils == null
- ? new LockPatternUtils(mContext) : mLockPatternUtils;
-
- mLockPatternView = (LockPatternView) findViewById(R.id.lockPatternView);
- mLockPatternView.setSaveEnabled(false);
- mLockPatternView.setFocusable(false);
- mLockPatternView.setOnPatternListener(new UnlockPatternListener());
-
- // stealth mode will be the same for the life of this screen
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled());
-
- // vibrate mode will be the same for the life of this screen
- mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
-
- mForgotPatternButton = (Button) findViewById(R.id.forgot_password_button);
- // note: some configurations don't have an emergency call area
- if (mForgotPatternButton != null) {
- mForgotPatternButton.setText(R.string.kg_forgot_pattern_button_text);
- mForgotPatternButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mCallback.showBackupSecurity();
- }
- });
- }
-
- setFocusableInTouchMode(true);
-
- maybeEnableFallback(mContext);
- mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
- mEcaView = findViewById(R.id.keyguard_selector_fade_container);
- View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame);
- if (bouncerFrameView != null) {
- mBouncerFrame = bouncerFrameView.getBackground();
- }
- }
-
- private void updateFooter(FooterMode mode) {
- if (mForgotPatternButton == null) return; // no ECA? no footer
-
- switch (mode) {
- case Normal:
- if (DEBUG) Log.d(TAG, "mode normal");
- mForgotPatternButton.setVisibility(View.GONE);
- break;
- case ForgotLockPattern:
- if (DEBUG) Log.d(TAG, "mode ForgotLockPattern");
- mForgotPatternButton.setVisibility(View.VISIBLE);
- break;
- case VerifyUnlocked:
- if (DEBUG) Log.d(TAG, "mode VerifyUnlocked");
- mForgotPatternButton.setVisibility(View.GONE);
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- boolean result = super.onTouchEvent(ev);
- // as long as the user is entering a pattern (i.e sending a touch event that was handled
- // by this screen), keep poking the wake lock so that the screen will stay on.
- final long elapsed = SystemClock.elapsedRealtime() - mLastPokeTime;
- if (result && (elapsed > (UNLOCK_PATTERN_WAKE_INTERVAL_MS - 100))) {
- mLastPokeTime = SystemClock.elapsedRealtime();
- }
- mTempRect.set(0, 0, 0, 0);
- offsetRectIntoDescendantCoords(mLockPatternView, mTempRect);
- ev.offsetLocation(mTempRect.left, mTempRect.top);
- result = mLockPatternView.dispatchTouchEvent(ev) || result;
- ev.offsetLocation(-mTempRect.left, -mTempRect.top);
- return result;
- }
-
- public void reset() {
- // reset lock pattern
- mLockPatternView.enableInput();
- mLockPatternView.setEnabled(true);
- mLockPatternView.clearPattern();
-
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
- if (deadline != 0) {
- handleAttemptLockout(deadline);
- } else {
- displayDefaultSecurityMessage();
- }
-
- // the footer depends on how many total attempts the user has failed
- if (mCallback.isVerifyUnlockOnly()) {
- updateFooter(FooterMode.VerifyUnlocked);
- } else if (mEnableFallback &&
- (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
- updateFooter(FooterMode.ForgotLockPattern);
- } else {
- updateFooter(FooterMode.Normal);
- }
-
- }
-
- private void displayDefaultSecurityMessage() {
- if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) {
- mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true);
- } else {
- mSecurityMessageDisplay.setMessage(R.string.kg_pattern_instructions, false);
- }
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- /** TODO: hook this up */
- public void cleanUp() {
- if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
- mLockPatternUtils = null;
- mLockPatternView.setOnPatternListener(null);
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
- if (hasWindowFocus) {
- // when timeout dialog closes we want to update our state
- reset();
- }
- }
-
- private class UnlockPatternListener implements LockPatternView.OnPatternListener {
-
- public void onPatternStart() {
- mLockPatternView.removeCallbacks(mCancelPatternRunnable);
- }
-
- public void onPatternCleared() {
- }
-
- public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
- // To guard against accidental poking of the wakelock, look for
- // the user actually trying to draw a pattern of some minimal length.
- if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
- mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
- } else {
- // Give just a little extra time if they hit one of the first few dots
- mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS);
- }
- }
-
- public void onPatternDetected(List<LockPatternView.Cell> pattern) {
- if (mLockPatternUtils.checkPattern(pattern)) {
- mCallback.reportSuccessfulUnlockAttempt();
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
- mTotalFailedPatternAttempts = 0;
- mCallback.dismiss(true);
- } else {
- if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
- mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
- }
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
- if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
- mTotalFailedPatternAttempts++;
- mFailedPatternAttemptsSinceLastTimeout++;
- mCallback.reportFailedUnlockAttempt();
- }
- if (mFailedPatternAttemptsSinceLastTimeout
- >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
- handleAttemptLockout(deadline);
- } else {
- mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern, true);
- mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
- }
- }
- }
- }
-
- private void maybeEnableFallback(Context context) {
- // Ask the account manager if we have an account that can be used as a
- // fallback in case the user forgets his pattern.
- AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context));
- accountAnalyzer.start();
- }
-
- private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
- private final AccountManager mAccountManager;
- private final Account[] mAccounts;
- private int mAccountIndex;
-
- private AccountAnalyzer(AccountManager accountManager) {
- mAccountManager = accountManager;
- mAccounts = accountManager.getAccountsByTypeAsUser("com.google",
- new UserHandle(mLockPatternUtils.getCurrentUser()));
- }
-
- private void next() {
- // if we are ready to enable the fallback or if we depleted the list of accounts
- // then finish and get out
- if (mEnableFallback || mAccountIndex >= mAccounts.length) {
- return;
- }
-
- // lookup the confirmCredentials intent for the current account
- mAccountManager.confirmCredentialsAsUser(mAccounts[mAccountIndex], null, null, this,
- null, new UserHandle(mLockPatternUtils.getCurrentUser()));
- }
-
- public void start() {
- mEnableFallback = false;
- mAccountIndex = 0;
- next();
- }
-
- public void run(AccountManagerFuture<Bundle> future) {
- try {
- Bundle result = future.getResult();
- if (result.getParcelable(AccountManager.KEY_INTENT) != null) {
- mEnableFallback = true;
- }
- } catch (OperationCanceledException e) {
- // just skip the account if we are unable to query it
- } catch (IOException e) {
- // just skip the account if we are unable to query it
- } catch (AuthenticatorException e) {
- // just skip the account if we are unable to query it
- } finally {
- mAccountIndex++;
- next();
- }
- }
- }
-
- private void handleAttemptLockout(long elapsedRealtimeDeadline) {
- mLockPatternView.clearPattern();
- mLockPatternView.setEnabled(false);
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- if (mEnableFallback) {
- updateFooter(FooterMode.ForgotLockPattern);
- }
-
- mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- final int secondsRemaining = (int) (millisUntilFinished / 1000);
- mSecurityMessageDisplay.setMessage(
- R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining);
- }
-
- @Override
- public void onFinish() {
- mLockPatternView.setEnabled(true);
- displayDefaultSecurityMessage();
- // TODO mUnlockIcon.setVisibility(View.VISIBLE);
- mFailedPatternAttemptsSinceLastTimeout = 0;
- if (mEnableFallback) {
- updateFooter(FooterMode.ForgotLockPattern);
- } else {
- updateFooter(FooterMode.Normal);
- }
- }
-
- }.start();
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onPause() {
- if (mCountdownTimer != null) {
- mCountdownTimer.cancel();
- mCountdownTimer = null;
- }
- }
-
- @Override
- public void onResume(int reason) {
- reset();
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void showBouncer(int duration) {
- KeyguardSecurityViewHelper.
- showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
- }
-
- @Override
- public void hideBouncer(int duration) {
- KeyguardSecurityViewHelper.
- hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java
deleted file mode 100644
index 7e6c108..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import com.android.internal.policy.impl.keyguard.KeyguardHostView.OnDismissAction;
-
-public interface KeyguardSecurityCallback {
-
- /**
- * Dismiss the given security screen.
- * @param securityVerified true if the user correctly entered credentials for the given screen.
- */
- void dismiss(boolean securityVerified);
-
- /**
- * Manually report user activity to keep the device awake. If timeout is 0,
- * uses user-defined timeout.
- * @param timeout
- */
- void userActivity(long timeout);
-
- /**
- * Checks if keyguard is in "verify credentials" mode.
- * @return true if user has been asked to verify security.
- */
- boolean isVerifyUnlockOnly();
-
- /**
- * Call when user correctly enters their credentials
- */
- void reportSuccessfulUnlockAttempt();
-
- /**
- * Call when the user incorrectly enters their credentials
- */
- void reportFailedUnlockAttempt();
-
- /**
- * Gets the number of attempts thus far as reported by {@link #reportFailedUnlockAttempt()}
- * @return number of failed attempts
- */
- int getFailedAttempts();
-
- /**
- * Shows the backup security for the current method. If none available, this call is a no-op.
- */
- void showBackupSecurity();
-
- /**
- * Sets an action to perform after the user successfully enters their credentials.
- * @param action
- */
- void setOnDismissAction(OnDismissAction action);
-
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
deleted file mode 100644
index 375a96a..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-
-public class KeyguardSecurityContainer extends FrameLayout {
- public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardSecurityContainer(Context context) {
- this(null, null, 0);
- }
-
- public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- KeyguardSecurityViewFlipper getFlipper() {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child instanceof KeyguardSecurityViewFlipper) {
- return (KeyguardSecurityViewFlipper) child;
- }
- }
- return null;
- }
-
- public void showBouncer(int duration) {
- KeyguardSecurityViewFlipper flipper = getFlipper();
- if (flipper != null) {
- flipper.showBouncer(duration);
- }
- }
-
- public void hideBouncer(int duration) {
- KeyguardSecurityViewFlipper flipper = getFlipper();
- if (flipper != null) {
- flipper.hideBouncer(duration);
- }
- }
-}
-
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
deleted file mode 100644
index 7a69586..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.telephony.TelephonyManager;
-
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.widget.LockPatternUtils;
-
-public class KeyguardSecurityModel {
- /**
- * The different types of security available for {@link Mode#UnlockScreen}.
- * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
- */
- enum SecurityMode {
- Invalid, // NULL state
- None, // No security enabled
- Pattern, // Unlock by drawing a pattern.
- Password, // Unlock by entering an alphanumeric password
- PIN, // Strictly numeric password
- Biometric, // Unlock with a biometric key (e.g. finger print or face unlock)
- Account, // Unlock by entering an account's login and password.
- SimPin, // Unlock by entering a sim pin.
- SimPuk // Unlock by entering a sim puk
- }
-
- private Context mContext;
- private LockPatternUtils mLockPatternUtils;
-
- KeyguardSecurityModel(Context context) {
- mContext = context;
- mLockPatternUtils = new LockPatternUtils(context);
- }
-
- void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- }
-
- /**
- * Returns true if biometric unlock is installed and selected. If this returns false there is
- * no need to even construct the biometric unlock.
- */
- boolean isBiometricUnlockEnabled() {
- return mLockPatternUtils.usingBiometricWeak()
- && mLockPatternUtils.isBiometricWeakInstalled();
- }
-
- /**
- * Returns true if a condition is currently suppressing the biometric unlock. If this returns
- * true there is no need to even construct the biometric unlock.
- */
- private boolean isBiometricUnlockSuppressed() {
- KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
- final boolean backupIsTimedOut = monitor.getFailedUnlockAttempts() >=
- LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
- return monitor.getMaxBiometricUnlockAttemptsReached() || backupIsTimedOut
- || !monitor.isAlternateUnlockEnabled()
- || monitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE;
- }
-
- SecurityMode getSecurityMode() {
- KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- final IccCardConstants.State simState = updateMonitor.getSimState();
- SecurityMode mode = SecurityMode.None;
- if (simState == IccCardConstants.State.PIN_REQUIRED) {
- mode = SecurityMode.SimPin;
- } else if (simState == IccCardConstants.State.PUK_REQUIRED
- && mLockPatternUtils.isPukUnlockScreenEnable()) {
- mode = SecurityMode.SimPuk;
- } else {
- final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality();
- switch (security) {
- case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
- mode = mLockPatternUtils.isLockPasswordEnabled() ?
- SecurityMode.PIN : SecurityMode.None;
- break;
- case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
- case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
- case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
- mode = mLockPatternUtils.isLockPasswordEnabled() ?
- SecurityMode.Password : SecurityMode.None;
- break;
-
- case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
- case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
- if (mLockPatternUtils.isLockPatternEnabled()) {
- mode = mLockPatternUtils.isPermanentlyLocked() ?
- SecurityMode.Account : SecurityMode.Pattern;
- }
- break;
-
- default:
- throw new IllegalStateException("Unknown unlock mode:" + mode);
- }
- }
- return mode;
- }
-
- /**
- * Some unlock methods can have an alternate, such as biometric unlocks (e.g. face unlock).
- * This function decides if an alternate unlock is available and returns it. Otherwise,
- * returns @param mode.
- *
- * @param mode the mode we want the alternate for
- * @return alternate or the given mode
- */
- SecurityMode getAlternateFor(SecurityMode mode) {
- if (isBiometricUnlockEnabled() && !isBiometricUnlockSuppressed()
- && (mode == SecurityMode.Password
- || mode == SecurityMode.PIN
- || mode == SecurityMode.Pattern)) {
- return SecurityMode.Biometric;
- }
- return mode; // no alternate, return what was given
- }
-
- /**
- * Some unlock methods can have a backup which gives the user another way to get into
- * the device. This is currently only supported for Biometric and Pattern unlock.
- *
- * @return backup method or current security mode
- */
- SecurityMode getBackupSecurityMode(SecurityMode mode) {
- switch(mode) {
- case Biometric:
- return getSecurityMode();
- case Pattern:
- return SecurityMode.Account;
- }
- return mode; // no backup, return current security mode
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
deleted file mode 100644
index a3ac39c..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import com.android.internal.widget.LockPatternUtils;
-
-public interface KeyguardSecurityView {
- static public final int SCREEN_ON = 1;
- static public final int VIEW_REVEALED = 2;
-
- /**
- * Interface back to keyguard to tell it when security
- * @param callback
- */
- void setKeyguardCallback(KeyguardSecurityCallback callback);
-
- /**
- * Set {@link LockPatternUtils} object. Useful for providing a mock interface.
- * @param utils
- */
- void setLockPatternUtils(LockPatternUtils utils);
-
- /**
- * Reset the view and prepare to take input. This should do things like clearing the
- * password or pattern and clear error messages.
- */
- void reset();
-
- /**
- * Emulate activity life cycle within the view. When called, the view should clean up
- * and prepare to be removed.
- */
- void onPause();
-
- /**
- * Emulate activity life cycle within this view. When called, the view should prepare itself
- * to be shown.
- * @param reason the root cause of the event.
- */
- void onResume(int reason);
-
- /**
- * Inquire whether this view requires IME (keyboard) interaction.
- *
- * @return true if IME interaction is required.
- */
- boolean needsInput();
-
- /**
- * Get {@link KeyguardSecurityCallback} for the given object
- * @return KeyguardSecurityCallback
- */
- KeyguardSecurityCallback getCallback();
-
- /**
- * Instruct the view to show usability hints, if any.
- *
- */
- void showUsabilityHint();
-
- /**
- * Place the security view into bouncer mode.
- * Animate transisiton if duration is non-zero.
- * @param duration millisends for the transisiton animation.
- */
- void showBouncer(int duration);
-
- /**
- * Place the security view into non-bouncer mode.
- * Animate transisiton if duration is non-zero.
- * @param duration millisends for the transisiton animation.
- */
- void hideBouncer(int duration);
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
deleted file mode 100644
index aa31b00..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.ViewFlipper;
-
-/**
- * Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so
- * we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy.
- *
- */
-public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
- private static final String TAG = "KeyguardSecurityViewFlipper";
- private static final boolean DEBUG = false;
-
- private Rect mTempRect = new Rect();
-
- public KeyguardSecurityViewFlipper(Context context) {
- this(context, null);
- }
-
- public KeyguardSecurityViewFlipper(Context context, AttributeSet attr) {
- super(context, attr);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- boolean result = super.onTouchEvent(ev);
- mTempRect.set(0, 0, 0, 0);
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child.getVisibility() == View.VISIBLE) {
- offsetRectIntoDescendantCoords(child, mTempRect);
- ev.offsetLocation(mTempRect.left, mTempRect.top);
- result = child.dispatchTouchEvent(ev) || result;
- ev.offsetLocation(-mTempRect.left, -mTempRect.top);
- }
- }
- return result;
- }
-
- KeyguardSecurityView getSecurityView() {
- View child = getChildAt(getDisplayedChild());
- if (child instanceof KeyguardSecurityView) {
- return (KeyguardSecurityView) child;
- }
- return null;
- }
-
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.setKeyguardCallback(callback);
- }
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.setLockPatternUtils(utils);
- }
- }
-
- @Override
- public void reset() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.reset();
- }
- }
-
- @Override
- public void onPause() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.onPause();
- }
- }
-
- @Override
- public void onResume(int reason) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.onResume(reason);
- }
- }
-
- @Override
- public boolean needsInput() {
- KeyguardSecurityView ksv = getSecurityView();
- return (ksv != null) ? ksv.needsInput() : false;
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- KeyguardSecurityView ksv = getSecurityView();
- return (ksv != null) ? ksv.getCallback() : null;
- }
-
- @Override
- public void showUsabilityHint() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showUsabilityHint();
- }
- }
-
- @Override
- public void showBouncer(int duration) {
- KeyguardSecurityView active = getSecurityView();
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child instanceof KeyguardSecurityView) {
- KeyguardSecurityView ksv = (KeyguardSecurityView) child;
- ksv.showBouncer(ksv == active ? duration : 0);
- }
- }
- }
-
- @Override
- public void hideBouncer(int duration) {
- KeyguardSecurityView active = getSecurityView();
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child instanceof KeyguardSecurityView) {
- KeyguardSecurityView ksv = (KeyguardSecurityView) child;
- ksv.hideBouncer(ksv == active ? duration : 0);
- }
- }
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams;
- }
-
- @Override
- protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) : new LayoutParams(p);
- }
-
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
-
- @Override
- protected void onMeasure(int widthSpec, int heightSpec) {
- final int widthMode = MeasureSpec.getMode(widthSpec);
- final int heightMode = MeasureSpec.getMode(heightSpec);
- if (DEBUG && widthMode != MeasureSpec.AT_MOST) {
- Log.w(TAG, "onMeasure: widthSpec " + MeasureSpec.toString(widthSpec) +
- " should be AT_MOST");
- }
- if (DEBUG && heightMode != MeasureSpec.AT_MOST) {
- Log.w(TAG, "onMeasure: heightSpec " + MeasureSpec.toString(heightSpec) +
- " should be AT_MOST");
- }
-
- final int widthSize = MeasureSpec.getSize(widthSpec);
- final int heightSize = MeasureSpec.getSize(heightSpec);
- int maxWidth = widthSize;
- int maxHeight = heightSize;
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) {
- maxWidth = lp.maxWidth;
- }
- if (lp.maxHeight > 0 && lp.maxHeight < maxHeight) {
- maxHeight = lp.maxHeight;
- }
- }
-
- final int wPadding = getPaddingLeft() + getPaddingRight();
- final int hPadding = getPaddingTop() + getPaddingBottom();
- maxWidth -= wPadding;
- maxHeight -= hPadding;
-
- int width = widthMode == MeasureSpec.EXACTLY ? widthSize : 0;
- int height = heightMode == MeasureSpec.EXACTLY ? heightSize : 0;
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- final int childWidthSpec = makeChildMeasureSpec(maxWidth, lp.width);
- final int childHeightSpec = makeChildMeasureSpec(maxHeight, lp.height);
-
- child.measure(childWidthSpec, childHeightSpec);
-
- width = Math.max(width, Math.min(child.getMeasuredWidth(), widthSize - wPadding));
- height = Math.max(height, Math.min(child.getMeasuredHeight(), heightSize - hPadding));
- }
- setMeasuredDimension(width + wPadding, height + hPadding);
- }
-
- private int makeChildMeasureSpec(int maxSize, int childDimen) {
- final int mode;
- final int size;
- switch (childDimen) {
- case LayoutParams.WRAP_CONTENT:
- mode = MeasureSpec.AT_MOST;
- size = maxSize;
- break;
- case LayoutParams.MATCH_PARENT:
- mode = MeasureSpec.EXACTLY;
- size = maxSize;
- break;
- default:
- mode = MeasureSpec.EXACTLY;
- size = Math.min(maxSize, childDimen);
- break;
- }
- return MeasureSpec.makeMeasureSpec(size, mode);
- }
-
- public static class LayoutParams extends FrameLayout.LayoutParams {
- @ViewDebug.ExportedProperty(category = "layout")
- public int maxWidth;
-
- @ViewDebug.ExportedProperty(category = "layout")
- public int maxHeight;
-
- public LayoutParams(ViewGroup.LayoutParams other) {
- super(other);
- }
-
- public LayoutParams(LayoutParams other) {
- super(other);
-
- maxWidth = other.maxWidth;
- maxHeight = other.maxHeight;
- }
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
-
- final TypedArray a = c.obtainStyledAttributes(attrs,
- R.styleable.KeyguardSecurityViewFlipper_Layout, 0, 0);
- maxWidth = a.getDimensionPixelSize(
- R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxWidth, 0);
- maxHeight = a.getDimensionPixelSize(
- R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxHeight, 0);
- a.recycle();
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewHelper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewHelper.java
deleted file mode 100644
index 3d59f8d..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewHelper.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-
-/**
- * Some common functions that are useful for KeyguardSecurityViews.
- */
-public class KeyguardSecurityViewHelper {
-
- public static void showBouncer(SecurityMessageDisplay securityMessageDisplay,
- final View ecaView, Drawable bouncerFrame, int duration) {
- if (securityMessageDisplay != null) {
- securityMessageDisplay.showBouncer(duration);
- }
- if (ecaView != null) {
- if (duration > 0) {
- Animator anim = ObjectAnimator.ofFloat(ecaView, "alpha", 0f);
- anim.setDuration(duration);
- anim.addListener(new AnimatorListenerAdapter() {
- private boolean mCanceled;
- @Override
- public void onAnimationCancel(Animator animation) {
- // Fail safe and show the emergency button in onAnimationEnd()
- mCanceled = true;
- ecaView.setAlpha(1f);
- }
- @Override
- public void onAnimationEnd(Animator animation) {
- ecaView.setVisibility(mCanceled ? View.VISIBLE : View.INVISIBLE);
- }
- });
- anim.start();
- } else {
- ecaView.setAlpha(0f);
- ecaView.setVisibility(View.INVISIBLE);
- }
- }
- if (bouncerFrame != null) {
- if (duration > 0) {
- Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 0, 255);
- anim.setDuration(duration);
- anim.start();
- } else {
- bouncerFrame.setAlpha(255);
- }
- }
- }
-
- public static void hideBouncer(SecurityMessageDisplay securityMessageDisplay,
- View ecaView, Drawable bouncerFrame, int duration) {
- if (securityMessageDisplay != null) {
- securityMessageDisplay.hideBouncer(duration);
- }
- if (ecaView != null) {
- ecaView.setVisibility(View.VISIBLE);
- if (duration > 0) {
- Animator anim = ObjectAnimator.ofFloat(ecaView, "alpha", 1f);
- anim.setDuration(duration);
- anim.start();
- } else {
- ecaView.setAlpha(1f);
- }
- }
- if (bouncerFrame != null) {
- if (duration > 0) {
- Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 255, 0);
- anim.setDuration(duration);
- anim.start();
- } else {
- bouncerFrame.setAlpha(0);
- }
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
deleted file mode 100644
index 6859042..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.animation.ObjectAnimator;
-import android.app.SearchManager;
-import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Slog;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.multiwaveview.GlowPadView;
-import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
-import com.android.internal.R;
-
-public class KeyguardSelectorView extends LinearLayout implements KeyguardSecurityView {
- private static final boolean DEBUG = KeyguardHostView.DEBUG;
- private static final String TAG = "SecuritySelectorView";
- private static final String ASSIST_ICON_METADATA_NAME =
- "com.android.systemui.action_assist_icon";
-
- private KeyguardSecurityCallback mCallback;
- private GlowPadView mGlowPadView;
- private ObjectAnimator mAnim;
- private View mFadeView;
- private boolean mIsBouncing;
- private boolean mCameraDisabled;
- private boolean mSearchDisabled;
- private LockPatternUtils mLockPatternUtils;
- private SecurityMessageDisplay mSecurityMessageDisplay;
- private Drawable mBouncerFrame;
-
- OnTriggerListener mOnTriggerListener = new OnTriggerListener() {
-
- public void onTrigger(View v, int target) {
- final int resId = mGlowPadView.getResourceIdForTarget(target);
- switch (resId) {
- case com.android.internal.R.drawable.ic_action_assist_generic:
- Intent assistIntent =
- ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
- if (assistIntent != null) {
- mActivityLauncher.launchActivity(assistIntent, false, true, null, null);
- } else {
- Log.w(TAG, "Failed to get intent for assist activity");
- }
- mCallback.userActivity(0);
- break;
-
- case com.android.internal.R.drawable.ic_lockscreen_camera:
- mActivityLauncher.launchCamera(null, null);
- mCallback.userActivity(0);
- break;
-
- case com.android.internal.R.drawable.ic_lockscreen_unlock_phantom:
- case com.android.internal.R.drawable.ic_lockscreen_unlock:
- mCallback.userActivity(0);
- mCallback.dismiss(false);
- break;
- }
- }
-
- public void onReleased(View v, int handle) {
- if (!mIsBouncing) {
- doTransition(mFadeView, 1.0f);
- }
- }
-
- public void onGrabbed(View v, int handle) {
- mCallback.userActivity(0);
- doTransition(mFadeView, 0.0f);
- }
-
- public void onGrabbedStateChange(View v, int handle) {
-
- }
-
- public void onFinishFinalAnimation() {
-
- }
-
- };
-
- KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
-
- @Override
- public void onDevicePolicyManagerStateChanged() {
- updateTargets();
- }
-
- @Override
- public void onSimStateChanged(State simState) {
- updateTargets();
- }
- };
-
- private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
-
- @Override
- KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- LockPatternUtils getLockPatternUtils() {
- return mLockPatternUtils;
- }
-
- @Override
- Context getContext() {
- return mContext;
- }};
-
- public KeyguardSelectorView(Context context) {
- this(context, null);
- }
-
- public KeyguardSelectorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mLockPatternUtils = new LockPatternUtils(getContext());
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
- mGlowPadView.setOnTriggerListener(mOnTriggerListener);
- updateTargets();
-
- mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
- View bouncerFrameView = findViewById(R.id.keyguard_selector_view_frame);
- mBouncerFrame = bouncerFrameView.getBackground();
- }
-
- public void setCarrierArea(View carrierArea) {
- mFadeView = carrierArea;
- }
-
- public boolean isTargetPresent(int resId) {
- return mGlowPadView.getTargetPosition(resId) != -1;
- }
-
- @Override
- public void showUsabilityHint() {
- mGlowPadView.ping();
- }
-
- private void updateTargets() {
- int currentUserHandle = mLockPatternUtils.getCurrentUser();
- DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
- int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUserHandle);
- boolean secureCameraDisabled = mLockPatternUtils.isSecure()
- && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
- boolean cameraDisabledByAdmin = dpm.getCameraDisabled(null, currentUserHandle)
- || secureCameraDisabled;
- final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext());
- boolean disabledBySimState = monitor.isSimLocked();
- boolean cameraTargetPresent =
- isTargetPresent(com.android.internal.R.drawable.ic_lockscreen_camera);
- boolean searchTargetPresent =
- isTargetPresent(com.android.internal.R.drawable.ic_action_assist_generic);
-
- if (cameraDisabledByAdmin) {
- Log.v(TAG, "Camera disabled by Device Policy");
- } else if (disabledBySimState) {
- Log.v(TAG, "Camera disabled by Sim State");
- }
- boolean currentUserSetup = 0 != Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE,
- 0 /*default */,
- currentUserHandle);
- boolean searchActionAvailable =
- ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
- mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent
- || !currentUserSetup;
- mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent
- || !currentUserSetup;
- updateResources();
- }
-
- public void updateResources() {
- // Update the search icon with drawable from the search .apk
- if (!mSearchDisabled) {
- Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
- if (intent != null) {
- // XXX Hack. We need to substitute the icon here but haven't formalized
- // the public API. The "_google" metadata will be going away, so
- // DON'T USE IT!
- ComponentName component = intent.getComponent();
- boolean replaced = mGlowPadView.replaceTargetDrawablesIfPresent(component,
- ASSIST_ICON_METADATA_NAME + "_google",
- com.android.internal.R.drawable.ic_action_assist_generic);
-
- if (!replaced && !mGlowPadView.replaceTargetDrawablesIfPresent(component,
- ASSIST_ICON_METADATA_NAME,
- com.android.internal.R.drawable.ic_action_assist_generic)) {
- Slog.w(TAG, "Couldn't grab icon from package " + component);
- }
- }
- }
-
- mGlowPadView.setEnableTarget(com.android.internal.R.drawable
- .ic_lockscreen_camera, !mCameraDisabled);
- mGlowPadView.setEnableTarget(com.android.internal.R.drawable
- .ic_action_assist_generic, !mSearchDisabled);
- }
-
- void doTransition(View view, float to) {
- if (mAnim != null) {
- mAnim.cancel();
- }
- mAnim = ObjectAnimator.ofFloat(view, "alpha", to);
- mAnim.start();
- }
-
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- }
-
- @Override
- public void reset() {
- mGlowPadView.reset(false);
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onPause() {
- KeyguardUpdateMonitor.getInstance(getContext()).removeCallback(mInfoCallback);
- }
-
- @Override
- public void onResume(int reason) {
- KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mInfoCallback);
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void showBouncer(int duration) {
- mIsBouncing = true;
- KeyguardSecurityViewHelper.
- showBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
- }
-
- @Override
- public void hideBouncer(int duration) {
- mIsBouncing = false;
- KeyguardSecurityViewHelper.
- hideBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
new file mode 100644
index 0000000..874076a
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
@@ -0,0 +1,306 @@
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy.OnKeyguardExitResult;
+
+import com.android.internal.policy.IKeyguardExitCallback;
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.policy.IKeyguardService;
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * A local class that keeps a cache of keyguard state that can be restored in the event
+ * keyguard crashes. It currently also allows runtime-selectable
+ * local or remote instances of keyguard.
+ */
+public class KeyguardServiceDelegate {
+ private static final String KEYGUARD_PACKAGE = "com.android.keyguard";
+ private static final String KEYGUARD_CLASS = "com.android.keyguard.KeyguardService";
+ private static final String TAG = "KeyguardServiceDelegate";
+ private static final boolean DEBUG = true;
+ protected KeyguardServiceWrapper mKeyguardService;
+ private View mScrim; // shown if keyguard crashes
+ private KeyguardState mKeyguardState = new KeyguardState();
+
+ /* package */ static final class KeyguardState {
+ boolean showing;
+ boolean showingAndNotHidden;
+ boolean inputRestricted;
+ boolean hidden;
+ boolean secure;
+ boolean dreaming;
+ boolean systemIsReady;
+ public boolean enabled;
+ public boolean dismissable;
+ public int offReason;
+ public int currentUser;
+ public boolean screenIsOn;
+ };
+
+ public interface ShowListener {
+ public void onShown(IBinder windowToken);
+ }
+
+ // A delegate class to map a particular invocation with a ShowListener object.
+ private final class KeyguardShowDelegate extends IKeyguardShowCallback.Stub {
+ private ShowListener mShowListener;
+
+ KeyguardShowDelegate(ShowListener showListener) {
+ mShowListener = showListener;
+ }
+
+ @Override
+ public void onShown(IBinder windowToken) throws RemoteException {
+ if (DEBUG) Log.v(TAG, "**** SHOWN CALLED ****");
+ if (mShowListener != null) {
+ mShowListener.onShown(windowToken);
+ }
+ hideScrim();
+ }
+ };
+
+ // A delegate class to map a particular invocation with an OnKeyguardExitResult object.
+ private final class KeyguardExitDelegate extends IKeyguardExitCallback.Stub {
+ private OnKeyguardExitResult mOnKeyguardExitResult;
+
+ KeyguardExitDelegate(OnKeyguardExitResult onKeyguardExitResult) {
+ mOnKeyguardExitResult = onKeyguardExitResult;
+ }
+
+ @Override
+ public void onKeyguardExitResult(boolean success) throws RemoteException {
+ if (DEBUG) Log.v(TAG, "**** onKeyguardExitResult(" + success +") CALLED ****");
+ if (mOnKeyguardExitResult != null) {
+ mOnKeyguardExitResult.onKeyguardExitResult(success);
+ }
+ }
+ };
+
+ public KeyguardServiceDelegate(Context context, LockPatternUtils lockPatternUtils) {
+ Intent intent = new Intent();
+ intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS);
+ mScrim = createScrim(context);
+ if (!context.bindServiceAsUser(intent, mKeyguardConnection,
+ Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+ if (DEBUG) Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS);
+ } else {
+ if (DEBUG) Log.v(TAG, "*** Keyguard started");
+ }
+ }
+
+ private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");
+ mKeyguardService = new KeyguardServiceWrapper(
+ IKeyguardService.Stub.asInterface(service));
+ if (mKeyguardState.systemIsReady) {
+ // If the system is ready, it means keyguard crashed and restarted.
+ mKeyguardService.onSystemReady();
+ // This is used to hide the scrim once keyguard displays.
+ mKeyguardService.onScreenTurnedOn(new KeyguardShowDelegate(null));
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
+ mKeyguardService = null;
+ }
+
+ };
+
+ public boolean isShowing() {
+ if (mKeyguardService != null) {
+ mKeyguardState.showing = mKeyguardService.isShowing();
+ }
+ return mKeyguardState.showing;
+ }
+
+ public boolean isShowingAndNotHidden() {
+ if (mKeyguardService != null) {
+ mKeyguardState.showingAndNotHidden = mKeyguardService.isShowingAndNotHidden();
+ }
+ return mKeyguardState.showingAndNotHidden;
+ }
+
+ public boolean isInputRestricted() {
+ if (mKeyguardService != null) {
+ mKeyguardState.inputRestricted = mKeyguardService.isInputRestricted();
+ }
+ return mKeyguardState.inputRestricted;
+ }
+
+ public void verifyUnlock(final OnKeyguardExitResult onKeyguardExitResult) {
+ if (mKeyguardService != null) {
+ mKeyguardService.verifyUnlock(new KeyguardExitDelegate(onKeyguardExitResult));
+ }
+ }
+
+ public void keyguardDone(boolean authenticated, boolean wakeup) {
+ if (mKeyguardService != null) {
+ mKeyguardService.keyguardDone(authenticated, wakeup);
+ }
+ }
+
+ public void setHidden(boolean isHidden) {
+ if (mKeyguardService != null) {
+ mKeyguardService.setHidden(isHidden);
+ }
+ mKeyguardState.hidden = isHidden;
+ }
+
+ public void dismiss() {
+ if (mKeyguardService != null) {
+ mKeyguardService.dismiss();
+ }
+ }
+
+ public boolean isSecure() {
+ if (mKeyguardService != null) {
+ mKeyguardState.secure = mKeyguardService.isSecure();
+ }
+ return mKeyguardState.secure;
+ }
+
+ public void onDreamingStarted() {
+ if (mKeyguardService != null) {
+ mKeyguardService.onDreamingStarted();
+ }
+ mKeyguardState.dreaming = true;
+ }
+
+ public void onDreamingStopped() {
+ if (mKeyguardService != null) {
+ mKeyguardService.onDreamingStopped();
+ }
+ mKeyguardState.dreaming = false;
+ }
+
+ public void onScreenTurnedOn(final ShowListener showListener) {
+ if (mKeyguardService != null) {
+ if (DEBUG) Log.v(TAG, "onScreenTurnedOn(showListener = " + showListener + ")");
+ mKeyguardService.onScreenTurnedOn(new KeyguardShowDelegate(showListener));
+ } else {
+ // try again when we establish a connection
+ Slog.w(TAG, "onScreenTurnedOn(): no keyguard service!");
+ // This shouldn't happen, but if it does, invoke the listener immediately
+ // to avoid a dark screen...
+ showListener.onShown(null);
+ }
+ mKeyguardState.screenIsOn = true;
+ }
+
+ public void onScreenTurnedOff(int why) {
+ if (mKeyguardService != null) {
+ mKeyguardService.onScreenTurnedOff(why);
+ }
+ mKeyguardState.offReason = why;
+ mKeyguardState.screenIsOn = false;
+ }
+
+ public void setKeyguardEnabled(boolean enabled) {
+ if (mKeyguardService != null) {
+ mKeyguardService.setKeyguardEnabled(enabled);
+ }
+ mKeyguardState.enabled = enabled;
+ }
+
+ public boolean isDismissable() {
+ if (mKeyguardService != null) {
+ mKeyguardState.dismissable = mKeyguardService.isDismissable();
+ }
+ return mKeyguardState.dismissable;
+ }
+
+ public void onSystemReady() {
+ if (mKeyguardService != null) {
+ mKeyguardService.onSystemReady();
+ } else {
+ if (DEBUG) Log.v(TAG, "onSystemReady() called before keyguard service was ready");
+ mKeyguardState.systemIsReady = true;
+ }
+ }
+
+ public void doKeyguardTimeout(Bundle options) {
+ if (mKeyguardService != null) {
+ mKeyguardService.doKeyguardTimeout(options);
+ }
+ }
+
+ public void showAssistant() {
+ if (mKeyguardService != null) {
+ mKeyguardService.showAssistant();
+ }
+ }
+
+ public void setCurrentUser(int newUserId) {
+ if (mKeyguardService != null) {
+ mKeyguardService.setCurrentUser(newUserId);
+ }
+ mKeyguardState.currentUser = newUserId;
+ }
+
+ private static final View createScrim(Context context) {
+ View view = new View(context);
+
+ int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+ | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER
+ ;
+
+ final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
+ final int type = WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM;
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);
+ lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+ lp.setTitle("KeyguardScrim");
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ wm.addView(view, lp);
+ view.setVisibility(View.GONE);
+ // Disable pretty much everything in statusbar until keyguard comes back and we know
+ // the state of the world.
+ view.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME
+ | View.STATUS_BAR_DISABLE_BACK
+ | View.STATUS_BAR_DISABLE_RECENT
+ | View.STATUS_BAR_DISABLE_EXPAND
+ | View.STATUS_BAR_DISABLE_SEARCH);
+ return view;
+ }
+
+ public void showScrim() {
+ mScrim.post(new Runnable() {
+ @Override
+ public void run() {
+ mScrim.setVisibility(View.VISIBLE);
+ }
+ });
+ }
+
+ public void hideScrim() {
+ mScrim.post(new Runnable() {
+ @Override
+ public void run() {
+ mScrim.setVisibility(View.GONE);
+ }
+ });
+ }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
new file mode 100644
index 0000000..6b9c7df
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.policy.IKeyguardExitCallback;
+import com.android.internal.policy.IKeyguardService;
+
+/**
+ * A wrapper class for KeyguardService. It implements IKeyguardService to ensure the interface
+ * remains consistent.
+ *
+ */
+public class KeyguardServiceWrapper implements IKeyguardService {
+ private IKeyguardService mService;
+ private String TAG = "KeyguardServiceWrapper";
+
+ public KeyguardServiceWrapper(IKeyguardService service) {
+ mService = service;
+ }
+
+ public boolean isShowing() {
+ try {
+ return mService.isShowing();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ return false;
+ }
+
+ public boolean isSecure() {
+ try {
+ return mService.isSecure();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ return false; // TODO cache state
+ }
+
+ public boolean isShowingAndNotHidden() {
+ try {
+ return mService.isShowingAndNotHidden();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ return false; // TODO cache state
+ }
+
+ public boolean isInputRestricted() {
+ try {
+ return mService.isInputRestricted();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ return false; // TODO cache state
+ }
+
+ public boolean isDismissable() {
+ try {
+ return mService.isDismissable();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ return true; // TODO cache state
+ }
+
+ public void verifyUnlock(IKeyguardExitCallback callback) {
+ try {
+ mService.verifyUnlock(callback);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void keyguardDone(boolean authenticated, boolean wakeup) {
+ try {
+ mService.keyguardDone(authenticated, wakeup);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void setHidden(boolean isHidden) {
+ try {
+ mService.setHidden(isHidden);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void dismiss() {
+ try {
+ mService.dismiss();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void onDreamingStarted() {
+ try {
+ mService.onDreamingStarted();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void onDreamingStopped() {
+ try {
+ mService.onDreamingStopped();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void onScreenTurnedOff(int reason) {
+ try {
+ mService.onScreenTurnedOff(reason);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void onScreenTurnedOn(IKeyguardShowCallback result) {
+ try {
+ mService.onScreenTurnedOn(result);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void setKeyguardEnabled(boolean enabled) {
+ try {
+ mService.setKeyguardEnabled(enabled);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void onSystemReady() {
+ try {
+ mService.onSystemReady();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void doKeyguardTimeout(Bundle options) {
+ try {
+ mService.doKeyguardTimeout(options);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void setCurrentUser(int userId) {
+ try {
+ mService.setCurrentUser(userId);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void showAssistant() {
+ try {
+ mService.showAssistant();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mService.asBinder();
+ }
+
+}
\ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
deleted file mode 100644
index ab364ee..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import com.android.internal.telephony.ITelephony;
-
-import android.content.Context;
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextWatcher;
-import android.text.method.DigitsKeyListener;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.TextView.OnEditorActionListener;
-
-import com.android.internal.R;
-
-/**
- * Displays a PIN pad for unlocking.
- */
-public class KeyguardSimPinView extends KeyguardAbsKeyInputView
- implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
-
- private ProgressDialog mSimUnlockProgressDialog = null;
- private volatile boolean mSimCheckInProgress;
-
- public KeyguardSimPinView(Context context) {
- this(context, null);
- }
-
- public KeyguardSimPinView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void resetState() {
- mSecurityMessageDisplay.setMessage(R.string.kg_sim_pin_instructions, true);
- mPasswordEntry.setEnabled(true);
- }
-
- @Override
- protected int getPasswordTextViewId() {
- return R.id.pinEntry;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- final View ok = findViewById(R.id.key_enter);
- if (ok != null) {
- ok.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- doHapticKeyClick();
- verifyPasswordAndUnlock();
- }
- });
- }
-
- // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
- // not a separate view
- View pinDelete = findViewById(R.id.delete_button);
- if (pinDelete != null) {
- pinDelete.setVisibility(View.VISIBLE);
- pinDelete.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- CharSequence str = mPasswordEntry.getText();
- if (str.length() > 0) {
- mPasswordEntry.setText(str.subSequence(0, str.length()-1));
- }
- doHapticKeyClick();
- }
- });
- pinDelete.setOnLongClickListener(new View.OnLongClickListener() {
- public boolean onLongClick(View v) {
- mPasswordEntry.setText("");
- doHapticKeyClick();
- return true;
- }
- });
- }
-
- mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
- | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
-
- mPasswordEntry.requestFocus();
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public void onPause() {
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPin extends Thread {
- private final String mPin;
-
- protected CheckSimPin(String pin) {
- mPin = pin;
- }
-
- abstract void onSimCheckResponse(boolean success);
-
- @Override
- public void run() {
- try {
- final boolean result = ITelephony.Stub.asInterface(ServiceManager
- .checkService("phone")).supplyPin(mPin);
- post(new Runnable() {
- public void run() {
- onSimCheckResponse(result);
- }
- });
- } catch (RemoteException e) {
- post(new Runnable() {
- public void run() {
- onSimCheckResponse(false);
- }
- });
- }
- }
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mContext);
- mSimUnlockProgressDialog.setMessage(
- mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- if (!(mContext instanceof Activity)) {
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- }
- return mSimUnlockProgressDialog;
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- String entry = mPasswordEntry.getText().toString();
-
- if (entry.length() < 4) {
- // otherwise, display a message to the user, and don't submit.
- mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint, true);
- mPasswordEntry.setText("");
- mCallback.userActivity(0);
- return;
- }
-
- getSimUnlockProgressDialog().show();
-
- if (!mSimCheckInProgress) {
- mSimCheckInProgress = true; // there should be only one
- new CheckSimPin(mPasswordEntry.getText().toString()) {
- void onSimCheckResponse(final boolean success) {
- post(new Runnable() {
- public void run() {
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- if (success) {
- // before closing the keyguard, report back that the sim is unlocked
- // so it knows right away.
- KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
- mCallback.dismiss(true);
- } else {
- mSecurityMessageDisplay.setMessage
- (R.string.kg_password_wrong_pin_code, true);
- mPasswordEntry.setText("");
- }
- mCallback.userActivity(0);
- mSimCheckInProgress = false;
- }
- });
- }
- }.start();
- }
- }
-}
-
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
deleted file mode 100644
index e5b4b73..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextWatcher;
-import android.text.method.DigitsKeyListener;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.TextView.OnEditorActionListener;
-
-import com.android.internal.telephony.ITelephony;
-
-import com.android.internal.R;
-
-/**
- * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
- */
-public class KeyguardSimPukView extends KeyguardAbsKeyInputView
- implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
-
- private ProgressDialog mSimUnlockProgressDialog = null;
- private volatile boolean mCheckInProgress;
- private String mPukText;
- private String mPinText;
- private StateMachine mStateMachine = new StateMachine();
-
- private class StateMachine {
- final int ENTER_PUK = 0;
- final int ENTER_PIN = 1;
- final int CONFIRM_PIN = 2;
- final int DONE = 3;
- private int state = ENTER_PUK;
-
- public void next() {
- int msg = 0;
- if (state == ENTER_PUK) {
- if (checkPuk()) {
- state = ENTER_PIN;
- msg = R.string.kg_puk_enter_pin_hint;
- } else {
- msg = R.string.kg_invalid_sim_puk_hint;
- }
- } else if (state == ENTER_PIN) {
- if (checkPin()) {
- state = CONFIRM_PIN;
- msg = R.string.kg_enter_confirm_pin_hint;
- } else {
- msg = R.string.kg_invalid_sim_pin_hint;
- }
- } else if (state == CONFIRM_PIN) {
- if (confirmPin()) {
- state = DONE;
- msg =
- com.android.internal.R.string.lockscreen_sim_unlock_progress_dialog_message;
- updateSim();
- } else {
- state = ENTER_PIN; // try again?
- msg = R.string.kg_invalid_confirm_pin_hint;
- }
- }
- mPasswordEntry.setText(null);
- if (msg != 0) {
- mSecurityMessageDisplay.setMessage(msg, true);
- }
- }
-
- void reset() {
- mPinText="";
- mPukText="";
- state = ENTER_PUK;
- mSecurityMessageDisplay.setMessage(R.string.kg_puk_enter_puk_hint, true);
- mPasswordEntry.requestFocus();
- }
- }
-
- public KeyguardSimPukView(Context context) {
- this(context, null);
- }
-
- public KeyguardSimPukView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void resetState() {
- mStateMachine.reset();
- mPasswordEntry.setEnabled(true);
- }
-
- @Override
- protected int getPasswordTextViewId() {
- return R.id.pinEntry;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- final View ok = findViewById(R.id.key_enter);
- if (ok != null) {
- ok.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- doHapticKeyClick();
- verifyPasswordAndUnlock();
- }
- });
- }
-
- // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
- // not a separate view
- View pinDelete = findViewById(R.id.delete_button);
- if (pinDelete != null) {
- pinDelete.setVisibility(View.VISIBLE);
- pinDelete.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- CharSequence str = mPasswordEntry.getText();
- if (str.length() > 0) {
- mPasswordEntry.setText(str.subSequence(0, str.length()-1));
- }
- doHapticKeyClick();
- }
- });
- pinDelete.setOnLongClickListener(new View.OnLongClickListener() {
- public boolean onLongClick(View v) {
- mPasswordEntry.setText("");
- doHapticKeyClick();
- return true;
- }
- });
- }
-
- mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
- | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
-
- mPasswordEntry.requestFocus();
-
- mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public void onPause() {
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPuk extends Thread {
-
- private final String mPin, mPuk;
-
- protected CheckSimPuk(String puk, String pin) {
- mPuk = puk;
- mPin = pin;
- }
-
- abstract void onSimLockChangedResponse(boolean success);
-
- @Override
- public void run() {
- try {
- final boolean result = ITelephony.Stub.asInterface(ServiceManager
- .checkService("phone")).supplyPuk(mPuk, mPin);
-
- post(new Runnable() {
- public void run() {
- onSimLockChangedResponse(result);
- }
- });
- } catch (RemoteException e) {
- post(new Runnable() {
- public void run() {
- onSimLockChangedResponse(false);
- }
- });
- }
- }
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mContext);
- mSimUnlockProgressDialog.setMessage(
- mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- if (!(mContext instanceof Activity)) {
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- }
- return mSimUnlockProgressDialog;
- }
-
- private boolean checkPuk() {
- // make sure the puk is at least 8 digits long.
- if (mPasswordEntry.getText().length() >= 8) {
- mPukText = mPasswordEntry.getText().toString();
- return true;
- }
- return false;
- }
-
- private boolean checkPin() {
- // make sure the PIN is between 4 and 8 digits
- int length = mPasswordEntry.getText().length();
- if (length >= 4 && length <= 8) {
- mPinText = mPasswordEntry.getText().toString();
- return true;
- }
- return false;
- }
-
- public boolean confirmPin() {
- return mPinText.equals(mPasswordEntry.getText().toString());
- }
-
- private void updateSim() {
- getSimUnlockProgressDialog().show();
-
- if (!mCheckInProgress) {
- mCheckInProgress = true;
- new CheckSimPuk(mPukText, mPinText) {
- void onSimLockChangedResponse(final boolean success) {
- post(new Runnable() {
- public void run() {
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- if (success) {
- mCallback.dismiss(true);
- } else {
- mStateMachine.reset();
- mSecurityMessageDisplay.setMessage(R.string.kg_invalid_puk, true);
- }
- mCheckInProgress = false;
- }
- });
- }
- }.start();
- }
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- mStateMachine.next();
- }
-}
-
-
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
deleted file mode 100644
index d938cec..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
-import android.widget.GridLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-import libcore.icu.ICU;
-
-public class KeyguardStatusView extends GridLayout {
- private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
- private static final String TAG = "KeyguardStatusView";
-
- public static final int LOCK_ICON = 0; // R.drawable.ic_lock_idle_lock;
- public static final int ALARM_ICON = com.android.internal.R.drawable.ic_lock_idle_alarm;
- public static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
- public static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
-
- private SimpleDateFormat mDateFormat;
- private LockPatternUtils mLockPatternUtils;
-
- private TextView mDateView;
- private TextView mAlarmStatusView;
- private ClockView mClockView;
-
- private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
-
- @Override
- public void onTimeChanged() {
- refresh();
- }
-
- @Override
- void onKeyguardVisibilityChanged(boolean showing) {
- if (showing) {
- if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
- refresh();
- }
- };
- };
-
- public KeyguardStatusView(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardStatusView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardStatusView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- Resources res = getContext().getResources();
- final Locale locale = Locale.getDefault();
- final String datePattern =
- res.getString(com.android.internal.R.string.system_ui_date_pattern);
- final String bestFormat = ICU.getBestDateTimePattern(datePattern, locale.toString());
- mDateFormat = new SimpleDateFormat(bestFormat, locale);
- mDateView = (TextView) findViewById(R.id.date);
- mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
- mClockView = (ClockView) findViewById(R.id.clock_view);
- mLockPatternUtils = new LockPatternUtils(getContext());
-
- // Use custom font in mDateView
- mDateView.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD);
-
- // Required to get Marquee to work.
- final View marqueeViews[] = { mDateView, mAlarmStatusView };
- for (int i = 0; i < marqueeViews.length; i++) {
- View v = marqueeViews[i];
- if (v == null) {
- throw new RuntimeException("Can't find widget at index " + i);
- }
- v.setSelected(true);
- }
- refresh();
- }
-
- protected void refresh() {
- mClockView.updateTime();
- refreshDate();
- refreshAlarmStatus(); // might as well
- }
-
- void refreshAlarmStatus() {
- // Update Alarm status
- String nextAlarm = mLockPatternUtils.getNextAlarm();
- if (!TextUtils.isEmpty(nextAlarm)) {
- maybeSetUpperCaseText(mAlarmStatusView, nextAlarm);
- mAlarmStatusView.setCompoundDrawablesWithIntrinsicBounds(ALARM_ICON, 0, 0, 0);
- mAlarmStatusView.setVisibility(View.VISIBLE);
- } else {
- mAlarmStatusView.setVisibility(View.GONE);
- }
- }
-
- void refreshDate() {
- maybeSetUpperCaseText(mDateView, mDateFormat.format(new Date()));
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
- }
-
- public int getAppWidgetId() {
- return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
- }
-
- private void maybeSetUpperCaseText(TextView textView, CharSequence text) {
- if (KeyguardViewManager.USE_UPPER_CASE) {
- textView.setText(text != null ? text.toString().toUpperCase() : null);
- } else {
- textView.setText(text);
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
deleted file mode 100644
index 5e3b7da..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.media.AudioManager;
-import android.media.IRemoteControlDisplay;
-import android.media.MediaMetadataRetriever;
-import android.media.RemoteControlClient;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.text.Spannable;
-import android.text.TextUtils;
-import android.text.style.ForegroundColorSpan;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.lang.ref.WeakReference;
-/**
- * This is the widget responsible for showing music controls in keyguard.
- */
-public class KeyguardTransportControlView extends FrameLayout implements OnClickListener {
-
- private static final int MSG_UPDATE_STATE = 100;
- private static final int MSG_SET_METADATA = 101;
- private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
- private static final int MSG_SET_ARTWORK = 103;
- private static final int MSG_SET_GENERATION_ID = 104;
- private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
- protected static final boolean DEBUG = false;
- protected static final String TAG = "TransportControlView";
-
- private ImageView mAlbumArt;
- private TextView mTrackTitle;
- private ImageView mBtnPrev;
- private ImageView mBtnPlay;
- private ImageView mBtnNext;
- private int mClientGeneration;
- private Metadata mMetadata = new Metadata();
- private boolean mAttached;
- private PendingIntent mClientIntent;
- private int mTransportControlFlags;
- private int mCurrentPlayState;
- private AudioManager mAudioManager;
- private IRemoteControlDisplayWeak mIRCD;
-
- /**
- * The metadata which should be populated into the view once we've been attached
- */
- private Bundle mPopulateMetadataWhenAttached = null;
-
- // This handler is required to ensure messages from IRCD are handled in sequence and on
- // the UI thread.
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_STATE:
- if (mClientGeneration == msg.arg1) updatePlayPauseState(msg.arg2);
- break;
-
- case MSG_SET_METADATA:
- if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj);
- break;
-
- case MSG_SET_TRANSPORT_CONTROLS:
- if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2);
- break;
-
- case MSG_SET_ARTWORK:
- if (mClientGeneration == msg.arg1) {
- if (mMetadata.bitmap != null) {
- mMetadata.bitmap.recycle();
- }
- mMetadata.bitmap = (Bitmap) msg.obj;
- mAlbumArt.setImageBitmap(mMetadata.bitmap);
- }
- break;
-
- case MSG_SET_GENERATION_ID:
- if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
- mClientGeneration = msg.arg1;
- mClientIntent = (PendingIntent) msg.obj;
- break;
-
- }
- }
- };
-
- /**
- * This class is required to have weak linkage to the current TransportControlView
- * because the remote process can hold a strong reference to this binder object and
- * we can't predict when it will be GC'd in the remote process. Without this code, it
- * would allow a heavyweight object to be held on this side of the binder when there's
- * no requirement to run a GC on the other side.
- */
- private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub {
- private WeakReference<Handler> mLocalHandler;
-
- IRemoteControlDisplayWeak(Handler handler) {
- mLocalHandler = new WeakReference<Handler>(handler);
- }
-
- public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
- long currentPosMs, float speed) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
- }
- }
-
- public void setMetadata(int generationId, Bundle metadata) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
- }
- }
-
- public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags)
- .sendToTarget();
- }
- }
-
- public void setArtwork(int generationId, Bitmap bitmap) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
- }
- }
-
- public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
- handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
- }
- }
-
- public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
- boolean clearing) throws RemoteException {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_GENERATION_ID,
- clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
- }
- }
- };
-
- public KeyguardTransportControlView(Context context, AttributeSet attrs) {
- super(context, attrs);
- if (DEBUG) Log.v(TAG, "Create TCV " + this);
- mAudioManager = new AudioManager(mContext);
- mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
- mIRCD = new IRemoteControlDisplayWeak(mHandler);
- }
-
- private void updateTransportControls(int transportControlFlags) {
- mTransportControlFlags = transportControlFlags;
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
- mTrackTitle = (TextView) findViewById(R.id.title);
- mTrackTitle.setSelected(true); // enable marquee
- mAlbumArt = (ImageView) findViewById(R.id.albumart);
- mBtnPrev = (ImageView) findViewById(R.id.btn_prev);
- mBtnPlay = (ImageView) findViewById(R.id.btn_play);
- mBtnNext = (ImageView) findViewById(R.id.btn_next);
- final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext };
- for (View view : buttons) {
- view.setOnClickListener(this);
- }
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (DEBUG) Log.v(TAG, "onAttachToWindow()");
- if (mPopulateMetadataWhenAttached != null) {
- updateMetadata(mPopulateMetadataWhenAttached);
- mPopulateMetadataWhenAttached = null;
- }
- if (!mAttached) {
- if (DEBUG) Log.v(TAG, "Registering TCV " + this);
- mAudioManager.registerRemoteControlDisplay(mIRCD);
- }
- mAttached = true;
- }
-
- @Override
- protected void onSizeChanged (int w, int h, int oldw, int oldh) {
- if (mAttached) {
- int dim = Math.min(512, Math.max(w, h));
- if (DEBUG) Log.v(TAG, "TCV uses bitmap size=" + dim);
- mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
- }
- }
-
- @Override
- public void onDetachedFromWindow() {
- if (DEBUG) Log.v(TAG, "onDetachFromWindow()");
- super.onDetachedFromWindow();
- if (mAttached) {
- if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
- mAudioManager.unregisterRemoteControlDisplay(mIRCD);
- }
- mAttached = false;
- }
-
- class Metadata {
- private String artist;
- private String trackTitle;
- private String albumTitle;
- private Bitmap bitmap;
-
- public String toString() {
- return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + albumTitle + "]";
- }
- }
-
- private String getMdString(Bundle data, int id) {
- return data.getString(Integer.toString(id));
- }
-
- private void updateMetadata(Bundle data) {
- if (mAttached) {
- mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
- mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
- mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
- populateMetadata();
- } else {
- mPopulateMetadataWhenAttached = data;
- }
- }
-
- /**
- * Populates the given metadata into the view
- */
- private void populateMetadata() {
- StringBuilder sb = new StringBuilder();
- int trackTitleLength = 0;
- if (!TextUtils.isEmpty(mMetadata.trackTitle)) {
- sb.append(mMetadata.trackTitle);
- trackTitleLength = mMetadata.trackTitle.length();
- }
- if (!TextUtils.isEmpty(mMetadata.artist)) {
- if (sb.length() != 0) {
- sb.append(" - ");
- }
- sb.append(mMetadata.artist);
- }
- if (!TextUtils.isEmpty(mMetadata.albumTitle)) {
- if (sb.length() != 0) {
- sb.append(" - ");
- }
- sb.append(mMetadata.albumTitle);
- }
- mTrackTitle.setText(sb.toString(), TextView.BufferType.SPANNABLE);
- Spannable str = (Spannable) mTrackTitle.getText();
- if (trackTitleLength != 0) {
- str.setSpan(new ForegroundColorSpan(0xffffffff), 0, trackTitleLength,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- trackTitleLength++;
- }
- if (sb.length() > trackTitleLength) {
- str.setSpan(new ForegroundColorSpan(0x7fffffff), trackTitleLength, sb.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
-
- mAlbumArt.setImageBitmap(mMetadata.bitmap);
- final int flags = mTransportControlFlags;
- setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
- setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
- setVisibilityBasedOnFlag(mBtnPlay, flags,
- RemoteControlClient.FLAG_KEY_MEDIA_PLAY
- | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
- | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
- | RemoteControlClient.FLAG_KEY_MEDIA_STOP);
-
- updatePlayPauseState(mCurrentPlayState);
- }
-
- private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
- if ((flags & flag) != 0) {
- view.setVisibility(View.VISIBLE);
- } else {
- view.setVisibility(View.GONE);
- }
- }
-
- private void updatePlayPauseState(int state) {
- if (DEBUG) Log.v(TAG,
- "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state);
- if (state == mCurrentPlayState) {
- return;
- }
- final int imageResId;
- final int imageDescId;
- switch (state) {
- case RemoteControlClient.PLAYSTATE_ERROR:
- imageResId = com.android.internal.R.drawable.stat_sys_warning;
- // TODO use more specific image description string for warning, but here the "play"
- // message is still valid because this button triggers a play command.
- imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
- break;
-
- case RemoteControlClient.PLAYSTATE_PLAYING:
- imageResId = com.android.internal.R.drawable.ic_media_pause;
- imageDescId = com.android.internal.R.string.lockscreen_transport_pause_description;
- break;
-
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- imageResId = com.android.internal.R.drawable.ic_media_stop;
- imageDescId = com.android.internal.R.string.lockscreen_transport_stop_description;
- break;
-
- case RemoteControlClient.PLAYSTATE_PAUSED:
- default:
- imageResId = com.android.internal.R.drawable.ic_media_play;
- imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
- break;
- }
- mBtnPlay.setImageResource(imageResId);
- mBtnPlay.setContentDescription(getResources().getString(imageDescId));
- mCurrentPlayState = state;
- }
-
- static class SavedState extends BaseSavedState {
- boolean clientPresent;
-
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- private SavedState(Parcel in) {
- super(in);
- this.clientPresent = in.readInt() != 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(this.clientPresent ? 1 : 0);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- public void onClick(View v) {
- int keyCode = -1;
- if (v == mBtnPrev) {
- keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
- } else if (v == mBtnNext) {
- keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
- } else if (v == mBtnPlay) {
- keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
-
- }
- if (keyCode != -1) {
- sendMediaButtonClick(keyCode);
- }
- }
-
- private void sendMediaButtonClick(int keyCode) {
- if (mClientIntent == null) {
- // Shouldn't be possible because this view should be hidden in this case.
- Log.e(TAG, "sendMediaButtonClick(): No client is currently registered");
- return;
- }
- // use the registered PendingIntent that will be processed by the registered
- // media button event receiver, which is the component of mClientIntent
- KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
- Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- try {
- mClientIntent.send(getContext(), 0, intent);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending intent for media button down: "+e);
- e.printStackTrace();
- }
-
- keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
- intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- try {
- mClientIntent.send(getContext(), 0, intent);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending intent for media button up: "+e);
- e.printStackTrace();
- }
- }
-
- public boolean providesClock() {
- return false;
- }
-
- private boolean wasPlayingRecently(int state, long stateChangeTimeMs) {
- switch (state) {
- case RemoteControlClient.PLAYSTATE_PLAYING:
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- case RemoteControlClient.PLAYSTATE_REWINDING:
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- // actively playing or about to play
- return true;
- case RemoteControlClient.PLAYSTATE_NONE:
- return false;
- case RemoteControlClient.PLAYSTATE_STOPPED:
- case RemoteControlClient.PLAYSTATE_PAUSED:
- case RemoteControlClient.PLAYSTATE_ERROR:
- // we have stopped playing, check how long ago
- if (DEBUG) {
- if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) {
- Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently");
- } else {
- Log.v(TAG, "wasPlayingRecently: time > TIMEOUT");
- }
- }
- return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS);
- default:
- Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()");
- return false;
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
deleted file mode 100644
index 5a64586..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ /dev/null
@@ -1,983 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.ActivityManagerNative;
-import android.app.IUserSwitchObserver;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.graphics.Bitmap;
-
-import static android.os.BatteryManager.BATTERY_STATUS_FULL;
-import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
-import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
-import static android.os.BatteryManager.EXTRA_STATUS;
-import static android.os.BatteryManager.EXTRA_PLUGGED;
-import static android.os.BatteryManager.EXTRA_LEVEL;
-import static android.os.BatteryManager.EXTRA_HEALTH;
-import android.media.AudioManager;
-import android.media.IRemoteControlDisplay;
-import android.os.BatteryManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IRemoteCallback;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.TelephonyIntents;
-
-import android.telephony.TelephonyManager;
-import android.util.Log;
-import com.android.internal.R;
-import com.google.android.collect.Lists;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-/**
- * Watches for updates that may be interesting to the keyguard, and provides
- * the up to date information as well as a registration for callbacks that care
- * to be updated.
- *
- * Note: under time crunch, this has been extended to include some stuff that
- * doesn't really belong here. see {@link #handleBatteryUpdate} where it shutdowns
- * the device, and {@link #getFailedUnlockAttempts()}, {@link #reportFailedAttempt()}
- * and {@link #clearFailedUnlockAttempts()}. Maybe we should rename this 'KeyguardContext'...
- */
-public class KeyguardUpdateMonitor {
-
- private static final String TAG = "KeyguardUpdateMonitor";
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_SIM_STATES = DEBUG || false;
- private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 3;
- private static final int LOW_BATTERY_THRESHOLD = 20;
-
- // Callback messages
- private static final int MSG_TIME_UPDATE = 301;
- private static final int MSG_BATTERY_UPDATE = 302;
- private static final int MSG_CARRIER_INFO_UPDATE = 303;
- private static final int MSG_SIM_STATE_CHANGE = 304;
- private static final int MSG_RINGER_MODE_CHANGED = 305;
- private static final int MSG_PHONE_STATE_CHANGED = 306;
- private static final int MSG_CLOCK_VISIBILITY_CHANGED = 307;
- private static final int MSG_DEVICE_PROVISIONED = 308;
- private static final int MSG_DPM_STATE_CHANGED = 309;
- private static final int MSG_USER_SWITCHING = 310;
- private static final int MSG_USER_REMOVED = 311;
- private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
- protected static final int MSG_BOOT_COMPLETED = 313;
- private static final int MSG_USER_SWITCH_COMPLETE = 314;
- private static final int MSG_SET_CURRENT_CLIENT_ID = 315;
- protected static final int MSG_SET_PLAYBACK_STATE = 316;
- protected static final int MSG_USER_INFO_CHANGED = 317;
-
-
- private static KeyguardUpdateMonitor sInstance;
-
- private final Context mContext;
-
- // Telephony state
- private IccCardConstants.State mSimState = IccCardConstants.State.READY;
- private CharSequence mTelephonyPlmn;
- private CharSequence mTelephonySpn;
- private int mRingMode;
- private int mPhoneState;
- private boolean mKeyguardIsVisible;
- private boolean mBootCompleted;
-
- // Device provisioning state
- private boolean mDeviceProvisioned;
-
- // Battery status
- private BatteryStatus mBatteryStatus;
-
- // Password attempts
- private int mFailedAttempts = 0;
- private int mFailedBiometricUnlockAttempts = 0;
-
- private boolean mAlternateUnlockEnabled;
-
- private boolean mClockVisible;
-
- private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
- mCallbacks = Lists.newArrayList();
- private ContentObserver mDeviceProvisionedObserver;
-
- private boolean mSwitchingUser;
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_TIME_UPDATE:
- handleTimeUpdate();
- break;
- case MSG_BATTERY_UPDATE:
- handleBatteryUpdate((BatteryStatus) msg.obj);
- break;
- case MSG_CARRIER_INFO_UPDATE:
- handleCarrierInfoUpdate();
- break;
- case MSG_SIM_STATE_CHANGE:
- handleSimStateChange((SimArgs) msg.obj);
- break;
- case MSG_RINGER_MODE_CHANGED:
- handleRingerModeChange(msg.arg1);
- break;
- case MSG_PHONE_STATE_CHANGED:
- handlePhoneStateChanged((String)msg.obj);
- break;
- case MSG_CLOCK_VISIBILITY_CHANGED:
- handleClockVisibilityChanged();
- break;
- case MSG_DEVICE_PROVISIONED:
- handleDeviceProvisioned();
- break;
- case MSG_DPM_STATE_CHANGED:
- handleDevicePolicyManagerStateChanged();
- break;
- case MSG_USER_SWITCHING:
- handleUserSwitching(msg.arg1, (IRemoteCallback)msg.obj);
- break;
- case MSG_USER_SWITCH_COMPLETE:
- handleUserSwitchComplete(msg.arg1);
- break;
- case MSG_USER_REMOVED:
- handleUserRemoved(msg.arg1);
- break;
- case MSG_KEYGUARD_VISIBILITY_CHANGED:
- handleKeyguardVisibilityChanged(msg.arg1);
- break;
- case MSG_BOOT_COMPLETED:
- handleBootCompleted();
- break;
- case MSG_SET_CURRENT_CLIENT_ID:
- handleSetGenerationId(msg.arg1, msg.arg2 != 0, (PendingIntent) msg.obj);
- break;
- case MSG_SET_PLAYBACK_STATE:
- handleSetPlaybackState(msg.arg1, msg.arg2, (Long) msg.obj);
- break;
- case MSG_USER_INFO_CHANGED:
- handleUserInfoChanged(msg.arg1);
- break;
- }
- }
- };
-
- private AudioManager mAudioManager;
-
- static class DisplayClientState {
- public int clientGeneration;
- public boolean clearing;
- public PendingIntent intent;
- public int playbackState;
- public long playbackEventTime;
- }
-
- private DisplayClientState mDisplayClientState = new DisplayClientState();
-
- /**
- * This currently implements the bare minimum required to enable showing and hiding
- * KeyguardTransportControl. There's a lot of client state to maintain which is why
- * KeyguardTransportControl maintains an independent connection while it's showing.
- */
- private final IRemoteControlDisplay.Stub mRemoteControlDisplay =
- new IRemoteControlDisplay.Stub() {
-
- public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
- long currentPosMs, float speed) {
- Message msg = mHandler.obtainMessage(MSG_SET_PLAYBACK_STATE,
- generationId, state, stateChangeTimeMs);
- mHandler.sendMessage(msg);
- }
-
- public void setMetadata(int generationId, Bundle metadata) {
-
- }
-
- public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
-
- }
-
- public void setArtwork(int generationId, Bitmap bitmap) {
-
- }
-
- public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
-
- }
-
- public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
- boolean clearing) throws RemoteException {
- Message msg = mHandler.obtainMessage(MSG_SET_CURRENT_CLIENT_ID,
- clientGeneration, (clearing ? 1 : 0), mediaIntent);
- mHandler.sendMessage(msg);
- }
- };
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (DEBUG) Log.d(TAG, "received broadcast " + action);
-
- if (Intent.ACTION_TIME_TICK.equals(action)
- || Intent.ACTION_TIME_CHANGED.equals(action)
- || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_TIME_UPDATE));
- } else if (TelephonyIntents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
- mTelephonyPlmn = getTelephonyPlmnFrom(intent);
- mTelephonySpn = getTelephonySpnFrom(intent);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CARRIER_INFO_UPDATE));
- } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
- final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
- final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
- final int level = intent.getIntExtra(EXTRA_LEVEL, 0);
- final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
- final Message msg = mHandler.obtainMessage(
- MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health));
- mHandler.sendMessage(msg);
- } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
- if (DEBUG_SIM_STATES) {
- Log.v(TAG, "action " + action + " state" +
- intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE));
- }
- mHandler.sendMessage(mHandler.obtainMessage(
- MSG_SIM_STATE_CHANGE, SimArgs.fromIntent(intent)));
- } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED,
- intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
- } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
- String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
- } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
- .equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED));
- } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED,
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
- } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_BOOT_COMPLETED));
- }
- }
- };
-
- private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
-
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_INFO_CHANGED,
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()), 0));
- }
- }
- };
-
- /**
- * When we receive a
- * {@link com.android.internal.telephony.TelephonyIntents#ACTION_SIM_STATE_CHANGED} broadcast,
- * and then pass a result via our handler to {@link KeyguardUpdateMonitor#handleSimStateChange},
- * we need a single object to pass to the handler. This class helps decode
- * the intent and provide a {@link SimCard.State} result.
- */
- private static class SimArgs {
- public final IccCardConstants.State simState;
-
- SimArgs(IccCardConstants.State state) {
- simState = state;
- }
-
- static SimArgs fromIntent(Intent intent) {
- IccCardConstants.State state;
- if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
- throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
- }
- String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
- if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
- final String absentReason = intent
- .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
-
- if (IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED.equals(
- absentReason)) {
- state = IccCardConstants.State.PERM_DISABLED;
- } else {
- state = IccCardConstants.State.ABSENT;
- }
- } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
- state = IccCardConstants.State.READY;
- } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
- final String lockedReason = intent
- .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
- if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
- state = IccCardConstants.State.PIN_REQUIRED;
- } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
- state = IccCardConstants.State.PUK_REQUIRED;
- } else {
- state = IccCardConstants.State.UNKNOWN;
- }
- } else if (IccCardConstants.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) {
- state = IccCardConstants.State.NETWORK_LOCKED;
- } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(stateExtra)
- || IccCardConstants.INTENT_VALUE_ICC_IMSI.equals(stateExtra)) {
- // This is required because telephony doesn't return to "READY" after
- // these state transitions. See bug 7197471.
- state = IccCardConstants.State.READY;
- } else {
- state = IccCardConstants.State.UNKNOWN;
- }
- return new SimArgs(state);
- }
-
- public String toString() {
- return simState.toString();
- }
- }
-
- /* package */ static class BatteryStatus {
- public final int status;
- public final int level;
- public final int plugged;
- public final int health;
- public BatteryStatus(int status, int level, int plugged, int health) {
- this.status = status;
- this.level = level;
- this.plugged = plugged;
- this.health = health;
- }
-
- /**
- * Determine whether the device is plugged in (USB, power, or wireless).
- * @return true if the device is plugged in.
- */
- boolean isPluggedIn() {
- return plugged == BatteryManager.BATTERY_PLUGGED_AC
- || plugged == BatteryManager.BATTERY_PLUGGED_USB
- || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
- }
-
- /**
- * Whether or not the device is charged. Note that some devices never return 100% for
- * battery level, so this allows either battery level or status to determine if the
- * battery is charged.
- * @return true if the device is charged
- */
- public boolean isCharged() {
- return status == BATTERY_STATUS_FULL || level >= 100;
- }
-
- /**
- * Whether battery is low and needs to be charged.
- * @return true if battery is low
- */
- public boolean isBatteryLow() {
- return level < LOW_BATTERY_THRESHOLD;
- }
-
- }
-
- public static KeyguardUpdateMonitor getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new KeyguardUpdateMonitor(context);
- }
- return sInstance;
- }
-
- protected void handleSetGenerationId(int clientGeneration, boolean clearing, PendingIntent p) {
- mDisplayClientState.clientGeneration = clientGeneration;
- mDisplayClientState.clearing = clearing;
- mDisplayClientState.intent = p;
- if (DEBUG)
- Log.v(TAG, "handleSetGenerationId(g=" + clientGeneration + ", clear=" + clearing + ")");
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onMusicClientIdChanged(clientGeneration, clearing, p);
- }
- }
- }
-
- protected void handleSetPlaybackState(int generationId, int playbackState, long eventTime) {
- if (DEBUG)
- Log.v(TAG, "handleSetPlaybackState(gen=" + generationId
- + ", state=" + playbackState + ", t=" + eventTime + ")");
- mDisplayClientState.playbackState = playbackState;
- mDisplayClientState.playbackEventTime = eventTime;
- if (generationId == mDisplayClientState.clientGeneration) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onMusicPlaybackStateChanged(playbackState, eventTime);
- }
- }
- } else {
- Log.w(TAG, "Ignoring generation id " + generationId + " because it's not current");
- }
- }
-
- private void handleUserInfoChanged(int userId) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onUserInfoChanged(userId);
- }
- }
- }
-
- private KeyguardUpdateMonitor(Context context) {
- mContext = context;
-
- mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
- // Since device can't be un-provisioned, we only need to register a content observer
- // to update mDeviceProvisioned when we are...
- if (!mDeviceProvisioned) {
- watchForDeviceProvisioning();
- }
-
- // Take a guess at initial SIM state, battery status and PLMN until we get an update
- mSimState = IccCardConstants.State.NOT_READY;
- mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0);
- mTelephonyPlmn = getDefaultPlmn();
-
- // Watch for interesting updates
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_TIME_TICK);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
- filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
- filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- filter.addAction(Intent.ACTION_USER_REMOVED);
- context.registerReceiver(mBroadcastReceiver, filter);
-
- final IntentFilter bootCompleteFilter = new IntentFilter();
- bootCompleteFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- bootCompleteFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
- context.registerReceiver(mBroadcastReceiver, bootCompleteFilter);
-
- final IntentFilter userInfoFilter = new IntentFilter(Intent.ACTION_USER_INFO_CHANGED);
- context.registerReceiverAsUser(mBroadcastAllReceiver, UserHandle.ALL, userInfoFilter,
- null, null);
-
- try {
- ActivityManagerNative.getDefault().registerUserSwitchObserver(
- new IUserSwitchObserver.Stub() {
- @Override
- public void onUserSwitching(int newUserId, IRemoteCallback reply) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
- newUserId, 0, reply));
- mSwitchingUser = true;
- }
- @Override
- public void onUserSwitchComplete(int newUserId) throws RemoteException {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE,
- newUserId));
- mSwitchingUser = false;
- }
- });
- } catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- private boolean isDeviceProvisionedInSettingsDb() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.DEVICE_PROVISIONED, 0) != 0;
- }
-
- private void watchForDeviceProvisioning() {
- mDeviceProvisionedObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
- if (mDeviceProvisioned) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED));
- }
- if (DEBUG) Log.d(TAG, "DEVICE_PROVISIONED state = " + mDeviceProvisioned);
- }
- };
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
- false, mDeviceProvisionedObserver);
-
- // prevent a race condition between where we check the flag and where we register the
- // observer by grabbing the value once again...
- boolean provisioned = isDeviceProvisionedInSettingsDb();
- if (provisioned != mDeviceProvisioned) {
- mDeviceProvisioned = provisioned;
- if (mDeviceProvisioned) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED));
- }
- }
- }
-
- /**
- * Handle {@link #MSG_DPM_STATE_CHANGED}
- */
- protected void handleDevicePolicyManagerStateChanged() {
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onDevicePolicyManagerStateChanged();
- }
- }
- }
-
- /**
- * Handle {@link #MSG_USER_SWITCHING}
- */
- protected void handleUserSwitching(int userId, IRemoteCallback reply) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onUserSwitching(userId);
- }
- }
- try {
- reply.sendResult(null);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * Handle {@link #MSG_USER_SWITCH_COMPLETE}
- */
- protected void handleUserSwitchComplete(int userId) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onUserSwitchComplete(userId);
- }
- }
- }
-
- /**
- * Handle {@link #MSG_BOOT_COMPLETED}
- */
- protected void handleBootCompleted() {
- mBootCompleted = true;
- mAudioManager = new AudioManager(mContext);
- mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onBootCompleted();
- }
- }
- }
-
- /**
- * We need to store this state in the KeyguardUpdateMonitor since this class will not be
- * destroyed.
- */
- public boolean hasBootCompleted() {
- return mBootCompleted;
- }
-
- /**
- * Handle {@link #MSG_USER_REMOVED}
- */
- protected void handleUserRemoved(int userId) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onUserRemoved(userId);
- }
- }
- }
-
- /**
- * Handle {@link #MSG_DEVICE_PROVISIONED}
- */
- protected void handleDeviceProvisioned() {
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onDeviceProvisioned();
- }
- }
- if (mDeviceProvisionedObserver != null) {
- // We don't need the observer anymore...
- mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);
- mDeviceProvisionedObserver = null;
- }
- }
-
- /**
- * Handle {@link #MSG_PHONE_STATE_CHANGED}
- */
- protected void handlePhoneStateChanged(String newState) {
- if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
- if (TelephonyManager.EXTRA_STATE_IDLE.equals(newState)) {
- mPhoneState = TelephonyManager.CALL_STATE_IDLE;
- } else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(newState)) {
- mPhoneState = TelephonyManager.CALL_STATE_OFFHOOK;
- } else if (TelephonyManager.EXTRA_STATE_RINGING.equals(newState)) {
- mPhoneState = TelephonyManager.CALL_STATE_RINGING;
- }
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onPhoneStateChanged(mPhoneState);
- }
- }
- }
-
- /**
- * Handle {@link #MSG_RINGER_MODE_CHANGED}
- */
- protected void handleRingerModeChange(int mode) {
- if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")");
- mRingMode = mode;
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onRingerModeChanged(mode);
- }
- }
- }
-
- /**
- * Handle {@link #MSG_TIME_UPDATE}
- */
- private void handleTimeUpdate() {
- if (DEBUG) Log.d(TAG, "handleTimeUpdate");
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onTimeChanged();
- }
- }
- }
-
- /**
- * Handle {@link #MSG_BATTERY_UPDATE}
- */
- private void handleBatteryUpdate(BatteryStatus status) {
- if (DEBUG) Log.d(TAG, "handleBatteryUpdate");
- final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status);
- mBatteryStatus = status;
- if (batteryUpdateInteresting) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onRefreshBatteryInfo(status);
- }
- }
- }
- }
-
- /**
- * Handle {@link #MSG_CARRIER_INFO_UPDATE}
- */
- private void handleCarrierInfoUpdate() {
- if (DEBUG) Log.d(TAG, "handleCarrierInfoUpdate: plmn = " + mTelephonyPlmn
- + ", spn = " + mTelephonySpn);
-
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
- }
- }
- }
-
- /**
- * Handle {@link #MSG_SIM_STATE_CHANGE}
- */
- private void handleSimStateChange(SimArgs simArgs) {
- final IccCardConstants.State state = simArgs.simState;
-
- if (DEBUG) {
- Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " "
- + "state resolved to " + state.toString());
- }
-
- if (state != IccCardConstants.State.UNKNOWN && state != mSimState) {
- mSimState = state;
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onSimStateChanged(state);
- }
- }
- }
- }
-
- /**
- * Handle {@link #MSG_CLOCK_VISIBILITY_CHANGED}
- */
- private void handleClockVisibilityChanged() {
- if (DEBUG) Log.d(TAG, "handleClockVisibilityChanged()");
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onClockVisibilityChanged();
- }
- }
- }
-
- /**
- * Handle {@link #MSG_KEYGUARD_VISIBILITY_CHANGED}
- */
- private void handleKeyguardVisibilityChanged(int showing) {
- if (DEBUG) Log.d(TAG, "handleKeyguardVisibilityChanged(" + showing + ")");
- boolean isShowing = (showing == 1);
- mKeyguardIsVisible = isShowing;
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onKeyguardVisibilityChanged(isShowing);
- }
- }
- }
-
- public boolean isKeyguardVisible() {
- return mKeyguardIsVisible;
- }
-
- public boolean isSwitchingUser() {
- return mSwitchingUser;
- }
-
- private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
- final boolean nowPluggedIn = current.isPluggedIn();
- final boolean wasPluggedIn = old.isPluggedIn();
- final boolean stateChangedWhilePluggedIn =
- wasPluggedIn == true && nowPluggedIn == true
- && (old.status != current.status);
-
- // change in plug state is always interesting
- if (wasPluggedIn != nowPluggedIn || stateChangedWhilePluggedIn) {
- return true;
- }
-
- // change in battery level while plugged in
- if (nowPluggedIn && old.level != current.level) {
- return true;
- }
-
- // change where battery needs charging
- if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) {
- return true;
- }
- return false;
- }
-
- /**
- * @param intent The intent with action {@link TelephonyIntents#SPN_STRINGS_UPDATED_ACTION}
- * @return The string to use for the plmn, or null if it should not be shown.
- */
- private CharSequence getTelephonyPlmnFrom(Intent intent) {
- if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
- final String plmn = intent.getStringExtra(TelephonyIntents.EXTRA_PLMN);
- return (plmn != null) ? plmn : getDefaultPlmn();
- }
- return null;
- }
-
- /**
- * @return The default plmn (no service)
- */
- private CharSequence getDefaultPlmn() {
- return mContext.getResources().getText(R.string.lockscreen_carrier_default);
- }
-
- /**
- * @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION}
- * @return The string to use for the plmn, or null if it should not be shown.
- */
- private CharSequence getTelephonySpnFrom(Intent intent) {
- if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
- final String spn = intent.getStringExtra(TelephonyIntents.EXTRA_SPN);
- if (spn != null) {
- return spn;
- }
- }
- return null;
- }
-
- /**
- * Remove the given observer's callback.
- *
- * @param callback The callback to remove
- */
- public void removeCallback(KeyguardUpdateMonitorCallback callback) {
- if (DEBUG) Log.v(TAG, "*** unregister callback for " + callback);
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- if (mCallbacks.get(i).get() == callback) {
- mCallbacks.remove(i);
- }
- }
- }
-
- /**
- * Register to receive notifications about general keyguard information
- * (see {@link InfoCallback}.
- * @param callback The callback to register
- */
- public void registerCallback(KeyguardUpdateMonitorCallback callback) {
- if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
- // Prevent adding duplicate callbacks
- for (int i = 0; i < mCallbacks.size(); i++) {
- if (mCallbacks.get(i).get() == callback) {
- if (DEBUG) Log.e(TAG, "Object tried to add another callback",
- new Exception("Called by"));
- return;
- }
- }
- mCallbacks.add(new WeakReference<KeyguardUpdateMonitorCallback>(callback));
- removeCallback(null); // remove unused references
- sendUpdates(callback);
- }
-
- private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
- // Notify listener of the current state
- callback.onRefreshBatteryInfo(mBatteryStatus);
- callback.onTimeChanged();
- callback.onRingerModeChanged(mRingMode);
- callback.onPhoneStateChanged(mPhoneState);
- callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
- callback.onClockVisibilityChanged();
- callback.onSimStateChanged(mSimState);
- callback.onMusicClientIdChanged(
- mDisplayClientState.clientGeneration,
- mDisplayClientState.clearing,
- mDisplayClientState.intent);
- callback.onMusicPlaybackStateChanged(mDisplayClientState.playbackState,
- mDisplayClientState.playbackEventTime);
- }
-
- public void sendKeyguardVisibilityChanged(boolean showing) {
- if (DEBUG) Log.d(TAG, "sendKeyguardVisibilityChanged(" + showing + ")");
- Message message = mHandler.obtainMessage(MSG_KEYGUARD_VISIBILITY_CHANGED);
- message.arg1 = showing ? 1 : 0;
- message.sendToTarget();
- }
-
- public void reportClockVisible(boolean visible) {
- mClockVisible = visible;
- mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget();
- }
-
- public IccCardConstants.State getSimState() {
- return mSimState;
- }
-
- /**
- * Report that the user successfully entered the SIM PIN or PUK/SIM PIN so we
- * have the information earlier than waiting for the intent
- * broadcast from the telephony code.
- *
- * NOTE: Because handleSimStateChange() invokes callbacks immediately without going
- * through mHandler, this *must* be called from the UI thread.
- */
- public void reportSimUnlocked() {
- handleSimStateChange(new SimArgs(IccCardConstants.State.READY));
- }
-
- public CharSequence getTelephonyPlmn() {
- return mTelephonyPlmn;
- }
-
- public CharSequence getTelephonySpn() {
- return mTelephonySpn;
- }
-
- /**
- * @return Whether the device is provisioned (whether they have gone through
- * the setup wizard)
- */
- public boolean isDeviceProvisioned() {
- return mDeviceProvisioned;
- }
-
- public int getFailedUnlockAttempts() {
- return mFailedAttempts;
- }
-
- public void clearFailedUnlockAttempts() {
- mFailedAttempts = 0;
- mFailedBiometricUnlockAttempts = 0;
- }
-
- public void reportFailedUnlockAttempt() {
- mFailedAttempts++;
- }
-
- public boolean isClockVisible() {
- return mClockVisible;
- }
-
- public int getPhoneState() {
- return mPhoneState;
- }
-
- public void reportFailedBiometricUnlockAttempt() {
- mFailedBiometricUnlockAttempts++;
- }
-
- public boolean getMaxBiometricUnlockAttemptsReached() {
- return mFailedBiometricUnlockAttempts >= FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP;
- }
-
- public boolean isAlternateUnlockEnabled() {
- return mAlternateUnlockEnabled;
- }
-
- public void setAlternateUnlockEnabled(boolean enabled) {
- mAlternateUnlockEnabled = enabled;
- }
-
- public boolean isSimLocked() {
- return isSimLocked(mSimState);
- }
-
- public static boolean isSimLocked(IccCardConstants.State state) {
- return state == IccCardConstants.State.PIN_REQUIRED
- || state == IccCardConstants.State.PUK_REQUIRED
- || state == IccCardConstants.State.PERM_DISABLED;
- }
-
- public boolean isSimPinSecure() {
- return isSimPinSecure(mSimState);
- }
-
- public static boolean isSimPinSecure(IccCardConstants.State state) {
- final IccCardConstants.State simState = state;
- return (simState == IccCardConstants.State.PIN_REQUIRED
- || simState == IccCardConstants.State.PUK_REQUIRED
- || simState == IccCardConstants.State.PERM_DISABLED);
- }
-
- public DisplayClientState getCachedDisplayClientState() {
- return mDisplayClientState;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
deleted file mode 100644
index 41816db..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.media.AudioManager;
-
-import com.android.internal.telephony.IccCardConstants;
-
-/**
- * Callback for general information relevant to lock screen.
- */
-class KeyguardUpdateMonitorCallback {
- /**
- * Called when the battery status changes, e.g. when plugged in or unplugged, charge
- * level, etc. changes.
- *
- * @param status current battery status
- */
- void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { }
-
- /**
- * Called once per minute or when the time changes.
- */
- void onTimeChanged() { }
-
- /**
- * Called when the carrier PLMN or SPN changes.
- *
- * @param plmn The operator name of the registered network. May be null if it shouldn't
- * be displayed.
- * @param spn The service provider name. May be null if it shouldn't be displayed.
- */
- void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { }
-
- /**
- * Called when the ringer mode changes.
- * @param state the current ringer state, as defined in
- * {@link AudioManager#RINGER_MODE_CHANGED_ACTION}
- */
- void onRingerModeChanged(int state) { }
-
- /**
- * Called when the phone state changes. String will be one of:
- * {@link TelephonyManager#EXTRA_STATE_IDLE}
- * {@link TelephonyManager@EXTRA_STATE_RINGING}
- * {@link TelephonyManager#EXTRA_STATE_OFFHOOK
- */
- void onPhoneStateChanged(int phoneState) { }
-
- /**
- * Called when the visibility of the keyguard changes.
- * @param showing Indicates if the keyguard is now visible.
- */
- void onKeyguardVisibilityChanged(boolean showing) { }
-
- /**
- * Called when visibility of lockscreen clock changes, such as when
- * obscured by a widget.
- */
- void onClockVisibilityChanged() { }
-
- /**
- * Called when the device becomes provisioned
- */
- void onDeviceProvisioned() { }
-
- /**
- * Called when the device policy changes.
- * See {@link DevicePolicyManager#ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED}
- */
- void onDevicePolicyManagerStateChanged() { }
-
- /**
- * Called when the user change begins.
- */
- void onUserSwitching(int userId) { }
-
- /**
- * Called when the user change is complete.
- */
- void onUserSwitchComplete(int userId) { }
-
- /**
- * Called when the SIM state changes.
- * @param simState
- */
- void onSimStateChanged(IccCardConstants.State simState) { }
-
- /**
- * Called when a user is removed.
- */
- void onUserRemoved(int userId) { }
-
- /**
- * Called when the user's info changed.
- */
- void onUserInfoChanged(int userId) { }
-
- /**
- * Called when boot completed.
- *
- * Note, this callback will only be received if boot complete occurs after registering with
- * KeyguardUpdateMonitor.
- */
- void onBootCompleted() { }
-
- /**
- * Called when audio client attaches or detaches from AudioManager.
- */
- void onMusicClientIdChanged(int clientGeneration, boolean clearing, PendingIntent intent) { }
-
- /**
- * Called when the audio playback state changes.
- * @param playbackState
- * @param eventTime
- */
- public void onMusicPlaybackStateChanged(int playbackState, long eventTime) { }
-
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
deleted file mode 100644
index 6fcacd3..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.media.IAudioService;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.TelephonyManager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Slog;
-import android.view.KeyEvent;
-import android.widget.FrameLayout;
-
-/**
- * Base class for keyguard view. {@link #reset} is where you should
- * reset the state of your view. Use the {@link KeyguardViewCallback} via
- * {@link #getCallback()} to send information back (such as poking the wake lock,
- * or finishing the keyguard).
- *
- * Handles intercepting of media keys that still work when the keyguard is
- * showing.
- */
-public abstract class KeyguardViewBase extends FrameLayout {
-
- private static final int BACKGROUND_COLOR = 0x70000000;
- private AudioManager mAudioManager;
- private TelephonyManager mTelephonyManager = null;
- protected KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
-
- // Whether the volume keys should be handled by keyguard. If true, then
- // they will be handled here for specific media types such as music, otherwise
- // the audio service will bring up the volume dialog.
- private static final boolean KEYGUARD_MANAGES_VOLUME = true;
-
- // This is a faster way to draw the background on devices without hardware acceleration
- private static final Drawable mBackgroundDrawable = new Drawable() {
- @Override
- public void draw(Canvas canvas) {
- canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC);
- }
-
- @Override
- public void setAlpha(int alpha) {
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
- };
-
- public KeyguardViewBase(Context context) {
- this(context, null);
- }
-
- public KeyguardViewBase(Context context, AttributeSet attrs) {
- super(context, attrs);
- resetBackground();
- }
-
- public void resetBackground() {
- setBackground(mBackgroundDrawable);
- }
-
- /**
- * Called when you need to reset the state of your view.
- */
- abstract public void reset();
-
- /**
- * Called when the screen turned off.
- */
- abstract public void onScreenTurnedOff();
-
- /**
- * Called when the screen turned on.
- */
- abstract public void onScreenTurnedOn();
-
- /**
- * Called when the view needs to be shown.
- */
- abstract public void show();
-
- /**
- * Called when a key has woken the device to give us a chance to adjust our
- * state according the the key. We are responsible for waking the device
- * (by poking the wake lock) once we are ready.
- *
- * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
- * Be sure not to take any action that takes a long time; any significant
- * action should be posted to a handler.
- *
- * @param keyCode The wake key, which may be relevant for configuring the
- * keyguard. May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking for a reason
- * other than a key press.
- */
- abstract public void wakeWhenReadyTq(int keyCode);
-
- /**
- * Verify that the user can get past the keyguard securely. This is called,
- * for example, when the phone disables the keyguard but then wants to launch
- * something else that requires secure access.
- *
- * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
- */
- abstract public void verifyUnlock();
-
- /**
- * Called before this view is being removed.
- */
- abstract public void cleanUp();
-
- /**
- * Gets the desired user activity timeout in milliseconds, or -1 if the
- * default should be used.
- */
- abstract public long getUserActivityTimeout();
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (interceptMediaKey(event)) {
- return true;
- }
- return super.dispatchKeyEvent(event);
- }
-
- /**
- * Allows the media keys to work when the keyguard is showing.
- * The media keys should be of no interest to the actual keyguard view(s),
- * so intercepting them here should not be of any harm.
- * @param event The key event
- * @return whether the event was consumed as a media key.
- */
- private boolean interceptMediaKey(KeyEvent event) {
- final int keyCode = event.getKeyCode();
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- /* Suppress PLAY/PAUSE toggle when phone is ringing or
- * in-call to avoid music playback */
- if (mTelephonyManager == null) {
- mTelephonyManager = (TelephonyManager) getContext().getSystemService(
- Context.TELEPHONY_SERVICE);
- }
- if (mTelephonyManager != null &&
- mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
- return true; // suppress key event
- }
- case KeyEvent.KEYCODE_MUTE:
- case KeyEvent.KEYCODE_HEADSETHOOK:
- case KeyEvent.KEYCODE_MEDIA_STOP:
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- case KeyEvent.KEYCODE_MEDIA_REWIND:
- case KeyEvent.KEYCODE_MEDIA_RECORD:
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
- handleMediaKeyEvent(event);
- return true;
- }
-
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_VOLUME_MUTE: {
- if (KEYGUARD_MANAGES_VOLUME) {
- synchronized (this) {
- if (mAudioManager == null) {
- mAudioManager = (AudioManager) getContext().getSystemService(
- Context.AUDIO_SERVICE);
- }
- }
- // Volume buttons should only function for music (local or remote).
- // TODO: Actually handle MUTE.
- mAudioManager.adjustLocalOrRemoteStreamVolume(
- AudioManager.STREAM_MUSIC,
- keyCode == KeyEvent.KEYCODE_VOLUME_UP
- ? AudioManager.ADJUST_RAISE
- : AudioManager.ADJUST_LOWER);
- // Don't execute default volume behavior
- return true;
- } else {
- return false;
- }
- }
- }
- } else if (event.getAction() == KeyEvent.ACTION_UP) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_MUTE:
- case KeyEvent.KEYCODE_HEADSETHOOK:
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- case KeyEvent.KEYCODE_MEDIA_STOP:
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- case KeyEvent.KEYCODE_MEDIA_REWIND:
- case KeyEvent.KEYCODE_MEDIA_RECORD:
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
- handleMediaKeyEvent(event);
- return true;
- }
- }
- }
- return false;
- }
-
- void handleMediaKeyEvent(KeyEvent keyEvent) {
- IAudioService audioService = IAudioService.Stub.asInterface(
- ServiceManager.checkService(Context.AUDIO_SERVICE));
- if (audioService != null) {
- try {
- audioService.dispatchMediaKeyEvent(keyEvent);
- } catch (RemoteException e) {
- Log.e("KeyguardViewBase", "dispatchMediaKeyEvent threw exception " + e);
- }
- } else {
- Slog.w("KeyguardViewBase", "Unable to find IAudioService for media key event");
- }
- }
-
- @Override
- public void dispatchSystemUiVisibilityChanged(int visibility) {
- super.dispatchSystemUiVisibilityChanged(visibility);
-
- if (!(mContext instanceof Activity)) {
- setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
- }
- }
-
- public void setViewMediatorCallback(
- KeyguardViewMediator.ViewMediatorCallback viewMediatorCallback) {
- mViewMediatorCallback = viewMediatorCallback;
- }
-
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
deleted file mode 100644
index 30c95fb..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.appwidget.AppWidgetManager;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcelable;
-import android.os.SystemProperties;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewManager;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
-
-/**
- * Manages creating, showing, hiding and resetting the keyguard. Calls back
- * via {@link KeyguardViewMediator.ViewMediatorCallback} to poke
- * the wake lock and report that the keyguard is done, which is in turn,
- * reported to this class by the current {@link KeyguardViewBase}.
- */
-public class KeyguardViewManager {
- private final static boolean DEBUG = KeyguardViewMediator.DEBUG;
- private static String TAG = "KeyguardViewManager";
- public static boolean USE_UPPER_CASE = true;
- public final static String IS_SWITCHING_USER = "is_switching_user";
-
- // Timeout used for keypresses
- static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
-
- private final Context mContext;
- private final ViewManager mViewManager;
- private final KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
-
- private WindowManager.LayoutParams mWindowLayoutParams;
- private boolean mNeedsInput = false;
-
- private FrameLayout mKeyguardHost;
- private KeyguardHostView mKeyguardView;
-
- private boolean mScreenOn = false;
- private LockPatternUtils mLockPatternUtils;
-
- public interface ShowListener {
- void onShown(IBinder windowToken);
- };
-
- /**
- * @param context Used to create views.
- * @param viewManager Keyguard will be attached to this.
- * @param callback Used to notify of changes.
- * @param lockPatternUtils
- */
- public KeyguardViewManager(Context context, ViewManager viewManager,
- KeyguardViewMediator.ViewMediatorCallback callback,
- LockPatternUtils lockPatternUtils) {
- mContext = context;
- mViewManager = viewManager;
- mViewMediatorCallback = callback;
- mLockPatternUtils = lockPatternUtils;
- }
-
- /**
- * Show the keyguard. Will handle creating and attaching to the view manager
- * lazily.
- */
- public synchronized void show(Bundle options) {
- if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
-
- boolean enableScreenRotation = shouldEnableScreenRotation();
-
- maybeCreateKeyguardLocked(enableScreenRotation, false, options);
- maybeEnableScreenRotation(enableScreenRotation);
-
- // Disable common aspects of the system/status/navigation bars that are not appropriate or
- // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED
- // activities. Other disabled bits are handled by the KeyguardViewMediator talking
- // directly to the status bar service.
- final int visFlags = View.STATUS_BAR_DISABLE_HOME;
- if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");
- mKeyguardHost.setSystemUiVisibility(visFlags);
-
- mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
- mKeyguardHost.setVisibility(View.VISIBLE);
- mKeyguardView.show();
- mKeyguardView.requestFocus();
- }
-
- private boolean shouldEnableScreenRotation() {
- Resources res = mContext.getResources();
- return SystemProperties.getBoolean("lockscreen.rot_override",false)
- || res.getBoolean(com.android.internal.R.bool.config_enableLockScreenRotation);
- }
-
- class ViewManagerHost extends FrameLayout {
- public ViewManagerHost(Context context) {
- super(context);
- setFitsSystemWindows(true);
- }
-
- @Override
- protected boolean fitSystemWindows(Rect insets) {
- Log.v("TAG", "bug 7643792: fitSystemWindows(" + insets.toShortString() + ")");
- return super.fitSystemWindows(insets);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (mKeyguardHost.getVisibility() == View.VISIBLE) {
- // only propagate configuration messages if we're currently showing
- maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
- } else {
- if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
- }
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (mKeyguardView != null) {
- // Always process back and menu keys, regardless of focus
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- int keyCode = event.getKeyCode();
- if (keyCode == KeyEvent.KEYCODE_BACK && mKeyguardView.handleBackKey()) {
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_MENU && mKeyguardView.handleMenuKey()) {
- return true;
- }
- }
- // Always process media keys, regardless of focus
- if (mKeyguardView.dispatchKeyEvent(event)) {
- return true;
- }
- }
- return super.dispatchKeyEvent(event);
- }
- }
-
- SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>();
-
- private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
- Bundle options) {
- final boolean isActivity = (mContext instanceof Activity); // for test activity
-
- if (mKeyguardHost != null) {
- mKeyguardHost.saveHierarchyState(mStateContainer);
- }
-
- if (mKeyguardHost == null) {
- if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
-
- mKeyguardHost = new ViewManagerHost(mContext);
-
- int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-
- if (!mNeedsInput) {
- flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- }
- if (ActivityManager.isHighEndGfx()) {
- flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- }
-
- final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
- final int type = isActivity ? WindowManager.LayoutParams.TYPE_APPLICATION
- : WindowManager.LayoutParams.TYPE_KEYGUARD;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);
- lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
- lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
- lp.screenOrientation = enableScreenRotation ?
- ActivityInfo.SCREEN_ORIENTATION_USER : ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
-
- if (ActivityManager.isHighEndGfx()) {
- lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- lp.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
- }
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;
- if (isActivity) {
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- }
- lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
- lp.setTitle(isActivity ? "KeyguardMock" : "Keyguard");
- mWindowLayoutParams = lp;
- mViewManager.addView(mKeyguardHost, lp);
- }
-
- if (force || mKeyguardView == null) {
- inflateKeyguardView(options);
- mKeyguardView.requestFocus();
- }
- updateUserActivityTimeoutInWindowLayoutParams();
- mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
-
- mKeyguardHost.restoreHierarchyState(mStateContainer);
- }
-
- private void inflateKeyguardView(Bundle options) {
- View v = mKeyguardHost.findViewById(R.id.keyguard_host_view);
- if (v != null) {
- mKeyguardHost.removeView(v);
- }
- // TODO: Remove once b/7094175 is fixed
- if (false) Slog.d(TAG, "inflateKeyguardView: b/7094175 mContext.config="
- + mContext.getResources().getConfiguration());
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true);
- mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view);
- mKeyguardView.setLockPatternUtils(mLockPatternUtils);
- mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);
- mKeyguardView.initializeSwitchingUserState(options != null &&
- options.getBoolean(IS_SWITCHING_USER));
-
- // HACK
- // The keyguard view will have set up window flags in onFinishInflate before we set
- // the view mediator callback. Make sure it knows the correct IME state.
- if (mViewMediatorCallback != null) {
- KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(
- R.id.keyguard_password_view);
-
- if (kpv != null) {
- mViewMediatorCallback.setNeedsInput(kpv.needsInput());
- }
- }
-
- if (options != null) {
- int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET,
- AppWidgetManager.INVALID_APPWIDGET_ID);
- if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
- mKeyguardView.goToWidget(widgetToShow);
- }
- }
- }
-
- public void updateUserActivityTimeout() {
- updateUserActivityTimeoutInWindowLayoutParams();
- mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
- }
-
- private void updateUserActivityTimeoutInWindowLayoutParams() {
- // Use the user activity timeout requested by the keyguard view, if any.
- if (mKeyguardView != null) {
- long timeout = mKeyguardView.getUserActivityTimeout();
- if (timeout >= 0) {
- mWindowLayoutParams.userActivityTimeout = timeout;
- return;
- }
- }
-
- // Otherwise, use the default timeout.
- mWindowLayoutParams.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS;
- }
-
- private void maybeEnableScreenRotation(boolean enableScreenRotation) {
- // TODO: move this outside
- if (enableScreenRotation) {
- if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!");
- mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
- } else {
- if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
- mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
- }
- mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
- }
-
- public void setNeedsInput(boolean needsInput) {
- mNeedsInput = needsInput;
- if (mWindowLayoutParams != null) {
- if (needsInput) {
- mWindowLayoutParams.flags &=
- ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- } else {
- mWindowLayoutParams.flags |=
- WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- }
-
- try {
- mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
- } catch (java.lang.IllegalArgumentException e) {
- // TODO: Ensure this method isn't called on views that are changing...
- Log.w(TAG,"Can't update input method on " + mKeyguardHost + " window not attached");
- }
- }
- }
-
- /**
- * Reset the state of the view.
- */
- public synchronized void reset(Bundle options) {
- if (DEBUG) Log.d(TAG, "reset()");
- // User might have switched, check if we need to go back to keyguard
- // TODO: It's preferable to stay and show the correct lockscreen or unlock if none
- maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options);
- }
-
- public synchronized void onScreenTurnedOff() {
- if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
- mScreenOn = false;
- if (mKeyguardView != null) {
- mKeyguardView.onScreenTurnedOff();
- }
- }
-
- public synchronized void onScreenTurnedOn(
- final KeyguardViewManager.ShowListener showListener) {
- if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
- mScreenOn = true;
- if (mKeyguardView != null) {
- mKeyguardView.onScreenTurnedOn();
-
- // Caller should wait for this window to be shown before turning
- // on the screen.
- if (showListener != null) {
- if (mKeyguardHost.getVisibility() == View.VISIBLE) {
- // Keyguard may be in the process of being shown, but not yet
- // updated with the window manager... give it a chance to do so.
- mKeyguardHost.post(new Runnable() {
- @Override
- public void run() {
- if (mKeyguardHost.getVisibility() == View.VISIBLE) {
- showListener.onShown(mKeyguardHost.getWindowToken());
- } else {
- showListener.onShown(null);
- }
- }
- });
- } else {
- showListener.onShown(null);
- }
- }
- } else if (showListener != null) {
- showListener.onShown(null);
- }
- }
-
- public synchronized void verifyUnlock() {
- if (DEBUG) Log.d(TAG, "verifyUnlock()");
- show(null);
- mKeyguardView.verifyUnlock();
- }
-
- /**
- * A key has woken the device. We use this to potentially adjust the state
- * of the lock screen based on the key.
- *
- * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
- * Be sure not to take any action that takes a long time; any significant
- * action should be posted to a handler.
- *
- * @param keyCode The wake key. May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking
- * for a reason other than a key press.
- */
- public boolean wakeWhenReadyTq(int keyCode) {
- if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
- if (mKeyguardView != null) {
- mKeyguardView.wakeWhenReadyTq(keyCode);
- return true;
- }
- Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq");
- return false;
- }
-
- /**
- * Hides the keyguard view
- */
- public synchronized void hide() {
- if (DEBUG) Log.d(TAG, "hide()");
-
- if (mKeyguardHost != null) {
- mKeyguardHost.setVisibility(View.GONE);
-
- // We really only want to preserve keyguard state for configuration changes. Hence
- // we should clear state of widgets (e.g. Music) when we hide keyguard so it can
- // start with a fresh state when we return.
- mStateContainer.clear();
-
- // Don't do this right away, so we can let the view continue to animate
- // as it goes away.
- if (mKeyguardView != null) {
- final KeyguardViewBase lastView = mKeyguardView;
- mKeyguardView = null;
- mKeyguardHost.postDelayed(new Runnable() {
- @Override
- public void run() {
- synchronized (KeyguardViewManager.this) {
- lastView.cleanUp();
- mKeyguardHost.removeView(lastView);
- }
- }
- }, 500);
- }
- }
- }
-
- /**
- * Dismisses the keyguard by going to the next screen or making it gone.
- */
- public synchronized void dismiss() {
- if (mScreenOn) {
- mKeyguardView.dismiss();
- }
- }
-
- /**
- * @return Whether the keyguard is showing
- */
- public synchronized boolean isShowing() {
- return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
- }
-
- public void showAssistant() {
- if (mKeyguardView != null) {
- mKeyguardView.showAssistant();
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
deleted file mode 100644
index 885cb45..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ /dev/null
@@ -1,1449 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-
-import android.app.Activity;
-import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.SearchManager;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.media.SoundPool;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
-import android.util.EventLog;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.widget.LockPatternUtils;
-
-
-/**
- * Mediates requests related to the keyguard. This includes queries about the
- * state of the keyguard, power management events that effect whether the keyguard
- * should be shown or reset, callbacks to the phone window manager to notify
- * it of when the keyguard is showing, and events from the keyguard view itself
- * stating that the keyguard was succesfully unlocked.
- *
- * Note that the keyguard view is shown when the screen is off (as appropriate)
- * so that once the screen comes on, it will be ready immediately.
- *
- * Example queries about the keyguard:
- * - is {movement, key} one that should wake the keygaurd?
- * - is the keyguard showing?
- * - are input events restricted due to the state of the keyguard?
- *
- * Callbacks to the phone window manager:
- * - the keyguard is showing
- *
- * Example external events that translate to keyguard view changes:
- * - screen turned off -> reset the keyguard, and show it so it will be ready
- * next time the screen turns on
- * - keyboard is slid open -> if the keyguard is not secure, hide it
- *
- * Events from the keyguard view:
- * - user succesfully unlocked keyguard -> hide keyguard view, and no longer
- * restrict input events.
- *
- * Note: in addition to normal power managment events that effect the state of
- * whether the keyguard should be showing, external apps and services may request
- * that the keyguard be disabled via {@link #setKeyguardEnabled(boolean)}. When
- * false, this will override all other conditions for turning on the keyguard.
- *
- * Threading and synchronization:
- * This class is created by the initialization routine of the {@link WindowManagerPolicy},
- * and runs on its thread. The keyguard UI is created from that thread in the
- * constructor of this class. The apis may be called from other threads, including the
- * {@link com.android.server.input.InputManagerService}'s and {@link android.view.WindowManager}'s.
- * Therefore, methods on this class are synchronized, and any action that is pointed
- * directly to the keyguard UI is posted to a {@link Handler} to ensure it is taken on the UI
- * thread of the keyguard.
- */
-public class KeyguardViewMediator {
- private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
- final static boolean DEBUG = false;
- private final static boolean DBG_WAKE = false;
-
- private final static String TAG = "KeyguardViewMediator";
-
- private static final String DELAYED_KEYGUARD_ACTION =
- "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD";
-
- // used for handler messages
- private static final int SHOW = 2;
- private static final int HIDE = 3;
- private static final int RESET = 4;
- private static final int VERIFY_UNLOCK = 5;
- private static final int NOTIFY_SCREEN_OFF = 6;
- private static final int NOTIFY_SCREEN_ON = 7;
- private static final int WAKE_WHEN_READY = 8;
- private static final int KEYGUARD_DONE = 9;
- private static final int KEYGUARD_DONE_DRAWING = 10;
- private static final int KEYGUARD_DONE_AUTHENTICATING = 11;
- private static final int SET_HIDDEN = 12;
- private static final int KEYGUARD_TIMEOUT = 13;
- private static final int SHOW_ASSISTANT = 14;
-
- /**
- * The default amount of time we stay awake (used for all key input)
- */
- protected static final int AWAKE_INTERVAL_DEFAULT_MS = 10000;
-
- /**
- * How long to wait after the screen turns off due to timeout before
- * turning on the keyguard (i.e, the user has this much time to turn
- * the screen back on without having to face the keyguard).
- */
- private static final int KEYGUARD_LOCK_AFTER_DELAY_DEFAULT = 5000;
-
- /**
- * How long we'll wait for the {@link ViewMediatorCallback#keyguardDoneDrawing()}
- * callback before unblocking a call to {@link #setKeyguardEnabled(boolean)}
- * that is reenabling the keyguard.
- */
- private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000;
-
- /**
- * Allow the user to expand the status bar when the keyguard is engaged
- * (without a pattern or password).
- */
- private static final boolean ENABLE_INSECURE_STATUS_BAR_EXPAND = true;
-
- /** The stream type that the lock sounds are tied to. */
- private int mMasterStreamType;
-
- private Context mContext;
- private AlarmManager mAlarmManager;
- private AudioManager mAudioManager;
- private StatusBarManager mStatusBarManager;
- private boolean mShowLockIcon;
- private boolean mShowingLockIcon;
- private boolean mSwitchingUser;
-
- private boolean mSystemReady;
-
- // Whether the next call to playSounds() should be skipped. Defaults to
- // true because the first lock (on boot) should be silent.
- private boolean mSuppressNextLockSound = true;
-
-
- /** High level access to the power manager for WakeLocks */
- private PowerManager mPM;
-
- /** UserManager for querying number of users */
- private UserManager mUserManager;
-
- /** SearchManager for determining whether or not search assistant is available */
- private SearchManager mSearchManager;
-
- /**
- * Used to keep the device awake while to ensure the keyguard finishes opening before
- * we sleep.
- */
- private PowerManager.WakeLock mShowKeyguardWakeLock;
-
- /**
- * Does not turn on screen, held while a call to {@link KeyguardViewManager#wakeWhenReadyTq(int)}
- * is called to make sure the device doesn't sleep before it has a chance to poke
- * the wake lock.
- * @see #wakeWhenReady(int)
- */
- private PowerManager.WakeLock mWakeAndHandOff;
-
- private KeyguardViewManager mKeyguardViewManager;
-
- // these are protected by synchronized (this)
-
- /**
- * External apps (like the phone app) can tell us to disable the keygaurd.
- */
- private boolean mExternallyEnabled = true;
-
- /**
- * Remember if an external call to {@link #setKeyguardEnabled} with value
- * false caused us to hide the keyguard, so that we need to reshow it once
- * the keygaurd is reenabled with another call with value true.
- */
- private boolean mNeedToReshowWhenReenabled = false;
-
- // cached value of whether we are showing (need to know this to quickly
- // answer whether the input should be restricted)
- private boolean mShowing = false;
-
- // true if the keyguard is hidden by another window
- private boolean mHidden = false;
-
- /**
- * Helps remember whether the screen has turned on since the last time
- * it turned off due to timeout. see {@link #onScreenTurnedOff(int)}
- */
- private int mDelayedShowingSequence;
-
- /**
- * If the user has disabled the keyguard, then requests to exit, this is
- * how we'll ultimately let them know whether it was successful. We use this
- * var being non-null as an indicator that there is an in progress request.
- */
- private WindowManagerPolicy.OnKeyguardExitResult mExitSecureCallback;
-
- // the properties of the keyguard
-
- private KeyguardUpdateMonitor mUpdateMonitor;
-
- private boolean mScreenOn;
-
- // last known state of the cellular connection
- private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
-
- /**
- * we send this intent when the keyguard is dismissed.
- */
- private Intent mUserPresentIntent;
-
- /**
- * {@link #setKeyguardEnabled} waits on this condition when it reenables
- * the keyguard.
- */
- private boolean mWaitingUntilKeyguardVisible = false;
- private LockPatternUtils mLockPatternUtils;
- private boolean mKeyguardDonePending = false;
-
- private SoundPool mLockSounds;
- private int mLockSoundId;
- private int mUnlockSoundId;
- private int mLockSoundStreamId;
-
- /**
- * The volume applied to the lock/unlock sounds.
- */
- private final float mLockSoundVolume;
-
- /**
- * Cache of avatar drawables, for use by KeyguardMultiUserAvatar.
- */
- private static MultiUserAvatarCache sMultiUserAvatarCache = new MultiUserAvatarCache();
-
- /**
- * The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
- * various things.
- */
- public interface ViewMediatorCallback {
-
- /**
- * Wake the device immediately.
- */
- void wakeUp();
-
- /**
- * Reports user activity and requests that the screen stay on.
- */
- void userActivity();
-
- /**
- * Reports user activity and requests that the screen stay on for at least
- * the specified amount of time.
- * @param millis The amount of time in millis. This value is currently ignored.
- */
- void userActivity(long millis);
-
- /**
- * Report that the keyguard is done.
- * @param authenticated Whether the user securely got past the keyguard.
- * the only reason for this to be false is if the keyguard was instructed
- * to appear temporarily to verify the user is supposed to get past the
- * keyguard, and the user fails to do so.
- */
- void keyguardDone(boolean authenticated);
-
- /**
- * Report that the keyguard is done drawing.
- */
- void keyguardDoneDrawing();
-
- /**
- * Tell ViewMediator that the current view needs IME input
- * @param needsInput
- */
- void setNeedsInput(boolean needsInput);
-
- /**
- * Tell view mediator that the keyguard view's desired user activity timeout
- * has changed and needs to be reapplied to the window.
- */
- void onUserActivityTimeoutChanged();
-
- /**
- * Report that the keyguard is dismissable, pending the next keyguardDone call.
- */
- void keyguardDonePending();
- }
-
- KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
-
- @Override
- public void onUserSwitching(int userId) {
- // Note that the mLockPatternUtils user has already been updated from setCurrentUser.
- // We need to force a reset of the views, since lockNow (called by
- // ActivityManagerService) will not reconstruct the keyguard if it is already showing.
- synchronized (KeyguardViewMediator.this) {
- mSwitchingUser = true;
- resetStateLocked(null);
- adjustStatusBarLocked();
- // When we switch users we want to bring the new user to the biometric unlock even
- // if the current user has gone to the backup.
- KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
- }
- }
-
- @Override
- public void onUserSwitchComplete(int userId) {
- mSwitchingUser = false;
- }
-
- @Override
- public void onUserRemoved(int userId) {
- mLockPatternUtils.removeUser(userId);
- sMultiUserAvatarCache.clear(userId);
- }
-
- @Override
- public void onUserInfoChanged(int userId) {
- sMultiUserAvatarCache.clear(userId);
- }
-
- @Override
- void onPhoneStateChanged(int phoneState) {
- synchronized (KeyguardViewMediator.this) {
- if (TelephonyManager.CALL_STATE_IDLE == phoneState // call ending
- && !mScreenOn // screen off
- && mExternallyEnabled) { // not disabled by any app
-
- // note: this is a way to gracefully reenable the keyguard when the call
- // ends and the screen is off without always reenabling the keyguard
- // each time the screen turns off while in call (and having an occasional ugly
- // flicker while turning back on the screen and disabling the keyguard again).
- if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
- + "keyguard is showing");
- doKeyguardLocked();
- }
- }
- };
-
- @Override
- public void onClockVisibilityChanged() {
- adjustStatusBarLocked();
- }
-
- @Override
- public void onDeviceProvisioned() {
- sendUserPresentBroadcast();
- }
-
- @Override
- public void onSimStateChanged(IccCardConstants.State simState) {
- if (DEBUG) Log.d(TAG, "onSimStateChanged: " + simState);
-
- switch (simState) {
- case NOT_READY:
- case ABSENT:
- // only force lock screen in case of missing sim if user hasn't
- // gone through setup wizard
- synchronized (this) {
- if (!mUpdateMonitor.isDeviceProvisioned()) {
- if (!isShowing()) {
- if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
- + " we need to show the keyguard since the "
- + "device isn't provisioned yet.");
- doKeyguardLocked();
- } else {
- resetStateLocked(null);
- }
- }
- }
- break;
- case PIN_REQUIRED:
- case PUK_REQUIRED:
- synchronized (this) {
- if (!isShowing()) {
- if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
- + "showing; need to show keyguard so user can enter sim pin");
- doKeyguardLocked();
- } else {
- resetStateLocked(null);
- }
- }
- break;
- case PERM_DISABLED:
- synchronized (this) {
- if (!isShowing()) {
- if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
- + "keygaurd isn't showing.");
- doKeyguardLocked();
- } else {
- if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
- + "show permanently disabled message in lockscreen.");
- resetStateLocked(null);
- }
- }
- break;
- case READY:
- synchronized (this) {
- if (isShowing()) {
- resetStateLocked(null);
- }
- }
- break;
- }
- }
-
- };
-
- ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
- public void wakeUp() {
- KeyguardViewMediator.this.wakeUp();
- }
-
- public void userActivity() {
- KeyguardViewMediator.this.userActivity();
- }
-
- public void userActivity(long holdMs) {
- KeyguardViewMediator.this.userActivity(holdMs);
- }
-
- public void keyguardDone(boolean authenticated) {
- KeyguardViewMediator.this.keyguardDone(authenticated, true);
- }
-
- public void keyguardDoneDrawing() {
- mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
- }
-
- @Override
- public void setNeedsInput(boolean needsInput) {
- mKeyguardViewManager.setNeedsInput(needsInput);
- }
-
- @Override
- public void onUserActivityTimeoutChanged() {
- mKeyguardViewManager.updateUserActivityTimeout();
- }
-
- @Override
- public void keyguardDonePending() {
- mKeyguardDonePending = true;
- }
- };
-
- public void wakeUp() {
- mPM.wakeUp(SystemClock.uptimeMillis());
- }
-
- public void userActivity() {
- userActivity(AWAKE_INTERVAL_DEFAULT_MS);
- }
-
- public void userActivity(long holdMs) {
- // We ignore the hold time. Eventually we should remove it.
- // Instead, the keyguard window has an explicit user activity timeout set on it.
- mPM.userActivity(SystemClock.uptimeMillis(), false);
- }
-
- /**
- * Construct a KeyguardViewMediator
- * @param context
- * @param lockPatternUtils optional mock interface for LockPatternUtils
- */
- public KeyguardViewMediator(Context context, LockPatternUtils lockPatternUtils) {
- mContext = context;
- mPM = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
- mShowKeyguardWakeLock.setReferenceCounted(false);
-
- mWakeAndHandOff = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "keyguardWakeAndHandOff");
- mWakeAndHandOff.setReferenceCounted(false);
-
- mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
-
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-
- mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
-
- mLockPatternUtils = lockPatternUtils != null
- ? lockPatternUtils : new LockPatternUtils(mContext);
- mLockPatternUtils.setCurrentUser(UserHandle.USER_OWNER);
-
- WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
-
- mKeyguardViewManager = new KeyguardViewManager(context, wm, mViewMediatorCallback,
- mLockPatternUtils);
-
- mUserPresentIntent = new Intent(Intent.ACTION_USER_PRESENT);
- mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
- final ContentResolver cr = mContext.getContentResolver();
- mShowLockIcon = (Settings.System.getInt(cr, "show_status_bar_lock", 0) == 1);
-
- mScreenOn = mPM.isScreenOn();
-
- mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
- String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND);
- if (soundPath != null) {
- mLockSoundId = mLockSounds.load(soundPath, 1);
- }
- if (soundPath == null || mLockSoundId == 0) {
- Log.w(TAG, "failed to load lock sound from " + soundPath);
- }
- soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);
- if (soundPath != null) {
- mUnlockSoundId = mLockSounds.load(soundPath, 1);
- }
- if (soundPath == null || mUnlockSoundId == 0) {
- Log.w(TAG, "failed to load unlock sound from " + soundPath);
- }
- int lockSoundDefaultAttenuation = context.getResources().getInteger(
- com.android.internal.R.integer.config_lockSoundVolumeDb);
- mLockSoundVolume = (float)Math.pow(10, (float)lockSoundDefaultAttenuation/20);
- }
-
- /**
- * Let us know that the system is ready after startup.
- */
- public void onSystemReady() {
- mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
- synchronized (this) {
- if (DEBUG) Log.d(TAG, "onSystemReady");
- mSystemReady = true;
- mUpdateMonitor.registerCallback(mUpdateCallback);
-
- // Suppress biometric unlock right after boot until things have settled if it is the
- // selected security method, otherwise unsuppress it. It must be unsuppressed if it is
- // not the selected security method for the following reason: if the user starts
- // without a screen lock selected, the biometric unlock would be suppressed the first
- // time they try to use it.
- //
- // Note that the biometric unlock will still not show if it is not the selected method.
- // Calling setAlternateUnlockEnabled(true) simply says don't suppress it if it is the
- // selected method.
- if (mLockPatternUtils.usingBiometricWeak()
- && mLockPatternUtils.isBiometricWeakInstalled()) {
- if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");
- mUpdateMonitor.setAlternateUnlockEnabled(false);
- } else {
- mUpdateMonitor.setAlternateUnlockEnabled(true);
- }
-
- doKeyguardLocked();
- }
- // Most services aren't available until the system reaches the ready state, so we
- // send it here when the device first boots.
- maybeSendUserPresentBroadcast();
- }
-
- /**
- * Called to let us know the screen was turned off.
- * @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER},
- * {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
- * {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
- */
- public void onScreenTurnedOff(int why) {
- synchronized (this) {
- mScreenOn = false;
- if (DEBUG) Log.d(TAG, "onScreenTurnedOff(" + why + ")");
-
- mKeyguardDonePending = false;
-
- // Lock immediately based on setting if secure (user has a pin/pattern/password).
- // This also "locks" the device when not secure to provide easy access to the
- // camera while preventing unwanted input.
- final boolean lockImmediately =
- mLockPatternUtils.getPowerButtonInstantlyLocks() || !mLockPatternUtils.isSecure();
-
- if (mExitSecureCallback != null) {
- if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
- mExitSecureCallback.onKeyguardExitResult(false);
- mExitSecureCallback = null;
- if (!mExternallyEnabled) {
- hideLocked();
- }
- } else if (mShowing) {
- notifyScreenOffLocked();
- resetStateLocked(null);
- } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT
- || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) {
- doKeyguardLaterLocked();
- } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
- // Do not enable the keyguard if the prox sensor forced the screen off.
- } else {
- doKeyguardLocked();
- }
- }
- }
-
- private void doKeyguardLaterLocked() {
- // if the screen turned off because of timeout or the user hit the power button
- // and we don't need to lock immediately, set an alarm
- // to enable it a little bit later (i.e, give the user a chance
- // to turn the screen back on within a certain window without
- // having to unlock the screen)
- final ContentResolver cr = mContext.getContentResolver();
-
- // From DisplaySettings
- long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT,
- KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT);
-
- // From SecuritySettings
- final long lockAfterTimeout = Settings.Secure.getInt(cr,
- Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
- KEYGUARD_LOCK_AFTER_DELAY_DEFAULT);
-
- // From DevicePolicyAdmin
- final long policyTimeout = mLockPatternUtils.getDevicePolicyManager()
- .getMaximumTimeToLock(null, mLockPatternUtils.getCurrentUser());
-
- long timeout;
- if (policyTimeout > 0) {
- // policy in effect. Make sure we don't go beyond policy limit.
- displayTimeout = Math.max(displayTimeout, 0); // ignore negative values
- timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout);
- } else {
- timeout = lockAfterTimeout;
- }
-
- if (timeout <= 0) {
- // Lock now
- mSuppressNextLockSound = true;
- doKeyguardLocked();
- } else {
- // Lock in the future
- long when = SystemClock.elapsedRealtime() + timeout;
- Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
- intent.putExtra("seq", mDelayedShowingSequence);
- PendingIntent sender = PendingIntent.getBroadcast(mContext,
- 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
- if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
- + mDelayedShowingSequence);
- }
- }
-
- private void cancelDoKeyguardLaterLocked() {
- mDelayedShowingSequence++;
- }
-
- /**
- * Let's us know the screen was turned on.
- */
- public void onScreenTurnedOn(KeyguardViewManager.ShowListener showListener) {
- synchronized (this) {
- mScreenOn = true;
- cancelDoKeyguardLaterLocked();
- if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
- if (showListener != null) {
- notifyScreenOnLocked(showListener);
- }
- }
- maybeSendUserPresentBroadcast();
- }
-
- private void maybeSendUserPresentBroadcast() {
- if (mSystemReady && mLockPatternUtils.isLockScreenDisabled()
- && mUserManager.getUsers(true).size() == 1) {
- // Lock screen is disabled because the user has set the preference to "None".
- // In this case, send out ACTION_USER_PRESENT here instead of in
- // handleKeyguardDone()
- sendUserPresentBroadcast();
- }
- }
-
- /**
- * A dream started. We should lock after the usual screen-off lock timeout but only
- * if there is a secure lock pattern.
- */
- public void onDreamingStarted() {
- synchronized (this) {
- if (mScreenOn && mLockPatternUtils.isSecure()) {
- doKeyguardLaterLocked();
- }
- }
- }
-
- /**
- * A dream stopped.
- */
- public void onDreamingStopped() {
- synchronized (this) {
- if (mScreenOn) {
- cancelDoKeyguardLaterLocked();
- }
- }
- }
-
- /**
- * Same semantics as {@link WindowManagerPolicy#enableKeyguard}; provide
- * a way for external stuff to override normal keyguard behavior. For instance
- * the phone app disables the keyguard when it receives incoming calls.
- */
- public void setKeyguardEnabled(boolean enabled) {
- synchronized (this) {
- if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")");
-
- mExternallyEnabled = enabled;
-
- if (!enabled && mShowing) {
- if (mExitSecureCallback != null) {
- if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring");
- // we're in the process of handling a request to verify the user
- // can get past the keyguard. ignore extraneous requests to disable / reenable
- return;
- }
-
- // hiding keyguard that is showing, remember to reshow later
- if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, "
- + "disabling status bar expansion");
- mNeedToReshowWhenReenabled = true;
- hideLocked();
- } else if (enabled && mNeedToReshowWhenReenabled) {
- // reenabled after previously hidden, reshow
- if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling "
- + "status bar expansion");
- mNeedToReshowWhenReenabled = false;
-
- if (mExitSecureCallback != null) {
- if (DEBUG) Log.d(TAG, "onKeyguardExitResult(false), resetting");
- mExitSecureCallback.onKeyguardExitResult(false);
- mExitSecureCallback = null;
- resetStateLocked(null);
- } else {
- showLocked(null);
-
- // block until we know the keygaurd is done drawing (and post a message
- // to unblock us after a timeout so we don't risk blocking too long
- // and causing an ANR).
- mWaitingUntilKeyguardVisible = true;
- mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS);
- if (DEBUG) Log.d(TAG, "waiting until mWaitingUntilKeyguardVisible is false");
- while (mWaitingUntilKeyguardVisible) {
- try {
- wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- if (DEBUG) Log.d(TAG, "done waiting for mWaitingUntilKeyguardVisible");
- }
- }
- }
- }
-
- /**
- * @see android.app.KeyguardManager#exitKeyguardSecurely
- */
- public void verifyUnlock(WindowManagerPolicy.OnKeyguardExitResult callback) {
- synchronized (this) {
- if (DEBUG) Log.d(TAG, "verifyUnlock");
- if (!mUpdateMonitor.isDeviceProvisioned()) {
- // don't allow this api when the device isn't provisioned
- if (DEBUG) Log.d(TAG, "ignoring because device isn't provisioned");
- callback.onKeyguardExitResult(false);
- } else if (mExternallyEnabled) {
- // this only applies when the user has externally disabled the
- // keyguard. this is unexpected and means the user is not
- // using the api properly.
- Log.w(TAG, "verifyUnlock called when not externally disabled");
- callback.onKeyguardExitResult(false);
- } else if (mExitSecureCallback != null) {
- // already in progress with someone else
- callback.onKeyguardExitResult(false);
- } else {
- mExitSecureCallback = callback;
- verifyUnlockLocked();
- }
- }
- }
-
- /**
- * Is the keyguard currently showing?
- */
- public boolean isShowing() {
- return mShowing;
- }
-
- /**
- * Is the keyguard currently showing and not being force hidden?
- */
- public boolean isShowingAndNotHidden() {
- return mShowing && !mHidden;
- }
-
- /**
- * Notify us when the keyguard is hidden by another window
- */
- public void setHidden(boolean isHidden) {
- if (DEBUG) Log.d(TAG, "setHidden " + isHidden);
- mUpdateMonitor.sendKeyguardVisibilityChanged(!isHidden);
- mHandler.removeMessages(SET_HIDDEN);
- Message msg = mHandler.obtainMessage(SET_HIDDEN, (isHidden ? 1 : 0), 0);
- mHandler.sendMessage(msg);
- }
-
- /**
- * Handles SET_HIDDEN message sent by setHidden()
- */
- private void handleSetHidden(boolean isHidden) {
- synchronized (KeyguardViewMediator.this) {
- if (mHidden != isHidden) {
- mHidden = isHidden;
- updateActivityLockScreenState();
- adjustStatusBarLocked();
- }
- }
- }
-
- /**
- * Used by PhoneWindowManager to enable the keyguard due to a user activity timeout.
- * This must be safe to call from any thread and with any window manager locks held.
- */
- public void doKeyguardTimeout(Bundle options) {
- mHandler.removeMessages(KEYGUARD_TIMEOUT);
- Message msg = mHandler.obtainMessage(KEYGUARD_TIMEOUT, options);
- mHandler.sendMessage(msg);
- }
-
- /**
- * Given the state of the keyguard, is the input restricted?
- * Input is restricted when the keyguard is showing, or when the keyguard
- * was suppressed by an app that disabled the keyguard or we haven't been provisioned yet.
- */
- public boolean isInputRestricted() {
- return mShowing || mNeedToReshowWhenReenabled || !mUpdateMonitor.isDeviceProvisioned();
- }
-
- private void doKeyguardLocked() {
- doKeyguardLocked(null);
- }
-
- /**
- * Enable the keyguard if the settings are appropriate.
- */
- private void doKeyguardLocked(Bundle options) {
- // if another app is disabling us, don't show
- if (!mExternallyEnabled) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
-
- // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
- // for an occasional ugly flicker in this situation:
- // 1) receive a call with the screen on (no keyguard) or make a call
- // 2) screen times out
- // 3) user hits key to turn screen back on
- // instead, we reenable the keyguard when we know the screen is off and the call
- // ends (see the broadcast receiver below)
- // TODO: clean this up when we have better support at the window manager level
- // for apps that wish to be on top of the keyguard
- return;
- }
-
- // if the keyguard is already showing, don't bother
- if (mKeyguardViewManager.isShowing()) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
- return;
- }
-
- // if the setup wizard hasn't run yet, don't show
- final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
- false);
- final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
- final IccCardConstants.State state = mUpdateMonitor.getSimState();
- final boolean lockedOrMissing = state.isPinLocked()
- || ((state == IccCardConstants.State.ABSENT
- || state == IccCardConstants.State.PERM_DISABLED)
- && requireSim);
-
- if (!lockedOrMissing && !provisioned) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
- + " and the sim is not locked or missing");
- return;
- }
-
- if (mUserManager.getUsers(true).size() < 2
- && mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
- return;
- }
-
- if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
- showLocked(options);
- }
-
- /**
- * Dismiss the keyguard through the security layers.
- */
- public void dismiss() {
- if (mShowing && !mHidden) {
- mKeyguardViewManager.dismiss();
- }
- }
-
- /**
- * Send message to keyguard telling it to reset its state.
- * @param options options about how to show the keyguard
- * @see #handleReset()
- */
- private void resetStateLocked(Bundle options) {
- if (DEBUG) Log.e(TAG, "resetStateLocked");
- Message msg = mHandler.obtainMessage(RESET, options);
- mHandler.sendMessage(msg);
- }
-
- /**
- * Send message to keyguard telling it to verify unlock
- * @see #handleVerifyUnlock()
- */
- private void verifyUnlockLocked() {
- if (DEBUG) Log.d(TAG, "verifyUnlockLocked");
- mHandler.sendEmptyMessage(VERIFY_UNLOCK);
- }
-
-
- /**
- * Send a message to keyguard telling it the screen just turned on.
- * @see #onScreenTurnedOff(int)
- * @see #handleNotifyScreenOff
- */
- private void notifyScreenOffLocked() {
- if (DEBUG) Log.d(TAG, "notifyScreenOffLocked");
- mHandler.sendEmptyMessage(NOTIFY_SCREEN_OFF);
- }
-
- /**
- * Send a message to keyguard telling it the screen just turned on.
- * @see #onScreenTurnedOn()
- * @see #handleNotifyScreenOn
- */
- private void notifyScreenOnLocked(KeyguardViewManager.ShowListener showListener) {
- if (DEBUG) Log.d(TAG, "notifyScreenOnLocked");
- Message msg = mHandler.obtainMessage(NOTIFY_SCREEN_ON, showListener);
- mHandler.sendMessage(msg);
- }
-
- /**
- * Send message to keyguard telling it about a wake key so it can adjust
- * its state accordingly and then poke the wake lock when it is ready.
- * @param keyCode The wake key.
- * @see #handleWakeWhenReady
- * @see #onWakeKeyWhenKeyguardShowingTq(int)
- */
- private void wakeWhenReady(int keyCode) {
- if (DBG_WAKE) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
-
- /**
- * acquire the handoff lock that will keep the cpu running. this will
- * be released once the keyguard has set itself up and poked the other wakelock
- * in {@link #handleWakeWhenReady(int)}
- */
- mWakeAndHandOff.acquire();
-
- Message msg = mHandler.obtainMessage(WAKE_WHEN_READY, keyCode, 0);
- mHandler.sendMessage(msg);
- }
-
- /**
- * Send message to keyguard telling it to show itself
- * @see #handleShow()
- */
- private void showLocked(Bundle options) {
- if (DEBUG) Log.d(TAG, "showLocked");
- // ensure we stay awake until we are finished displaying the keyguard
- mShowKeyguardWakeLock.acquire();
- Message msg = mHandler.obtainMessage(SHOW, options);
- mHandler.sendMessage(msg);
- }
-
- /**
- * Send message to keyguard telling it to hide itself
- * @see #handleHide()
- */
- private void hideLocked() {
- if (DEBUG) Log.d(TAG, "hideLocked");
- Message msg = mHandler.obtainMessage(HIDE);
- mHandler.sendMessage(msg);
- }
-
- public boolean isSecure() {
- return mLockPatternUtils.isSecure()
- || KeyguardUpdateMonitor.getInstance(mContext).isSimPinSecure();
- }
-
- /**
- * Update the newUserId. Call while holding WindowManagerService lock.
- * NOTE: Should only be called by KeyguardViewMediator in response to the user id changing.
- *
- * @param newUserId The id of the incoming user.
- */
- public void setCurrentUser(int newUserId) {
- mLockPatternUtils.setCurrentUser(newUserId);
- }
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DELAYED_KEYGUARD_ACTION.equals(intent.getAction())) {
- final int sequence = intent.getIntExtra("seq", 0);
- if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
- + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
- synchronized (KeyguardViewMediator.this) {
- if (mDelayedShowingSequence == sequence) {
- // Don't play lockscreen SFX if the screen went off due to timeout.
- mSuppressNextLockSound = true;
- doKeyguardLocked();
- }
- }
- }
- }
- };
-
- /**
- * When a key is received when the screen is off and the keyguard is showing,
- * we need to decide whether to actually turn on the screen, and if so, tell
- * the keyguard to prepare itself and poke the wake lock when it is ready.
- *
- * The 'Tq' suffix is per the documentation in {@link WindowManagerPolicy}.
- * Be sure not to take any action that takes a long time; any significant
- * action should be posted to a handler.
- *
- * @param keyCode The keycode of the key that woke the device
- */
- public void onWakeKeyWhenKeyguardShowingTq(int keyCode) {
- if (DEBUG) Log.d(TAG, "onWakeKeyWhenKeyguardShowing(" + keyCode + ")");
-
- // give the keyguard view manager a chance to adjust the state of the
- // keyguard based on the key that woke the device before poking
- // the wake lock
- wakeWhenReady(keyCode);
- }
-
- /**
- * When a wake motion such as an external mouse movement is received when the screen
- * is off and the keyguard is showing, we need to decide whether to actually turn
- * on the screen, and if so, tell the keyguard to prepare itself and poke the wake
- * lock when it is ready.
- *
- * The 'Tq' suffix is per the documentation in {@link WindowManagerPolicy}.
- * Be sure not to take any action that takes a long time; any significant
- * action should be posted to a handler.
- */
- public void onWakeMotionWhenKeyguardShowingTq() {
- if (DEBUG) Log.d(TAG, "onWakeMotionWhenKeyguardShowing()");
-
- // give the keyguard view manager a chance to adjust the state of the
- // keyguard based on the key that woke the device before poking
- // the wake lock
- wakeWhenReady(KeyEvent.KEYCODE_UNKNOWN);
- }
-
- public void keyguardDone(boolean authenticated, boolean wakeup) {
- mKeyguardDonePending = false;
- synchronized (this) {
- EventLog.writeEvent(70000, 2);
- if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")");
- Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
- msg.arg1 = wakeup ? 1 : 0;
- mHandler.sendMessage(msg);
-
- if (authenticated) {
- mUpdateMonitor.clearFailedUnlockAttempts();
- }
-
- if (mExitSecureCallback != null) {
- mExitSecureCallback.onKeyguardExitResult(authenticated);
- mExitSecureCallback = null;
-
- if (authenticated) {
- // after succesfully exiting securely, no need to reshow
- // the keyguard when they've released the lock
- mExternallyEnabled = true;
- mNeedToReshowWhenReenabled = false;
- }
- }
- }
- }
-
- /**
- * This handler will be associated with the policy thread, which will also
- * be the UI thread of the keyguard. Since the apis of the policy, and therefore
- * this class, can be called by other threads, any action that directly
- * interacts with the keyguard ui should be posted to this handler, rather
- * than called directly.
- */
- private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case SHOW:
- handleShow((Bundle) msg.obj);
- return ;
- case HIDE:
- handleHide();
- return ;
- case RESET:
- handleReset((Bundle) msg.obj);
- return ;
- case VERIFY_UNLOCK:
- handleVerifyUnlock();
- return;
- case NOTIFY_SCREEN_OFF:
- handleNotifyScreenOff();
- return;
- case NOTIFY_SCREEN_ON:
- handleNotifyScreenOn((KeyguardViewManager.ShowListener)msg.obj);
- return;
- case WAKE_WHEN_READY:
- handleWakeWhenReady(msg.arg1);
- return;
- case KEYGUARD_DONE:
- handleKeyguardDone(msg.arg1 != 0);
- return;
- case KEYGUARD_DONE_DRAWING:
- handleKeyguardDoneDrawing();
- return;
- case KEYGUARD_DONE_AUTHENTICATING:
- keyguardDone(true, true);
- return;
- case SET_HIDDEN:
- handleSetHidden(msg.arg1 != 0);
- break;
- case KEYGUARD_TIMEOUT:
- synchronized (KeyguardViewMediator.this) {
- doKeyguardLocked((Bundle) msg.obj);
- }
- break;
- case SHOW_ASSISTANT:
- handleShowAssistant();
- break;
- }
- }
- };
-
- /**
- * @see #keyguardDone
- * @see #KEYGUARD_DONE
- */
- private void handleKeyguardDone(boolean wakeup) {
- if (DEBUG) Log.d(TAG, "handleKeyguardDone");
- handleHide();
- if (wakeup) {
- wakeUp();
- }
-
- sendUserPresentBroadcast();
- }
-
- private void sendUserPresentBroadcast() {
- if (!(mContext instanceof Activity)) {
- final UserHandle currentUser = new UserHandle(mLockPatternUtils.getCurrentUser());
- mContext.sendBroadcastAsUser(mUserPresentIntent, currentUser);
- }
- }
-
- /**
- * @see #keyguardDoneDrawing
- * @see #KEYGUARD_DONE_DRAWING
- */
- private void handleKeyguardDoneDrawing() {
- synchronized(this) {
- if (false) Log.d(TAG, "handleKeyguardDoneDrawing");
- if (mWaitingUntilKeyguardVisible) {
- if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing: notifying mWaitingUntilKeyguardVisible");
- mWaitingUntilKeyguardVisible = false;
- notifyAll();
-
- // there will usually be two of these sent, one as a timeout, and one
- // as a result of the callback, so remove any remaining messages from
- // the queue
- mHandler.removeMessages(KEYGUARD_DONE_DRAWING);
- }
- }
- }
-
- private void playSounds(boolean locked) {
- // User feedback for keyguard.
-
- if (mSuppressNextLockSound) {
- mSuppressNextLockSound = false;
- return;
- }
-
- final ContentResolver cr = mContext.getContentResolver();
- if (Settings.System.getInt(cr, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) == 1) {
- final int whichSound = locked
- ? mLockSoundId
- : mUnlockSoundId;
- mLockSounds.stop(mLockSoundStreamId);
- // Init mAudioManager
- if (mAudioManager == null) {
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- if (mAudioManager == null) return;
- mMasterStreamType = mAudioManager.getMasterStreamType();
- }
- // If the stream is muted, don't play the sound
- if (mAudioManager.isStreamMute(mMasterStreamType)) return;
-
- mLockSoundStreamId = mLockSounds.play(whichSound,
- mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
- }
- }
-
- private void updateActivityLockScreenState() {
- try {
- ActivityManagerNative.getDefault().setLockScreenShown(
- mShowing && !mHidden);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * Handle message sent by {@link #showLocked}.
- * @see #SHOW
- */
- private void handleShow(Bundle options) {
- synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleShow");
- if (!mSystemReady) return;
-
- mKeyguardViewManager.show(options);
- mShowing = true;
- mKeyguardDonePending = false;
- updateActivityLockScreenState();
- adjustStatusBarLocked();
- userActivity();
- try {
- ActivityManagerNative.getDefault().closeSystemDialogs("lock");
- } catch (RemoteException e) {
- }
-
- // Do this at the end to not slow down display of the keyguard.
- playSounds(true);
-
- mShowKeyguardWakeLock.release();
- }
- }
-
- /**
- * Handle message sent by {@link #hideLocked()}
- * @see #HIDE
- */
- private void handleHide() {
- synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleHide");
- if (mWakeAndHandOff.isHeld()) {
- Log.w(TAG, "attempt to hide the keyguard while waking, ignored");
- return;
- }
-
- // only play "unlock" noises if not on a call (since the incall UI
- // disables the keyguard)
- if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
- playSounds(false);
- }
-
- mKeyguardViewManager.hide();
- mShowing = false;
- mKeyguardDonePending = false;
- updateActivityLockScreenState();
- adjustStatusBarLocked();
- }
- }
-
- private void adjustStatusBarLocked() {
- if (mStatusBarManager == null) {
- mStatusBarManager = (StatusBarManager)
- mContext.getSystemService(Context.STATUS_BAR_SERVICE);
- }
- if (mStatusBarManager == null) {
- Log.w(TAG, "Could not get status bar manager");
- } else {
- if (mShowLockIcon) {
- // Give feedback to user when secure keyguard is active and engaged
- if (mShowing && isSecure()) {
- if (!mShowingLockIcon) {
- String contentDescription = mContext.getString(
- com.android.internal.R.string.status_bar_device_locked);
- mStatusBarManager.setIcon("secure",
- com.android.internal.R.drawable.stat_sys_secure, 0,
- contentDescription);
- mShowingLockIcon = true;
- }
- } else {
- if (mShowingLockIcon) {
- mStatusBarManager.removeIcon("secure");
- mShowingLockIcon = false;
- }
- }
- }
-
- // Disable aspects of the system/status/navigation bars that must not be re-enabled by
- // windows that appear on top, ever
- int flags = StatusBarManager.DISABLE_NONE;
- if (mShowing) {
- // Permanently disable components not available when keyguard is enabled
- // (like recents). Temporary enable/disable (e.g. the "back" button) are
- // done in KeyguardHostView.
- flags |= StatusBarManager.DISABLE_RECENT;
- if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
- // showing secure lockscreen; disable expanding.
- flags |= StatusBarManager.DISABLE_EXPAND;
- }
- if (isSecure()) {
- // showing secure lockscreen; disable ticker.
- flags |= StatusBarManager.DISABLE_NOTIFICATION_TICKER;
- }
- if (!isAssistantAvailable()) {
- flags |= StatusBarManager.DISABLE_SEARCH;
- }
- }
-
- if (DEBUG) {
- Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mHidden=" + mHidden
- + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
- }
-
- if (!(mContext instanceof Activity)) {
- mStatusBarManager.disable(flags);
- }
- }
- }
-
- /**
- * Handle message sent by {@link #wakeWhenReady(int)}
- * @param keyCode The key that woke the device.
- * @see #WAKE_WHEN_READY
- */
- private void handleWakeWhenReady(int keyCode) {
- synchronized (KeyguardViewMediator.this) {
- if (DBG_WAKE) Log.d(TAG, "handleWakeWhenReady(" + keyCode + ")");
-
- // this should result in a call to 'poke wakelock' which will set a timeout
- // on releasing the wakelock
- if (!mKeyguardViewManager.wakeWhenReadyTq(keyCode)) {
- // poke wakelock ourselves if keyguard is no longer active
- Log.w(TAG, "mKeyguardViewManager.wakeWhenReadyTq did not poke wake lock, so poke it ourselves");
- userActivity();
- }
-
- /**
- * Now that the keyguard is ready and has poked the wake lock, we can
- * release the handoff wakelock
- */
- mWakeAndHandOff.release();
- }
- }
-
- /**
- * Handle message sent by {@link #resetStateLocked(Bundle)}
- * @see #RESET
- */
- private void handleReset(Bundle options) {
- if (options == null) {
- options = new Bundle();
- }
- options.putBoolean(KeyguardViewManager.IS_SWITCHING_USER, mSwitchingUser);
- synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleReset");
- mKeyguardViewManager.reset(options);
- }
- }
-
- /**
- * Handle message sent by {@link #verifyUnlock}
- * @see #VERIFY_UNLOCK
- */
- private void handleVerifyUnlock() {
- synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
- mKeyguardViewManager.verifyUnlock();
- mShowing = true;
- updateActivityLockScreenState();
- }
- }
-
- /**
- * Handle message sent by {@link #notifyScreenOffLocked()}
- * @see #NOTIFY_SCREEN_OFF
- */
- private void handleNotifyScreenOff() {
- synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleNotifyScreenOff");
- mKeyguardViewManager.onScreenTurnedOff();
- }
- }
-
- /**
- * Handle message sent by {@link #notifyScreenOnLocked()}
- * @see #NOTIFY_SCREEN_ON
- */
- private void handleNotifyScreenOn(KeyguardViewManager.ShowListener showListener) {
- synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleNotifyScreenOn");
- mKeyguardViewManager.onScreenTurnedOn(showListener);
- }
- }
-
- public boolean isDismissable() {
- return mKeyguardDonePending || !isSecure();
- }
-
- public void showAssistant() {
- Message msg = mHandler.obtainMessage(SHOW_ASSISTANT);
- mHandler.sendMessage(msg);
- }
-
- public void handleShowAssistant() {
- mKeyguardViewManager.showAssistant();
- }
-
- private boolean isAssistantAvailable() {
- return mSearchManager != null
- && mSearchManager.getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
- }
-
- public static MultiUserAvatarCache getAvatarCache() {
- return sMultiUserAvatarCache;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
deleted file mode 100644
index 4410063..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.view.View;
-
-public class KeyguardViewStateManager implements
- SlidingChallengeLayout.OnChallengeScrolledListener,
- ChallengeLayout.OnBouncerStateChangedListener {
-
- private KeyguardWidgetPager mKeyguardWidgetPager;
- private ChallengeLayout mChallengeLayout;
- private KeyguardHostView mKeyguardHostView;
- private int[] mTmpPoint = new int[2];
- private int[] mTmpLoc = new int[2];
-
- private KeyguardSecurityView mKeyguardSecurityContainer;
- private static final int SCREEN_ON_HINT_DURATION = 1000;
- private static final int SCREEN_ON_RING_HINT_DELAY = 300;
- Handler mMainQueue = new Handler(Looper.myLooper());
-
- int mLastScrollState = SlidingChallengeLayout.SCROLL_STATE_IDLE;
-
- // Paged view state
- private int mPageListeningToSlider = -1;
- private int mCurrentPage = -1;
- private int mPageIndexOnPageBeginMoving = -1;
-
- int mChallengeTop = 0;
-
- public KeyguardViewStateManager(KeyguardHostView hostView) {
- mKeyguardHostView = hostView;
- }
-
- public void setPagedView(KeyguardWidgetPager pagedView) {
- mKeyguardWidgetPager = pagedView;
- updateEdgeSwiping();
- }
-
- public void setChallengeLayout(ChallengeLayout layout) {
- mChallengeLayout = layout;
- updateEdgeSwiping();
- }
-
- private void updateEdgeSwiping() {
- if (mChallengeLayout != null && mKeyguardWidgetPager != null) {
- if (mChallengeLayout.isChallengeOverlapping()) {
- mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(true);
- } else {
- mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(false);
- }
- }
- }
-
- public boolean isChallengeShowing() {
- if (mChallengeLayout != null) {
- return mChallengeLayout.isChallengeShowing();
- }
- return false;
- }
-
- public boolean isChallengeOverlapping() {
- if (mChallengeLayout != null) {
- return mChallengeLayout.isChallengeOverlapping();
- }
- return false;
- }
-
- public void setSecurityViewContainer(KeyguardSecurityView container) {
- mKeyguardSecurityContainer = container;
- }
-
- public void showBouncer(boolean show) {
- mChallengeLayout.showBouncer();
- }
-
- public boolean isBouncing() {
- return mChallengeLayout.isBouncing();
- }
-
- public void fadeOutSecurity(int duration) {
- ((View) mKeyguardSecurityContainer).animate().alpha(0).setDuration(duration);
- }
-
- public void fadeInSecurity(int duration) {
- ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration);
- }
-
- public void onPageBeginMoving() {
- if (mChallengeLayout.isChallengeOverlapping() &&
- mChallengeLayout instanceof SlidingChallengeLayout) {
- SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
- scl.fadeOutChallenge();
- mPageIndexOnPageBeginMoving = mKeyguardWidgetPager.getCurrentPage();
- }
- // We use mAppWidgetToShow to show a particular widget after you add it--
- // once the user swipes a page we clear that behavior
- if (mKeyguardHostView != null) {
- mKeyguardHostView.clearAppWidgetToShow();
- mKeyguardHostView.setOnDismissAction(null);
- }
- if (mHideHintsRunnable != null) {
- mMainQueue.removeCallbacks(mHideHintsRunnable);
- mHideHintsRunnable = null;
- }
- }
-
- public void onPageEndMoving() {
- mPageIndexOnPageBeginMoving = -1;
- }
-
- public void onPageSwitching(View newPage, int newPageIndex) {
- if (mKeyguardWidgetPager != null && mChallengeLayout instanceof SlidingChallengeLayout) {
- boolean isCameraPage = newPage instanceof CameraWidgetFrame;
- ((SlidingChallengeLayout) mChallengeLayout).setChallengeInteractive(!isCameraPage);
- }
-
- // If the page we're settling to is the same as we started on, and the action of
- // moving the page hid the security, we restore it immediately.
- if (mPageIndexOnPageBeginMoving == mKeyguardWidgetPager.getNextPage() &&
- mChallengeLayout instanceof SlidingChallengeLayout) {
- SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
- scl.fadeInChallenge();
- mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(-1);
- }
- mPageIndexOnPageBeginMoving = -1;
- }
-
- public void onPageSwitched(View newPage, int newPageIndex) {
- // Reset the previous page size and ensure the current page is sized appropriately.
- // We only modify the page state if it is not currently under control by the slider.
- // This prevents conflicts.
-
- // If the page hasn't switched, don't bother with any of this
- if (mCurrentPage == newPageIndex) return;
-
- if (mKeyguardWidgetPager != null && mChallengeLayout != null) {
- KeyguardWidgetFrame prevPage = mKeyguardWidgetPager.getWidgetPageAt(mCurrentPage);
- if (prevPage != null && mCurrentPage != mPageListeningToSlider && mCurrentPage
- != mKeyguardWidgetPager.getWidgetToResetOnPageFadeOut()) {
- prevPage.resetSize();
- }
-
- KeyguardWidgetFrame newCurPage = mKeyguardWidgetPager.getWidgetPageAt(newPageIndex);
- boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
- if (challengeOverlapping && !newCurPage.isSmall()
- && mPageListeningToSlider != newPageIndex) {
- newCurPage.shrinkWidget();
- }
- }
-
- mCurrentPage = newPageIndex;
- }
-
- private int getChallengeTopRelativeToFrame(KeyguardWidgetFrame frame, int top) {
- mTmpPoint[0] = 0;
- mTmpPoint[1] = top;
- mapPoint((View) mChallengeLayout, frame, mTmpPoint);
- return mTmpPoint[1];
- }
-
- /**
- * Simple method to map a point from one view's coordinates to another's. Note: this method
- * doesn't account for transforms, so if the views will be transformed, this should not be used.
- *
- * @param fromView The view to which the point is relative
- * @param toView The view into which the point should be mapped
- * @param pt The point
- */
- private void mapPoint(View fromView, View toView, int pt[]) {
- fromView.getLocationInWindow(mTmpLoc);
-
- int x = mTmpLoc[0];
- int y = mTmpLoc[1];
-
- toView.getLocationInWindow(mTmpLoc);
- int vX = mTmpLoc[0];
- int vY = mTmpLoc[1];
-
- pt[0] += x - vX;
- pt[1] += y - vY;
- }
-
- private void userActivity() {
- if (mKeyguardHostView != null) {
- mKeyguardHostView.onUserActivityTimeoutChanged();
- mKeyguardHostView.userActivity();
- }
- }
-
- @Override
- public void onScrollStateChanged(int scrollState) {
- if (mKeyguardWidgetPager == null || mChallengeLayout == null) return;
-
- boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
-
- if (scrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
- KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
- if (frame == null) return;
-
- if (!challengeOverlapping) {
- if (!mKeyguardWidgetPager.isPageMoving()) {
- frame.resetSize();
- userActivity();
- } else {
- mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(mPageListeningToSlider);
- }
- }
- if (frame.isSmall()) {
- // This is to make sure that if the scroller animation gets cut off midway
- // that the frame doesn't stay in a partial down position.
- frame.setFrameHeight(frame.getSmallFrameHeight());
- }
- if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
- frame.hideFrame(this);
- }
- updateEdgeSwiping();
-
- if (mChallengeLayout.isChallengeShowing()) {
- mKeyguardSecurityContainer.onResume(KeyguardSecurityView.VIEW_REVEALED);
- } else {
- mKeyguardSecurityContainer.onPause();
- }
- mPageListeningToSlider = -1;
- } else if (mLastScrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
- // Whether dragging or settling, if the last state was idle, we use this signal
- // to update the current page who will receive events from the sliding challenge.
- // We resize the frame as appropriate.
- mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
- KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
- if (frame == null) return;
-
- // Skip showing the frame and shrinking the widget if we are
- if (!mChallengeLayout.isBouncing()) {
- if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
- frame.showFrame(this);
- }
-
- // As soon as the security begins sliding, the widget becomes small (if it wasn't
- // small to begin with).
- if (!frame.isSmall()) {
- // We need to fetch the final page, in case the pages are in motion.
- mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
- frame.shrinkWidget(false);
- }
- } else {
- if (!frame.isSmall()) {
- // We need to fetch the final page, in case the pages are in motion.
- mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
- }
- }
-
- // View is on the move. Pause the security view until it completes.
- mKeyguardSecurityContainer.onPause();
- }
- mLastScrollState = scrollState;
- }
-
- @Override
- public void onScrollPositionChanged(float scrollPosition, int challengeTop) {
- mChallengeTop = challengeTop;
- KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
- if (frame != null && mLastScrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
- frame.adjustFrame(getChallengeTopRelativeToFrame(frame, mChallengeTop));
- }
- }
-
- private Runnable mHideHintsRunnable = new Runnable() {
- @Override
- public void run() {
- if (mKeyguardWidgetPager != null) {
- mKeyguardWidgetPager.hideOutlinesAndSidePages();
- }
- }
- };
-
- public void showUsabilityHints() {
- mMainQueue.postDelayed( new Runnable() {
- @Override
- public void run() {
- mKeyguardSecurityContainer.showUsabilityHint();
- }
- } , SCREEN_ON_RING_HINT_DELAY);
- mKeyguardWidgetPager.showInitialPageHints();
- if (mHideHintsRunnable != null) {
- mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION);
- }
- }
-
- // ChallengeLayout.OnBouncerStateChangedListener
- @Override
- public void onBouncerStateChanged(boolean bouncerActive) {
- if (bouncerActive) {
- mKeyguardWidgetPager.zoomOutToBouncer();
- } else {
- mKeyguardWidgetPager.zoomInFromBouncer();
- if (mKeyguardHostView != null) {
- mKeyguardHostView.setOnDismissAction(null);
- }
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
deleted file mode 100644
index 257fd27..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-
-public class KeyguardWidgetCarousel extends KeyguardWidgetPager {
-
- private float mAdjacentPagesAngle;
- private static float MAX_SCROLL_PROGRESS = 1.3f;
- private static float CAMERA_DISTANCE = 10000;
- protected AnimatorSet mChildrenTransformsAnimator;
- float[] mTmpTransform = new float[3];
-
- public KeyguardWidgetCarousel(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardWidgetCarousel(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardWidgetCarousel(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mAdjacentPagesAngle = context.getResources().getInteger(R.integer.kg_carousel_angle);
- }
-
- protected float getMaxScrollProgress() {
- return MAX_SCROLL_PROGRESS;
- }
-
- public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
- View child = getChildAt(index);
- if (child == null) return 0f;
-
- boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1;
- float scrollProgress = getScrollProgress(screenCenter, child, index);
-
- if (isOverScrollChild(index, scrollProgress)) {
- return 1.0f;
- } else if ((showSidePages && inVisibleRange) || index == getNextPage()) {
- scrollProgress = getBoundedScrollProgress(screenCenter, child, index);
- float alpha = 1.0f - 1.0f * Math.abs(scrollProgress / MAX_SCROLL_PROGRESS);
- return alpha;
- } else {
- return 0f;
- }
- }
-
- public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) {
- boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1;
- if (inVisibleRange) {
- return super.getOutlineAlphaForPage(screenCenter, index, showSidePages);
- } else {
- return 0f;
- }
- }
-
- private void updatePageAlphaValues(int screenCenter) {
- if (mChildrenOutlineFadeAnimation != null) {
- mChildrenOutlineFadeAnimation.cancel();
- mChildrenOutlineFadeAnimation = null;
- }
- boolean showSidePages = mShowingInitialHints || isPageMoving();
- if (!isReordering(false)) {
- for (int i = 0; i < getChildCount(); i++) {
- KeyguardWidgetFrame child = getWidgetPageAt(i);
- if (child != null) {
- float outlineAlpha = getOutlineAlphaForPage(screenCenter, i, showSidePages);
- float contentAlpha = getAlphaForPage(screenCenter, i,showSidePages);
- child.setBackgroundAlpha(outlineAlpha);
- child.setContentAlpha(contentAlpha);
- }
- }
- }
- }
-
- public void showInitialPageHints() {
- mShowingInitialHints = true;
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- boolean inVisibleRange = i >= getNextPage() - 1 && i <= getNextPage() + 1;
- KeyguardWidgetFrame child = getWidgetPageAt(i);
- if (inVisibleRange) {
- child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
- child.setContentAlpha(1f);
- } else {
- child.setBackgroundAlpha(0f);
- child.setContentAlpha(0f);
- }
- }
- }
-
- @Override
- protected void screenScrolled(int screenCenter) {
- mScreenCenter = screenCenter;
- updatePageAlphaValues(screenCenter);
- if (isReordering(false)) return;
- for (int i = 0; i < getChildCount(); i++) {
- KeyguardWidgetFrame v = getWidgetPageAt(i);
- float scrollProgress = getScrollProgress(screenCenter, v, i);
- float boundedProgress = getBoundedScrollProgress(screenCenter, v, i);
- if (v == mDragView || v == null) continue;
- v.setCameraDistance(CAMERA_DISTANCE);
-
- if (isOverScrollChild(i, scrollProgress)) {
- v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress);
- v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0);
- } else {
- int width = v.getMeasuredWidth();
- float pivotX = (width / 2f) + boundedProgress * (width / 2f);
- float pivotY = v.getMeasuredHeight() / 2;
- float rotationY = - mAdjacentPagesAngle * boundedProgress;
- v.setPivotX(pivotX);
- v.setPivotY(pivotY);
- v.setRotationY(rotationY);
- v.setOverScrollAmount(0f, false);
- }
- float alpha = v.getAlpha();
- // If the view has 0 alpha, we set it to be invisible so as to prevent
- // it from accepting touches
- if (alpha == 0) {
- v.setVisibility(INVISIBLE);
- } else if (v.getVisibility() != VISIBLE) {
- v.setVisibility(VISIBLE);
- }
- }
- }
-
- void animatePagesToNeutral() {
- if (mChildrenTransformsAnimator != null) {
- mChildrenTransformsAnimator.cancel();
- mChildrenTransformsAnimator = null;
- }
-
- int count = getChildCount();
- PropertyValuesHolder alpha;
- PropertyValuesHolder outlineAlpha;
- PropertyValuesHolder rotationY;
- ArrayList<Animator> anims = new ArrayList<Animator>();
-
- for (int i = 0; i < count; i++) {
- KeyguardWidgetFrame child = getWidgetPageAt(i);
- boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1);
- if (!inVisibleRange) {
- child.setRotationY(0f);
- }
- alpha = PropertyValuesHolder.ofFloat("contentAlpha", 1.0f);
- outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha",
- KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
- rotationY = PropertyValuesHolder.ofFloat("rotationY", 0f);
- ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha, rotationY);
- child.setVisibility(VISIBLE);
- if (!inVisibleRange) {
- a.setInterpolator(mSlowFadeInterpolator);
- }
- anims.add(a);
- }
-
- int duration = REORDERING_ZOOM_IN_OUT_DURATION;
- mChildrenTransformsAnimator = new AnimatorSet();
- mChildrenTransformsAnimator.playTogether(anims);
-
- mChildrenTransformsAnimator.setDuration(duration);
- mChildrenTransformsAnimator.start();
- }
-
- private void getTransformForPage(int screenCenter, int index, float[] transform) {
- View child = getChildAt(index);
- float boundedProgress = getBoundedScrollProgress(screenCenter, child, index);
- float rotationY = - mAdjacentPagesAngle * boundedProgress;
- int width = child.getMeasuredWidth();
- float pivotX = (width / 2f) + boundedProgress * (width / 2f);
- float pivotY = child.getMeasuredHeight() / 2;
-
- transform[0] = pivotX;
- transform[1] = pivotY;
- transform[2] = rotationY;
- }
-
- Interpolator mFastFadeInterpolator = new Interpolator() {
- Interpolator mInternal = new DecelerateInterpolator(1.5f);
- float mFactor = 2.5f;
- @Override
- public float getInterpolation(float input) {
- return mInternal.getInterpolation(Math.min(mFactor * input, 1f));
- }
- };
-
- Interpolator mSlowFadeInterpolator = new Interpolator() {
- Interpolator mInternal = new AccelerateInterpolator(1.5f);
- float mFactor = 1.3f;
- @Override
- public float getInterpolation(float input) {
- input -= (1 - 1 / mFactor);
- input = mFactor * Math.max(input, 0f);
- return mInternal.getInterpolation(input);
- }
- };
-
- void animatePagesToCarousel() {
- if (mChildrenTransformsAnimator != null) {
- mChildrenTransformsAnimator.cancel();
- mChildrenTransformsAnimator = null;
- }
-
- int count = getChildCount();
- PropertyValuesHolder alpha;
- PropertyValuesHolder outlineAlpha;
- PropertyValuesHolder rotationY;
- PropertyValuesHolder pivotX;
- PropertyValuesHolder pivotY;
- ArrayList<Animator> anims = new ArrayList<Animator>();
-
- for (int i = 0; i < count; i++) {
- KeyguardWidgetFrame child = getWidgetPageAt(i);
- float finalAlpha = getAlphaForPage(mScreenCenter, i, true);
- float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i, true);
- getTransformForPage(mScreenCenter, i, mTmpTransform);
-
- boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1);
-
- ObjectAnimator a;
- alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalAlpha);
- outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", finalOutlineAlpha);
- pivotX = PropertyValuesHolder.ofFloat("pivotX", mTmpTransform[0]);
- pivotY = PropertyValuesHolder.ofFloat("pivotY", mTmpTransform[1]);
- rotationY = PropertyValuesHolder.ofFloat("rotationY", mTmpTransform[2]);
-
- if (inVisibleRange) {
- // for the central pages we animate into a rotated state
- a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha,
- pivotX, pivotY, rotationY);
- } else {
- a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha);
- a.setInterpolator(mFastFadeInterpolator);
- }
- anims.add(a);
- }
-
- int duration = REORDERING_ZOOM_IN_OUT_DURATION;
- mChildrenTransformsAnimator = new AnimatorSet();
- mChildrenTransformsAnimator.playTogether(anims);
-
- mChildrenTransformsAnimator.setDuration(duration);
- mChildrenTransformsAnimator.start();
- }
-
- protected void reorderStarting() {
- mViewStateManager.fadeOutSecurity(REORDERING_ZOOM_IN_OUT_DURATION);
- animatePagesToNeutral();
- }
-
- protected boolean zoomIn(final Runnable onCompleteRunnable) {
- animatePagesToCarousel();
- return super.zoomIn(onCompleteRunnable);
- }
-
- @Override
- protected void onEndReordering() {
- super.onEndReordering();
- mViewStateManager.fadeInSecurity(REORDERING_ZOOM_IN_OUT_DURATION);
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
deleted file mode 100644
index babb9cb..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-
-public class KeyguardWidgetFrame extends FrameLayout {
- private final static PorterDuffXfermode sAddBlendMode =
- new PorterDuffXfermode(PorterDuff.Mode.ADD);
-
- static final float OUTLINE_ALPHA_MULTIPLIER = 0.6f;
- static final int HOVER_OVER_DELETE_DROP_TARGET_OVERLAY_COLOR = 0x99FF0000;
-
- // Temporarily disable this for the time being until we know why the gfx is messing up
- static final boolean ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY = true;
-
- private int mGradientColor;
- private LinearGradient mForegroundGradient;
- private LinearGradient mLeftToRightGradient;
- private LinearGradient mRightToLeftGradient;
- private Paint mGradientPaint = new Paint();
- boolean mLeftToRight = true;
-
- private float mOverScrollAmount = 0f;
- private final Rect mForegroundRect = new Rect();
- private int mForegroundAlpha = 0;
- private CheckLongPressHelper mLongPressHelper;
- private Animator mFrameFade;
- private boolean mIsSmall = false;
- private Handler mWorkerHandler;
-
- private float mBackgroundAlpha;
- private float mContentAlpha;
- private float mBackgroundAlphaMultiplier = 1.0f;
- private Drawable mBackgroundDrawable;
- private Rect mBackgroundRect = new Rect();
-
- // These variables are all needed in order to size things properly before we're actually
- // measured.
- private int mSmallWidgetHeight;
- private int mSmallFrameHeight;
- private boolean mWidgetLockedSmall = false;
- private int mMaxChallengeTop = -1;
- private int mFrameStrokeAdjustment;
- private boolean mPerformAppWidgetSizeUpdateOnBootComplete;
-
- // This will hold the width value before we've actually been measured
- private int mFrameHeight;
-
- private boolean mIsHoveringOverDeleteDropTarget;
-
- // Multiple callers may try and adjust the alpha of the frame. When a caller shows
- // the outlines, we give that caller control, and nobody else can fade them out.
- // This prevents animation conflicts.
- private Object mBgAlphaController;
-
- public KeyguardWidgetFrame(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardWidgetFrame(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- mLongPressHelper = new CheckLongPressHelper(this);
-
- Resources res = context.getResources();
- // TODO: this padding should really correspond to the padding embedded in the background
- // drawable (ie. outlines).
- float density = res.getDisplayMetrics().density;
- int padding = (int) (res.getDisplayMetrics().density * 8);
- setPadding(padding, padding, padding, padding);
-
- mFrameStrokeAdjustment = 2 + (int) (2 * density);
-
- // This will be overriden on phones based on the current security mode, however on tablets
- // we need to specify a height.
- mSmallWidgetHeight =
- res.getDimensionPixelSize(com.android.internal.R.dimen.kg_small_widget_height);
- mBackgroundDrawable = res.getDrawable(R.drawable.kg_widget_bg_padded);
- mGradientColor = res.getColor(com.android.internal.R.color.kg_widget_pager_gradient);
- mGradientPaint.setXfermode(sAddBlendMode);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- cancelLongPress();
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
-
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
- }
-
- private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onBootCompleted() {
- if (mPerformAppWidgetSizeUpdateOnBootComplete) {
- performAppWidgetSizeCallbacksIfNecessary();
- mPerformAppWidgetSizeUpdateOnBootComplete = false;
- }
- }
- };
-
- void setIsHoveringOverDeleteDropTarget(boolean isHovering) {
- if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) {
- if (mIsHoveringOverDeleteDropTarget != isHovering) {
- mIsHoveringOverDeleteDropTarget = isHovering;
- invalidate();
- }
- }
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // Watch for longpress events at this level to make sure
- // users can always pick up this widget
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLongPressHelper.postCheckForLongPress(ev);
- break;
- case MotionEvent.ACTION_MOVE:
- mLongPressHelper.onMove(ev);
- break;
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mLongPressHelper.cancelLongPress();
- break;
- }
-
- // Otherwise continue letting touch events fall through to children
- return false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- // Watch for longpress events at this level to make sure
- // users can always pick up this widget
- switch (ev.getAction()) {
- case MotionEvent.ACTION_MOVE:
- mLongPressHelper.onMove(ev);
- break;
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mLongPressHelper.cancelLongPress();
- break;
- }
-
- // We return true here to ensure that we will get cancel / up signal
- // even if none of our children have requested touch.
- return true;
- }
-
- @Override
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- cancelLongPress();
- }
-
- @Override
- public void cancelLongPress() {
- super.cancelLongPress();
- mLongPressHelper.cancelLongPress();
- }
-
-
- private void drawGradientOverlay(Canvas c) {
- mGradientPaint.setShader(mForegroundGradient);
- mGradientPaint.setAlpha(mForegroundAlpha);
- c.drawRect(mForegroundRect, mGradientPaint);
- }
-
- private void drawHoveringOverDeleteOverlay(Canvas c) {
- if (mIsHoveringOverDeleteDropTarget) {
- c.drawColor(HOVER_OVER_DELETE_DROP_TARGET_OVERLAY_COLOR);
- }
- }
-
- protected void drawBg(Canvas canvas) {
- if (mBackgroundAlpha > 0.0f) {
- Drawable bg = mBackgroundDrawable;
-
- bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
- bg.setBounds(mBackgroundRect);
- bg.draw(canvas);
- }
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) {
- canvas.save();
- }
- drawBg(canvas);
- super.dispatchDraw(canvas);
- drawGradientOverlay(canvas);
- if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) {
- drawHoveringOverDeleteOverlay(canvas);
- canvas.restore();
- }
- }
-
- /**
- * Because this view has fading outlines, it is essential that we enable hardware
- * layers on the content (child) so that updating the alpha of the outlines doesn't
- * result in the content layer being recreated.
- */
- public void enableHardwareLayersForContent() {
- View widget = getContent();
- if (widget != null) {
- widget.setLayerType(LAYER_TYPE_HARDWARE, null);
- }
- }
-
- /**
- * Because this view has fading outlines, it is essential that we enable hardware
- * layers on the content (child) so that updating the alpha of the outlines doesn't
- * result in the content layer being recreated.
- */
- public void disableHardwareLayersForContent() {
- View widget = getContent();
- if (widget != null) {
- widget.setLayerType(LAYER_TYPE_NONE, null);
- }
- }
-
- public void enableHardwareLayers() {
- setLayerType(LAYER_TYPE_HARDWARE, null);
- }
-
- public void disableHardwareLayers() {
- setLayerType(LAYER_TYPE_NONE, null);
- }
-
- public View getContent() {
- return getChildAt(0);
- }
-
- public int getContentAppWidgetId() {
- View content = getContent();
- if (content instanceof AppWidgetHostView) {
- return ((AppWidgetHostView) content).getAppWidgetId();
- } else if (content instanceof KeyguardStatusView) {
- return ((KeyguardStatusView) content).getAppWidgetId();
- } else {
- return AppWidgetManager.INVALID_APPWIDGET_ID;
- }
- }
-
- public float getBackgroundAlpha() {
- return mBackgroundAlpha;
- }
-
- public void setBackgroundAlphaMultiplier(float multiplier) {
- if (Float.compare(mBackgroundAlphaMultiplier, multiplier) != 0) {
- mBackgroundAlphaMultiplier = multiplier;
- invalidate();
- }
- }
-
- public float getBackgroundAlphaMultiplier() {
- return mBackgroundAlphaMultiplier;
- }
-
- public void setBackgroundAlpha(float alpha) {
- if (Float.compare(mBackgroundAlpha, alpha) != 0) {
- mBackgroundAlpha = alpha;
- invalidate();
- }
- }
-
- public float getContentAlpha() {
- return mContentAlpha;
- }
-
- public void setContentAlpha(float alpha) {
- mContentAlpha = alpha;
- View content = getContent();
- if (content != null) {
- content.setAlpha(alpha);
- }
- }
-
- /**
- * Depending on whether the security is up, the widget size needs to change
- *
- * @param height The height of the widget, -1 for full height
- */
- private void setWidgetHeight(int height) {
- boolean needLayout = false;
- View widget = getContent();
- if (widget != null) {
- LayoutParams lp = (LayoutParams) widget.getLayoutParams();
- if (lp.height != height) {
- needLayout = true;
- lp.height = height;
- }
- }
- if (needLayout) {
- requestLayout();
- }
- }
-
- public void setMaxChallengeTop(int top) {
- boolean dirty = mMaxChallengeTop != top;
- mMaxChallengeTop = top;
- mSmallWidgetHeight = top - getPaddingTop();
- mSmallFrameHeight = top + getPaddingBottom();
- if (dirty && mIsSmall) {
- setWidgetHeight(mSmallWidgetHeight);
- setFrameHeight(mSmallFrameHeight);
- } else if (dirty && mWidgetLockedSmall) {
- setWidgetHeight(mSmallWidgetHeight);
- }
- }
-
- public boolean isSmall() {
- return mIsSmall;
- }
-
- public void adjustFrame(int challengeTop) {
- int frameHeight = challengeTop + getPaddingBottom();
- setFrameHeight(frameHeight);
- }
-
- public void shrinkWidget(boolean alsoShrinkFrame) {
- mIsSmall = true;
- setWidgetHeight(mSmallWidgetHeight);
-
- if (alsoShrinkFrame) {
- setFrameHeight(mSmallFrameHeight);
- }
- }
-
- public int getSmallFrameHeight() {
- return mSmallFrameHeight;
- }
-
- public void shrinkWidget() {
- shrinkWidget(true);
- }
-
- public void setWidgetLockedSmall(boolean locked) {
- if (locked) {
- setWidgetHeight(mSmallWidgetHeight);
- }
- mWidgetLockedSmall = locked;
- }
-
- public void resetSize() {
- mIsSmall = false;
- if (!mWidgetLockedSmall) {
- setWidgetHeight(LayoutParams.MATCH_PARENT);
- }
- setFrameHeight(getMeasuredHeight());
- }
-
- public void setFrameHeight(int height) {
- mFrameHeight = height;
- mBackgroundRect.set(0, 0, getMeasuredWidth(), Math.min(mFrameHeight, getMeasuredHeight()));
- mForegroundRect.set(mFrameStrokeAdjustment, mFrameStrokeAdjustment,getMeasuredWidth() -
- mFrameStrokeAdjustment, Math.min(getMeasuredHeight(), mFrameHeight) -
- mFrameStrokeAdjustment);
- updateGradient();
- invalidate();
- }
-
- public void hideFrame(Object caller) {
- fadeFrame(caller, false, 0f, KeyguardWidgetPager.CHILDREN_OUTLINE_FADE_OUT_DURATION);
- }
-
- public void showFrame(Object caller) {
- fadeFrame(caller, true, OUTLINE_ALPHA_MULTIPLIER,
- KeyguardWidgetPager.CHILDREN_OUTLINE_FADE_IN_DURATION);
- }
-
- public void fadeFrame(Object caller, boolean takeControl, float alpha, int duration) {
- if (takeControl) {
- mBgAlphaController = caller;
- }
-
- if (mBgAlphaController != caller && mBgAlphaController != null) {
- return;
- }
-
- if (mFrameFade != null) {
- mFrameFade.cancel();
- mFrameFade = null;
- }
- PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", alpha);
- mFrameFade = ObjectAnimator.ofPropertyValuesHolder(this, bgAlpha);
- mFrameFade.setDuration(duration);
- mFrameFade.start();
- }
-
- private void updateGradient() {
- float x0 = mLeftToRight ? 0 : mForegroundRect.width();
- float x1 = mLeftToRight ? mForegroundRect.width(): 0;
- mLeftToRightGradient = new LinearGradient(x0, 0f, x1, 0f,
- mGradientColor, 0, Shader.TileMode.CLAMP);
- mRightToLeftGradient = new LinearGradient(x1, 0f, x0, 0f,
- mGradientColor, 0, Shader.TileMode.CLAMP);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
-
- if (!mIsSmall) {
- mFrameHeight = h;
- }
-
- // mFrameStrokeAdjustment is a cludge to prevent the overlay from drawing outside the
- // rounded rect background.
- mForegroundRect.set(mFrameStrokeAdjustment, mFrameStrokeAdjustment,
- w - mFrameStrokeAdjustment, Math.min(h, mFrameHeight) - mFrameStrokeAdjustment);
-
- mBackgroundRect.set(0, 0, getMeasuredWidth(), Math.min(h, mFrameHeight));
- updateGradient();
- invalidate();
- }
-
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- performAppWidgetSizeCallbacksIfNecessary();
- }
-
- private void performAppWidgetSizeCallbacksIfNecessary() {
- View content = getContent();
- if (!(content instanceof AppWidgetHostView)) return;
-
- if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
- mPerformAppWidgetSizeUpdateOnBootComplete = true;
- return;
- }
-
- // TODO: there's no reason to force the AppWidgetHostView to catch duplicate size calls.
- // We can do that even more cheaply here. It's not an issue right now since we're in the
- // system process and hence no binder calls.
- AppWidgetHostView awhv = (AppWidgetHostView) content;
- float density = getResources().getDisplayMetrics().density;
-
- int width = (int) (content.getMeasuredWidth() / density);
- int height = (int) (content.getMeasuredHeight() / density);
- awhv.updateAppWidgetSize(null, width, height, width, height, true);
- }
-
- void setOverScrollAmount(float r, boolean left) {
- if (Float.compare(mOverScrollAmount, r) != 0) {
- mOverScrollAmount = r;
- mForegroundGradient = left ? mLeftToRightGradient : mRightToLeftGradient;
- mForegroundAlpha = (int) Math.round((0.5f * r * 255));
-
- // We bump up the alpha of the outline to hide the fact that the overlay is drawing
- // over the rounded part of the frame.
- float bgAlpha = Math.min(OUTLINE_ALPHA_MULTIPLIER + r * (1 - OUTLINE_ALPHA_MULTIPLIER),
- 1f);
- setBackgroundAlpha(bgAlpha);
- invalidate();
- }
- }
-
- public void onActive(boolean isActive) {
- // hook for subclasses
- }
-
- public boolean onUserInteraction(MotionEvent event) {
- // hook for subclasses
- return false;
- }
-
- public void onBouncerShowing(boolean showing) {
- // hook for subclasses
- }
-
- public void setWorkerHandler(Handler workerHandler) {
- mWorkerHandler = workerHandler;
- }
-
- public Handler getWorkerHandler() {
- return mWorkerHandler;
- }
-
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
deleted file mode 100644
index 770fafc..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ /dev/null
@@ -1,926 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.Context;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.text.format.DateFormat;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.TextClock;
-
-import com.android.internal.widget.LockPatternUtils;
-
-import java.util.ArrayList;
-import java.util.TimeZone;
-
-public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener,
- OnLongClickListener, ChallengeLayout.OnBouncerStateChangedListener {
-
- ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
- private static float CAMERA_DISTANCE = 10000;
- protected static float OVERSCROLL_MAX_ROTATION = 30;
- private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
-
- private static final int FLAG_HAS_LOCAL_HOUR = 0x1;
- private static final int FLAG_HAS_LOCAL_MINUTE = 0x2;
-
- protected KeyguardViewStateManager mViewStateManager;
- private LockPatternUtils mLockPatternUtils;
-
- // Related to the fading in / out background outlines
- public static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
- public static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
- protected AnimatorSet mChildrenOutlineFadeAnimation;
- protected int mScreenCenter;
- private boolean mHasMeasure = false;
- boolean showHintsAfterLayout = false;
-
- private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
- private static final String TAG = "KeyguardWidgetPager";
- private boolean mCenterSmallWidgetsVertically;
-
- private int mPage = 0;
- private Callbacks mCallbacks;
-
- private int mWidgetToResetAfterFadeOut;
- protected boolean mShowingInitialHints = false;
-
- // A temporary handle to the Add-Widget view
- private View mAddWidgetView;
- private int mLastWidthMeasureSpec;
- private int mLastHeightMeasureSpec;
-
- // Bouncer
- private int mBouncerZoomInOutDuration = 250;
- private float BOUNCER_SCALE_FACTOR = 0.67f;
-
- // Background worker thread: used here for persistence, also made available to widget frames
- private final HandlerThread mBackgroundWorkerThread;
- private final Handler mBackgroundWorkerHandler;
-
- public KeyguardWidgetPager(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardWidgetPager(Context context) {
- this(null, null, 0);
- }
-
- public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
-
- setPageSwitchListener(this);
-
- mBackgroundWorkerThread = new HandlerThread("KeyguardWidgetPager Worker");
- mBackgroundWorkerThread.start();
- mBackgroundWorkerHandler = new Handler(mBackgroundWorkerThread.getLooper());
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- // Clean up the worker thread
- mBackgroundWorkerThread.quit();
- }
-
- public void setViewStateManager(KeyguardViewStateManager viewStateManager) {
- mViewStateManager = viewStateManager;
- }
-
- public void setLockPatternUtils(LockPatternUtils l) {
- mLockPatternUtils = l;
- }
-
- @Override
- public void onPageSwitching(View newPage, int newPageIndex) {
- if (mViewStateManager != null) {
- mViewStateManager.onPageSwitching(newPage, newPageIndex);
- }
- }
-
- @Override
- public void onPageSwitched(View newPage, int newPageIndex) {
- boolean showingClock = false;
- if (newPage instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) newPage;
- if (vg.getChildAt(0) instanceof KeyguardStatusView) {
- showingClock = true;
- }
- }
-
- if (newPage != null &&
- findClockInHierarchy(newPage) == (FLAG_HAS_LOCAL_HOUR | FLAG_HAS_LOCAL_MINUTE)) {
- showingClock = true;
- }
-
- // Disable the status bar clock if we're showing the default status widget
- if (showingClock) {
- setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
- } else {
- setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
- }
-
- // Extend the display timeout if the user switches pages
- if (mPage != newPageIndex) {
- int oldPageIndex = mPage;
- mPage = newPageIndex;
- userActivity();
- KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex);
- if (oldWidgetPage != null) {
- oldWidgetPage.onActive(false);
- }
- KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex);
- if (newWidgetPage != null) {
- newWidgetPage.onActive(true);
- newWidgetPage.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- newWidgetPage.requestAccessibilityFocus();
- }
- if (mParent != null && AccessibilityManager.getInstance(mContext).isEnabled()) {
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_VIEW_SCROLLED);
- onInitializeAccessibilityEvent(event);
- onPopulateAccessibilityEvent(event);
- mParent.requestSendAccessibilityEvent(this, event);
- }
- }
- if (mViewStateManager != null) {
- mViewStateManager.onPageSwitched(newPage, newPageIndex);
- }
- }
-
- @Override
- public void sendAccessibilityEvent(int eventType) {
- if (eventType != AccessibilityEvent.TYPE_VIEW_SCROLLED || isPageMoving()) {
- super.sendAccessibilityEvent(eventType);
- }
- }
-
- private void updateWidgetFramesImportantForAccessibility() {
- final int pageCount = getPageCount();
- for (int i = 0; i < pageCount; i++) {
- KeyguardWidgetFrame frame = getWidgetPageAt(i);
- updateWidgetFrameImportantForAccessibility(frame);
- }
- }
-
- private void updateWidgetFrameImportantForAccessibility(KeyguardWidgetFrame frame) {
- if (frame.getContentAlpha() <= 0) {
- frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- } else {
- frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
- }
-
- private void userActivity() {
- if (mCallbacks != null) {
- mCallbacks.onUserActivityTimeoutChanged();
- mCallbacks.userActivity();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return captureUserInteraction(ev) || super.onTouchEvent(ev);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return captureUserInteraction(ev) || super.onInterceptTouchEvent(ev);
- }
-
- private boolean captureUserInteraction(MotionEvent ev) {
- KeyguardWidgetFrame currentWidgetPage = getWidgetPageAt(getCurrentPage());
- return currentWidgetPage != null && currentWidgetPage.onUserInteraction(ev);
- }
-
- public void showPagingFeedback() {
- // Nothing yet.
- }
-
- public long getUserActivityTimeout() {
- View page = getPageAt(mPage);
- if (page instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) page;
- View view = vg.getChildAt(0);
- if (!(view instanceof KeyguardStatusView)
- && !(view instanceof KeyguardMultiUserSelectorView)) {
- return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT;
- }
- }
- return -1;
- }
-
- public void setCallbacks(Callbacks callbacks) {
- mCallbacks = callbacks;
- }
-
- public interface Callbacks {
- public void userActivity();
- public void onUserActivityTimeoutChanged();
- public void onAddView(View v);
- public void onRemoveView(View v, boolean deletePermanently);
- public void onRemoveViewAnimationCompleted();
- }
-
- public void addWidget(View widget) {
- addWidget(widget, -1);
- }
-
- public void onRemoveView(View v, final boolean deletePermanently) {
- final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
- if (mCallbacks != null) {
- mCallbacks.onRemoveView(v, deletePermanently);
- }
- mBackgroundWorkerHandler.post(new Runnable() {
- @Override
- public void run() {
- mLockPatternUtils.removeAppWidget(appWidgetId);
- }
- });
- }
-
- @Override
- public void onRemoveViewAnimationCompleted() {
- if (mCallbacks != null) {
- mCallbacks.onRemoveViewAnimationCompleted();
- }
- }
-
- public void onAddView(View v, final int index) {
- final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
- final int[] pagesRange = new int[mTempVisiblePagesRange.length];
- getVisiblePages(pagesRange);
- boundByReorderablePages(true, pagesRange);
- if (mCallbacks != null) {
- mCallbacks.onAddView(v);
- }
- // Subtract from the index to take into account pages before the reorderable
- // pages (e.g. the "add widget" page)
- mBackgroundWorkerHandler.post(new Runnable() {
- @Override
- public void run() {
- mLockPatternUtils.addAppWidget(appWidgetId, index - pagesRange[0]);
- }
- });
- }
-
- /*
- * We wrap widgets in a special frame which handles drawing the over scroll foreground.
- */
- public void addWidget(View widget, int pageIndex) {
- KeyguardWidgetFrame frame;
- // All views contained herein should be wrapped in a KeyguardWidgetFrame
- if (!(widget instanceof KeyguardWidgetFrame)) {
- frame = new KeyguardWidgetFrame(getContext());
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
- lp.gravity = Gravity.TOP;
-
- // The framework adds a default padding to AppWidgetHostView. We don't need this padding
- // for the Keyguard, so we override it to be 0.
- widget.setPadding(0, 0, 0, 0);
- frame.addView(widget, lp);
-
- // We set whether or not this widget supports vertical resizing.
- if (widget instanceof AppWidgetHostView) {
- AppWidgetHostView awhv = (AppWidgetHostView) widget;
- AppWidgetProviderInfo info = awhv.getAppWidgetInfo();
- if ((info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
- frame.setWidgetLockedSmall(false);
- } else {
- // Lock the widget to be small.
- frame.setWidgetLockedSmall(true);
- if (mCenterSmallWidgetsVertically) {
- lp.gravity = Gravity.CENTER;
- }
- }
- }
- } else {
- frame = (KeyguardWidgetFrame) widget;
- }
-
- ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- frame.setOnLongClickListener(this);
- frame.setWorkerHandler(mBackgroundWorkerHandler);
-
- if (pageIndex == -1) {
- addView(frame, pageLp);
- } else {
- addView(frame, pageIndex, pageLp);
- }
-
- // Update the frame content description.
- View content = (widget == frame) ? frame.getContent() : widget;
- if (content != null) {
- String contentDescription = mContext.getString(
- com.android.internal.R.string.keyguard_accessibility_widget,
- content.getContentDescription());
- frame.setContentDescription(contentDescription);
- }
- updateWidgetFrameImportantForAccessibility(frame);
- }
-
- /**
- * Use addWidget() instead.
- * @deprecated
- */
- @Override
- public void addView(View child, int index) {
- enforceKeyguardWidgetFrame(child);
- super.addView(child, index);
- }
-
- /**
- * Use addWidget() instead.
- * @deprecated
- */
- @Override
- public void addView(View child, int width, int height) {
- enforceKeyguardWidgetFrame(child);
- super.addView(child, width, height);
- }
-
- /**
- * Use addWidget() instead.
- * @deprecated
- */
- @Override
- public void addView(View child, LayoutParams params) {
- enforceKeyguardWidgetFrame(child);
- super.addView(child, params);
- }
-
- /**
- * Use addWidget() instead.
- * @deprecated
- */
- @Override
- public void addView(View child, int index, LayoutParams params) {
- enforceKeyguardWidgetFrame(child);
- super.addView(child, index, params);
- }
-
- private void enforceKeyguardWidgetFrame(View child) {
- if (!(child instanceof KeyguardWidgetFrame)) {
- throw new IllegalArgumentException(
- "KeyguardWidgetPager children must be KeyguardWidgetFrames");
- }
- }
-
- public KeyguardWidgetFrame getWidgetPageAt(int index) {
- // This is always a valid cast as we've guarded the ability to
- return (KeyguardWidgetFrame) getChildAt(index);
- }
-
- protected void onUnhandledTap(MotionEvent ev) {
- showPagingFeedback();
- }
-
- @Override
- protected void onPageBeginMoving() {
- if (mViewStateManager != null) {
- mViewStateManager.onPageBeginMoving();
- }
- if (!isReordering(false)) {
- showOutlinesAndSidePages();
- }
- userActivity();
- }
-
- @Override
- protected void onPageEndMoving() {
- if (mViewStateManager != null) {
- mViewStateManager.onPageEndMoving();
- }
-
- // In the reordering case, the pages will be faded appropriately on completion
- // of the zoom in animation.
- if (!isReordering(false)) {
- hideOutlinesAndSidePages();
- }
- }
-
- protected void enablePageContentLayers() {
- int children = getChildCount();
- for (int i = 0; i < children; i++) {
- getWidgetPageAt(i).enableHardwareLayersForContent();
- }
- }
-
- protected void disablePageContentLayers() {
- int children = getChildCount();
- for (int i = 0; i < children; i++) {
- getWidgetPageAt(i).disableHardwareLayersForContent();
- }
- }
-
- /*
- * This interpolator emulates the rate at which the perceived scale of an object changes
- * as its distance from a camera increases. When this interpolator is applied to a scale
- * animation on a view, it evokes the sense that the object is shrinking due to moving away
- * from the camera.
- */
- static class ZInterpolator implements TimeInterpolator {
- private float focalLength;
-
- public ZInterpolator(float foc) {
- focalLength = foc;
- }
-
- public float getInterpolation(float input) {
- return (1.0f - focalLength / (focalLength + input)) /
- (1.0f - focalLength / (focalLength + 1.0f));
- }
- }
-
- @Override
- protected void overScroll(float amount) {
- acceleratedOverScroll(amount);
- }
-
- float backgroundAlphaInterpolator(float r) {
- return Math.min(1f, r);
- }
-
- private void updatePageAlphaValues(int screenCenter) {
- }
-
- public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
- if (showSidePages) {
- return 1f;
- } else {
- return index == mCurrentPage ? 1.0f : 0f;
- }
- }
-
- public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) {
- if (showSidePages) {
- return getAlphaForPage(screenCenter, index, showSidePages)
- * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER;
- } else {
- return 0f;
- }
- }
-
- protected boolean isOverScrollChild(int index, float scrollProgress) {
- boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
- return (isInOverscroll && (index == 0 && scrollProgress < 0 ||
- index == getChildCount() - 1 && scrollProgress > 0));
- }
-
- @Override
- protected void screenScrolled(int screenCenter) {
- mScreenCenter = screenCenter;
- updatePageAlphaValues(screenCenter);
- for (int i = 0; i < getChildCount(); i++) {
- KeyguardWidgetFrame v = getWidgetPageAt(i);
- if (v == mDragView) continue;
- if (v != null) {
- float scrollProgress = getScrollProgress(screenCenter, v, i);
-
- v.setCameraDistance(mDensity * CAMERA_DISTANCE);
-
- if (isOverScrollChild(i, scrollProgress) && PERFORM_OVERSCROLL_ROTATION) {
- float pivotX = v.getMeasuredWidth() / 2;
- float pivotY = v.getMeasuredHeight() / 2;
- v.setPivotX(pivotX);
- v.setPivotY(pivotY);
- v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress);
- v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0);
- } else {
- v.setRotationY(0f);
- v.setOverScrollAmount(0, false);
- }
-
- float alpha = v.getAlpha();
- // If the view has 0 alpha, we set it to be invisible so as to prevent
- // it from accepting touches
- if (alpha == 0) {
- v.setVisibility(INVISIBLE);
- } else if (v.getVisibility() != VISIBLE) {
- v.setVisibility(VISIBLE);
- }
- }
- }
- }
-
- public boolean isWidgetPage(int pageIndex) {
- if (pageIndex < 0 || pageIndex >= getChildCount()) {
- return false;
- }
- View v = getChildAt(pageIndex);
- if (v != null && v instanceof KeyguardWidgetFrame) {
- KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v;
- return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID;
- }
- return false;
- }
-
- /**
- * Returns the bounded set of pages that are re-orderable. The range is fully inclusive.
- */
- @Override
- void boundByReorderablePages(boolean isReordering, int[] range) {
- if (isReordering) {
- // Remove non-widget pages from the range
- while (range[1] >= range[0] && !isWidgetPage(range[1])) {
- range[1]--;
- }
- while (range[0] <= range[1] && !isWidgetPage(range[0])) {
- range[0]++;
- }
- }
- }
-
- protected void reorderStarting() {
- showOutlinesAndSidePages();
- }
-
- @Override
- protected void onStartReordering() {
- super.onStartReordering();
- enablePageContentLayers();
- reorderStarting();
- }
-
- @Override
- protected void onEndReordering() {
- super.onEndReordering();
- hideOutlinesAndSidePages();
- }
-
- void showOutlinesAndSidePages() {
- animateOutlinesAndSidePages(true);
- }
-
- void hideOutlinesAndSidePages() {
- animateOutlinesAndSidePages(false);
- }
-
- void updateChildrenContentAlpha(float sidePageAlpha) {
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- KeyguardWidgetFrame child = getWidgetPageAt(i);
- if (i != mCurrentPage) {
- child.setBackgroundAlpha(sidePageAlpha);
- child.setContentAlpha(0f);
- } else {
- child.setBackgroundAlpha(0f);
- child.setContentAlpha(1f);
- }
- }
- }
-
- public void showInitialPageHints() {
- mShowingInitialHints = true;
- updateChildrenContentAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
- }
-
- @Override
- void setCurrentPage(int currentPage) {
- super.setCurrentPage(currentPage);
- updateChildrenContentAlpha(0.0f);
- updateWidgetFramesImportantForAccessibility();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- mHasMeasure = false;
- }
-
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mLastWidthMeasureSpec = widthMeasureSpec;
- mLastHeightMeasureSpec = heightMeasureSpec;
-
- int maxChallengeTop = -1;
- View parent = (View) getParent();
- boolean challengeShowing = false;
- // Widget pages need to know where the top of the sliding challenge is so that they
- // now how big the widget should be when the challenge is up. We compute it here and
- // then propagate it to each of our children.
- if (parent.getParent() instanceof SlidingChallengeLayout) {
- SlidingChallengeLayout scl = (SlidingChallengeLayout) parent.getParent();
- int top = scl.getMaxChallengeTop();
-
- // This is a bit evil, but we need to map a coordinate relative to the SCL into a
- // coordinate relative to our children, hence we subtract the top padding.s
- maxChallengeTop = top - getPaddingTop();
- challengeShowing = scl.isChallengeShowing();
-
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- KeyguardWidgetFrame frame = getWidgetPageAt(i);
- frame.setMaxChallengeTop(maxChallengeTop);
- // On the very first measure pass, if the challenge is showing, we need to make sure
- // that the widget on the current page is small.
- if (challengeShowing && i == mCurrentPage && !mHasMeasure) {
- frame.shrinkWidget();
- }
- }
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mHasMeasure = true;
- }
-
- void animateOutlinesAndSidePages(final boolean show) {
- animateOutlinesAndSidePages(show, -1);
- }
-
- public void setWidgetToResetOnPageFadeOut(int widget) {
- mWidgetToResetAfterFadeOut = widget;
- }
-
- public int getWidgetToResetOnPageFadeOut() {
- return mWidgetToResetAfterFadeOut;
- }
-
- void animateOutlinesAndSidePages(final boolean show, int duration) {
- if (mChildrenOutlineFadeAnimation != null) {
- mChildrenOutlineFadeAnimation.cancel();
- mChildrenOutlineFadeAnimation = null;
- }
- int count = getChildCount();
- PropertyValuesHolder alpha;
- ArrayList<Animator> anims = new ArrayList<Animator>();
-
- if (duration == -1) {
- duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION :
- CHILDREN_OUTLINE_FADE_OUT_DURATION;
- }
-
- int curPage = getNextPage();
- for (int i = 0; i < count; i++) {
- float finalContentAlpha;
- if (show) {
- finalContentAlpha = getAlphaForPage(mScreenCenter, i, true);
- } else if (!show && i == curPage) {
- finalContentAlpha = 1f;
- } else {
- finalContentAlpha = 0f;
- }
- KeyguardWidgetFrame child = getWidgetPageAt(i);
-
- alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalContentAlpha);
- ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha);
- anims.add(a);
-
- float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i, true) : 0f;
- child.fadeFrame(this, show, finalOutlineAlpha, duration);
- }
-
- mChildrenOutlineFadeAnimation = new AnimatorSet();
- mChildrenOutlineFadeAnimation.playTogether(anims);
-
- mChildrenOutlineFadeAnimation.setDuration(duration);
- mChildrenOutlineFadeAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- if (show) {
- enablePageContentLayers();
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!show) {
- disablePageContentLayers();
- KeyguardWidgetFrame frame = getWidgetPageAt(mWidgetToResetAfterFadeOut);
- if (frame != null && !(frame == getWidgetPageAt(mCurrentPage) &&
- mViewStateManager.isChallengeOverlapping())) {
- frame.resetSize();
- }
- mWidgetToResetAfterFadeOut = -1;
- mShowingInitialHints = false;
- }
- updateWidgetFramesImportantForAccessibility();
- }
- });
- mChildrenOutlineFadeAnimation.start();
- }
-
- @Override
- public boolean onLongClick(View v) {
- // Disallow long pressing to reorder if the challenge is showing
- boolean isChallengeOverlapping = mViewStateManager.isChallengeShowing() &&
- mViewStateManager.isChallengeOverlapping();
- if (!isChallengeOverlapping && startReordering()) {
- return true;
- }
- return false;
- }
-
- public void removeWidget(View view) {
- if (view instanceof KeyguardWidgetFrame) {
- removeView(view);
- } else {
- // Assume view was wrapped by a KeyguardWidgetFrame in KeyguardWidgetPager#addWidget().
- // This supports legacy hard-coded "widgets" like KeyguardTransportControlView.
- int pos = getWidgetPageIndex(view);
- if (pos != -1) {
- KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(pos);
- frame.removeView(view);
- removeView(frame);
- } else {
- Slog.w(TAG, "removeWidget() can't find:" + view);
- }
- }
- }
-
- public int getWidgetPageIndex(View view) {
- if (view instanceof KeyguardWidgetFrame) {
- return indexOfChild(view);
- } else {
- // View was wrapped by a KeyguardWidgetFrame by KeyguardWidgetPager#addWidget()
- return indexOfChild((KeyguardWidgetFrame)view.getParent());
- }
- }
-
- @Override
- protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {
- KeyguardWidgetFrame child = getWidgetPageAt(viewIndex);
- child.setIsHoveringOverDeleteDropTarget(isHovering);
- }
-
- // ChallengeLayout.OnBouncerStateChangedListener
- @Override
- public void onBouncerStateChanged(boolean bouncerActive) {
- if (bouncerActive) {
- zoomOutToBouncer();
- } else {
- zoomInFromBouncer();
- }
- }
-
- void setBouncerAnimationDuration(int duration) {
- mBouncerZoomInOutDuration = duration;
- }
-
- // Zoom in after the bouncer is dismissed
- void zoomInFromBouncer() {
- if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
- mZoomInOutAnim.cancel();
- }
- final View currentPage = getPageAt(getCurrentPage());
- if (currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f) {
- mZoomInOutAnim = new AnimatorSet();
- mZoomInOutAnim.playTogether(
- ObjectAnimator.ofFloat(currentPage, "scaleX", 1f),
- ObjectAnimator.ofFloat(currentPage , "scaleY", 1f));
- mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
- mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
- mZoomInOutAnim.start();
- }
- if (currentPage instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame)currentPage).onBouncerShowing(false);
- }
- }
-
- // Zoom out after the bouncer is initiated
- void zoomOutToBouncer() {
- if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
- mZoomInOutAnim.cancel();
- }
- int curPage = getCurrentPage();
- View currentPage = getPageAt(curPage);
- if (shouldSetTopAlignedPivotForWidget(curPage)) {
- currentPage.setPivotY(0);
- // Note: we are working around the issue that setting the x-pivot to the same value as it
- // was does not actually work.
- currentPage.setPivotX(0);
- currentPage.setPivotX(currentPage.getMeasuredWidth() / 2);
- }
- if (!(currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f)) {
- mZoomInOutAnim = new AnimatorSet();
- mZoomInOutAnim.playTogether(
- ObjectAnimator.ofFloat(currentPage, "scaleX", BOUNCER_SCALE_FACTOR),
- ObjectAnimator.ofFloat(currentPage, "scaleY", BOUNCER_SCALE_FACTOR));
- mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
- mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
- mZoomInOutAnim.start();
- }
- if (currentPage instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame)currentPage).onBouncerShowing(true);
- }
- }
-
- void setAddWidgetEnabled(boolean enabled) {
- if (mAddWidgetView != null && enabled) {
- addView(mAddWidgetView, 0);
- // We need to force measure the PagedView so that the calls to update the scroll
- // position below work
- measure(mLastWidthMeasureSpec, mLastHeightMeasureSpec);
- // Bump up the current page to account for the addition of the new page
- setCurrentPage(mCurrentPage + 1);
- mAddWidgetView = null;
- } else if (mAddWidgetView == null && !enabled) {
- View addWidget = findViewById(com.android.internal.R.id.keyguard_add_widget);
- if (addWidget != null) {
- mAddWidgetView = addWidget;
- removeView(addWidget);
- }
- }
- }
-
- boolean isAddPage(int pageIndex) {
- View v = getChildAt(pageIndex);
- return v != null && v.getId() == com.android.internal.R.id.keyguard_add_widget;
- }
-
- boolean isCameraPage(int pageIndex) {
- View v = getChildAt(pageIndex);
- return v != null && v instanceof CameraWidgetFrame;
- }
-
- @Override
- protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
- return !isCameraPage(childIndex) && super.shouldSetTopAlignedPivotForWidget(childIndex);
- }
-
- /**
- * Search given {@link View} hierarchy for {@link TextClock} instances that
- * show various time components. Returns combination of
- * {@link #FLAG_HAS_LOCAL_HOUR} and {@link #FLAG_HAS_LOCAL_MINUTE}.
- */
- private static int findClockInHierarchy(View view) {
- if (view instanceof TextClock) {
- return getClockFlags((TextClock) view);
- } else if (view instanceof ViewGroup) {
- int flags = 0;
- final ViewGroup group = (ViewGroup) view;
- final int size = group.getChildCount();
- for (int i = 0; i < size; i++) {
- flags |= findClockInHierarchy(group.getChildAt(i));
- }
- return flags;
- } else {
- return 0;
- }
- }
-
- /**
- * Return combination of {@link #FLAG_HAS_LOCAL_HOUR} and
- * {@link #FLAG_HAS_LOCAL_MINUTE} describing the time represented described
- * by the given {@link TextClock}.
- */
- private static int getClockFlags(TextClock clock) {
- int flags = 0;
-
- final String timeZone = clock.getTimeZone();
- if (timeZone != null && !TimeZone.getDefault().equals(TimeZone.getTimeZone(timeZone))) {
- // Ignore clocks showing another timezone
- return 0;
- }
-
- final CharSequence format = clock.getFormat();
- final char hour = clock.is24HourModeEnabled() ? DateFormat.HOUR_OF_DAY
- : DateFormat.HOUR;
-
- if (DateFormat.hasDesignator(format, hour)) {
- flags |= FLAG_HAS_LOCAL_HOUR;
- }
- if (DateFormat.hasDesignator(format, DateFormat.MINUTE)) {
- flags |= FLAG_HAS_LOCAL_MINUTE;
- }
-
- return flags;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java b/policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java
deleted file mode 100644
index 818108c..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-/**
- * Hover listener that implements lift-to-activate interaction for
- * accessibility. May be added to multiple views.
- */
-class LiftToActivateListener implements View.OnHoverListener {
- /** Manager used to query accessibility enabled state. */
- private final AccessibilityManager mAccessibilityManager;
-
- private boolean mCachedClickableState;
-
- public LiftToActivateListener(Context context) {
- mAccessibilityManager = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- }
-
- @Override
- public boolean onHover(View v, MotionEvent event) {
- // When touch exploration is turned on, lifting a finger while
- // inside the view bounds should perform a click action.
- if (mAccessibilityManager.isEnabled()
- && mAccessibilityManager.isTouchExplorationEnabled()) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_HOVER_ENTER:
- // Lift-to-type temporarily disables double-tap
- // activation by setting the view as not clickable.
- mCachedClickableState = v.isClickable();
- v.setClickable(false);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if ((x > v.getPaddingLeft()) && (y > v.getPaddingTop())
- && (x < v.getWidth() - v.getPaddingRight())
- && (y < v.getHeight() - v.getPaddingBottom())) {
- v.performClick();
- }
- v.setClickable(mCachedClickableState);
- break;
- }
- }
-
- // Pass the event to View.onHoverEvent() to handle accessibility.
- v.onHoverEvent(event);
-
- // Consume the event so it doesn't fall through to other views.
- return true;
- }
-}
\ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
deleted file mode 100644
index 0ca46c3..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import com.android.internal.R;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayout {
- private static final String TAG = "MultiPaneChallengeLayout";
-
- final int mOrientation;
- private boolean mIsBouncing;
-
- public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
- public static final int VERTICAL = LinearLayout.VERTICAL;
- public static final int ANIMATE_BOUNCE_DURATION = 350;
-
- private KeyguardSecurityContainer mChallengeView;
- private View mUserSwitcherView;
- private View mScrimView;
- private OnBouncerStateChangedListener mBouncerListener;
-
- private final Rect mTempRect = new Rect();
- private final Rect mZeroPadding = new Rect();
-
- private final DisplayMetrics mDisplayMetrics;
-
- private final OnClickListener mScrimClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- hideBouncer();
- }
- };
-
- public MultiPaneChallengeLayout(Context context) {
- this(context, null);
- }
-
- public MultiPaneChallengeLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public MultiPaneChallengeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.MultiPaneChallengeLayout, defStyleAttr, 0);
- mOrientation = a.getInt(R.styleable.MultiPaneChallengeLayout_orientation,
- HORIZONTAL);
- a.recycle();
-
- final Resources res = getResources();
- mDisplayMetrics = res.getDisplayMetrics();
-
- setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE);
- }
-
- @Override
- public boolean isChallengeShowing() {
- return true;
- }
-
- @Override
- public boolean isChallengeOverlapping() {
- return false;
- }
-
- @Override
- public void showChallenge(boolean b) {
- }
-
- @Override
- public int getBouncerAnimationDuration() {
- return ANIMATE_BOUNCE_DURATION;
- }
-
- @Override
- public void showBouncer() {
- if (mIsBouncing) return;
- mIsBouncing = true;
- if (mScrimView != null) {
- if (mChallengeView != null) {
- mChallengeView.showBouncer(ANIMATE_BOUNCE_DURATION);
- }
-
- Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f);
- anim.setDuration(ANIMATE_BOUNCE_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mScrimView.setVisibility(VISIBLE);
- }
- });
- anim.start();
- }
- if (mBouncerListener != null) {
- mBouncerListener.onBouncerStateChanged(true);
- }
- }
-
- @Override
- public void hideBouncer() {
- if (!mIsBouncing) return;
- mIsBouncing = false;
- if (mScrimView != null) {
- if (mChallengeView != null) {
- mChallengeView.hideBouncer(ANIMATE_BOUNCE_DURATION);
- }
-
- Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f);
- anim.setDuration(ANIMATE_BOUNCE_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mScrimView.setVisibility(INVISIBLE);
- }
- });
- anim.start();
- }
- if (mBouncerListener != null) {
- mBouncerListener.onBouncerStateChanged(false);
- }
- }
-
- @Override
- public boolean isBouncing() {
- return mIsBouncing;
- }
-
- @Override
- public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
- mBouncerListener = listener;
- }
-
- @Override
- public void requestChildFocus(View child, View focused) {
- if (mIsBouncing && child != mChallengeView) {
- // Clear out of the bouncer if the user tries to move focus outside of
- // the security challenge view.
- hideBouncer();
- }
- super.requestChildFocus(child, focused);
- }
-
- void setScrimView(View scrim) {
- if (mScrimView != null) {
- mScrimView.setOnClickListener(null);
- }
- mScrimView = scrim;
- mScrimView.setAlpha(mIsBouncing ? 1.0f : 0.0f);
- mScrimView.setVisibility(mIsBouncing ? VISIBLE : INVISIBLE);
- mScrimView.setFocusable(true);
- mScrimView.setOnClickListener(mScrimClickListener);
- }
-
- private int getVirtualHeight(LayoutParams lp, int height, int heightUsed) {
- int virtualHeight = height;
- final View root = getRootView();
- if (root != null) {
- // This calculation is super dodgy and relies on several assumptions.
- // Specifically that the root of the window will be padded in for insets
- // and that the window is LAYOUT_IN_SCREEN.
- virtualHeight = mDisplayMetrics.heightPixels - root.getPaddingTop();
- }
- if (lp.childType == LayoutParams.CHILD_TYPE_WIDGET ||
- lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
- // Always measure the widget pager/user switcher as if there were no IME insets
- // on the window. We want to avoid resizing widgets when possible as it can
- // be ugly/expensive. This lets us simply clip them instead.
- return virtualHeight - heightUsed;
- } else if (lp.childType == LayoutParams.CHILD_TYPE_PAGE_DELETE_DROP_TARGET) {
- return height;
- }
- return Math.min(virtualHeight - heightUsed, height);
- }
-
- @Override
- protected void onMeasure(final int widthSpec, final int heightSpec) {
- if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
- MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
- throw new IllegalArgumentException(
- "MultiPaneChallengeLayout must be measured with an exact size");
- }
-
- final int width = MeasureSpec.getSize(widthSpec);
- final int height = MeasureSpec.getSize(heightSpec);
- setMeasuredDimension(width, height);
-
- int widthUsed = 0;
- int heightUsed = 0;
-
- // First pass. Find the challenge view and measure the user switcher,
- // which consumes space in the layout.
- mChallengeView = null;
- mUserSwitcherView = null;
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
- if (mChallengeView != null) {
- throw new IllegalStateException(
- "There may only be one child of type challenge");
- }
- if (!(child instanceof KeyguardSecurityContainer)) {
- throw new IllegalArgumentException(
- "Challenge must be a KeyguardSecurityContainer");
- }
- mChallengeView = (KeyguardSecurityContainer) child;
- } else if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
- if (mUserSwitcherView != null) {
- throw new IllegalStateException(
- "There may only be one child of type userSwitcher");
- }
- mUserSwitcherView = child;
-
- if (child.getVisibility() == GONE) continue;
-
- int adjustedWidthSpec = widthSpec;
- int adjustedHeightSpec = heightSpec;
- if (lp.maxWidth >= 0) {
- adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
- Math.min(lp.maxWidth, width), MeasureSpec.EXACTLY);
- }
- if (lp.maxHeight >= 0) {
- adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
- Math.min(lp.maxHeight, height), MeasureSpec.EXACTLY);
- }
- // measureChildWithMargins will resolve layout direction for the LayoutParams
- measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
-
- // Only subtract out space from one dimension. Favor vertical.
- // Offset by 1.5x to add some balance along the other edge.
- if (Gravity.isVertical(lp.gravity)) {
- heightUsed += child.getMeasuredHeight() * 1.5f;
- } else if (Gravity.isHorizontal(lp.gravity)) {
- widthUsed += child.getMeasuredWidth() * 1.5f;
- }
- } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
- setScrimView(child);
- child.measure(widthSpec, heightSpec);
- }
- }
-
- // Second pass. Measure everything that's left.
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER ||
- lp.childType == LayoutParams.CHILD_TYPE_SCRIM ||
- child.getVisibility() == GONE) {
- // Don't need to measure GONE children, and the user switcher was already measured.
- continue;
- }
-
- final int virtualHeight = getVirtualHeight(lp, height, heightUsed);
-
- int adjustedWidthSpec;
- int adjustedHeightSpec;
- if (lp.centerWithinArea > 0) {
- if (mOrientation == HORIZONTAL) {
- adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
- (int) ((width - widthUsed) * lp.centerWithinArea + 0.5f),
- MeasureSpec.EXACTLY);
- adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
- virtualHeight, MeasureSpec.EXACTLY);
- } else {
- adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
- width - widthUsed, MeasureSpec.EXACTLY);
- adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
- (int) (virtualHeight * lp.centerWithinArea + 0.5f),
- MeasureSpec.EXACTLY);
- }
- } else {
- adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
- width - widthUsed, MeasureSpec.EXACTLY);
- adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
- virtualHeight, MeasureSpec.EXACTLY);
- }
- if (lp.maxWidth >= 0) {
- adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
- Math.min(lp.maxWidth, MeasureSpec.getSize(adjustedWidthSpec)),
- MeasureSpec.EXACTLY);
- }
- if (lp.maxHeight >= 0) {
- adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
- Math.min(lp.maxHeight, MeasureSpec.getSize(adjustedHeightSpec)),
- MeasureSpec.EXACTLY);
- }
-
- measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- final Rect padding = mTempRect;
- padding.left = getPaddingLeft();
- padding.top = getPaddingTop();
- padding.right = getPaddingRight();
- padding.bottom = getPaddingBottom();
- final int width = r - l;
- final int height = b - t;
-
- // Reserve extra space in layout for the user switcher by modifying
- // local padding during this layout pass
- if (mUserSwitcherView != null && mUserSwitcherView.getVisibility() != GONE) {
- layoutWithGravity(width, height, mUserSwitcherView, padding, true);
- }
-
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- // We did the user switcher above if we have one.
- if (child == mUserSwitcherView || child.getVisibility() == GONE) continue;
-
- if (child == mScrimView) {
- child.layout(0, 0, width, height);
- continue;
- } else if (lp.childType == LayoutParams.CHILD_TYPE_PAGE_DELETE_DROP_TARGET) {
- layoutWithGravity(width, height, child, mZeroPadding, false);
- continue;
- }
-
- layoutWithGravity(width, height, child, padding, false);
- }
- }
-
- private void layoutWithGravity(int width, int height, View child, Rect padding,
- boolean adjustPadding) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- final int heightUsed = padding.top + padding.bottom - getPaddingTop() - getPaddingBottom();
- height = getVirtualHeight(lp, height, heightUsed);
-
- final int gravity = Gravity.getAbsoluteGravity(lp.gravity, getLayoutDirection());
-
- final boolean fixedLayoutSize = lp.centerWithinArea > 0;
- final boolean fixedLayoutHorizontal = fixedLayoutSize && mOrientation == HORIZONTAL;
- final boolean fixedLayoutVertical = fixedLayoutSize && mOrientation == VERTICAL;
-
- final int adjustedWidth;
- final int adjustedHeight;
- if (fixedLayoutHorizontal) {
- final int paddedWidth = width - padding.left - padding.right;
- adjustedWidth = (int) (paddedWidth * lp.centerWithinArea + 0.5f);
- adjustedHeight = height;
- } else if (fixedLayoutVertical) {
- final int paddedHeight = height - getPaddingTop() - getPaddingBottom();
- adjustedWidth = width;
- adjustedHeight = (int) (paddedHeight * lp.centerWithinArea + 0.5f);
- } else {
- adjustedWidth = width;
- adjustedHeight = height;
- }
-
- final boolean isVertical = Gravity.isVertical(gravity);
- final boolean isHorizontal = Gravity.isHorizontal(gravity);
- final int childWidth = child.getMeasuredWidth();
- final int childHeight = child.getMeasuredHeight();
-
- int left = padding.left;
- int top = padding.top;
- int right = left + childWidth;
- int bottom = top + childHeight;
- switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.TOP:
- top = fixedLayoutVertical ?
- padding.top + (adjustedHeight - childHeight) / 2 : padding.top;
- bottom = top + childHeight;
- if (adjustPadding && isVertical) {
- padding.top = bottom;
- padding.bottom += childHeight / 2;
- }
- break;
- case Gravity.BOTTOM:
- bottom = fixedLayoutVertical
- ? padding.top + height - (adjustedHeight - childHeight) / 2
- : padding.top + height;
- top = bottom - childHeight;
- if (adjustPadding && isVertical) {
- padding.bottom = height - top;
- padding.top += childHeight / 2;
- }
- break;
- case Gravity.CENTER_VERTICAL:
- top = padding.top + (height - childHeight) / 2;
- bottom = top + childHeight;
- break;
- }
- switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- left = fixedLayoutHorizontal ?
- padding.left + (adjustedWidth - childWidth) / 2 : padding.left;
- right = left + childWidth;
- if (adjustPadding && isHorizontal && !isVertical) {
- padding.left = right;
- padding.right += childWidth / 2;
- }
- break;
- case Gravity.RIGHT:
- right = fixedLayoutHorizontal
- ? width - padding.right - (adjustedWidth - childWidth) / 2
- : width - padding.right;
- left = right - childWidth;
- if (adjustPadding && isHorizontal && !isVertical) {
- padding.right = width - left;
- padding.left += childWidth / 2;
- }
- break;
- case Gravity.CENTER_HORIZONTAL:
- final int paddedWidth = width - padding.left - padding.right;
- left = (paddedWidth - childWidth) / 2;
- right = left + childWidth;
- break;
- }
- child.layout(left, top, right, bottom);
- }
-
- @Override
- public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs, this);
- }
-
- @Override
- protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
- p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
- new LayoutParams(p);
- }
-
- @Override
- protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams();
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams;
- }
-
- public static class LayoutParams extends MarginLayoutParams {
-
- public float centerWithinArea = 0;
-
- public int childType = 0;
-
- public static final int CHILD_TYPE_NONE = 0;
- public static final int CHILD_TYPE_WIDGET = 1;
- public static final int CHILD_TYPE_CHALLENGE = 2;
- public static final int CHILD_TYPE_USER_SWITCHER = 3;
- public static final int CHILD_TYPE_SCRIM = 4;
- public static final int CHILD_TYPE_PAGE_DELETE_DROP_TARGET = 7;
-
- public int gravity = Gravity.NO_GRAVITY;
-
- public int maxWidth = -1;
- public int maxHeight = -1;
-
- public LayoutParams() {
- this(WRAP_CONTENT, WRAP_CONTENT);
- }
-
- LayoutParams(Context c, AttributeSet attrs, MultiPaneChallengeLayout parent) {
- super(c, attrs);
-
- final TypedArray a = c.obtainStyledAttributes(attrs,
- R.styleable.MultiPaneChallengeLayout_Layout);
-
- centerWithinArea = a.getFloat(
- R.styleable.MultiPaneChallengeLayout_Layout_layout_centerWithinArea, 0);
- childType = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_childType,
- CHILD_TYPE_NONE);
- gravity = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_gravity,
- Gravity.NO_GRAVITY);
- maxWidth = a.getDimensionPixelSize(
- R.styleable.MultiPaneChallengeLayout_Layout_layout_maxWidth, -1);
- maxHeight = a.getDimensionPixelSize(
- R.styleable.MultiPaneChallengeLayout_Layout_layout_maxHeight, -1);
-
- // Default gravity settings based on type and parent orientation
- if (gravity == Gravity.NO_GRAVITY) {
- if (parent.mOrientation == HORIZONTAL) {
- switch (childType) {
- case CHILD_TYPE_WIDGET:
- gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
- break;
- case CHILD_TYPE_CHALLENGE:
- gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
- break;
- case CHILD_TYPE_USER_SWITCHER:
- gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- break;
- }
- } else {
- switch (childType) {
- case CHILD_TYPE_WIDGET:
- gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- break;
- case CHILD_TYPE_CHALLENGE:
- gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- break;
- case CHILD_TYPE_USER_SWITCHER:
- gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- break;
- }
- }
- }
-
- a.recycle();
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(ViewGroup.LayoutParams source) {
- super(source);
- }
-
- public LayoutParams(MarginLayoutParams source) {
- super(source);
- }
-
- public LayoutParams(LayoutParams source) {
- this((MarginLayoutParams) source);
-
- centerWithinArea = source.centerWithinArea;
- childType = source.childType;
- gravity = source.gravity;
- maxWidth = source.maxWidth;
- maxHeight = source.maxHeight;
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiUserAvatarCache.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiUserAvatarCache.java
deleted file mode 100644
index 7969c7d..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/MultiUserAvatarCache.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.graphics.drawable.Drawable;
-
-import java.util.HashMap;
-
-public class MultiUserAvatarCache {
-
- private final HashMap<Integer, Drawable> mCache;
-
- public MultiUserAvatarCache() {
- mCache = new HashMap<Integer, Drawable>();
- }
-
- public void clear(int userId) {
- mCache.remove(userId);
- }
-
- public Drawable get(int userId) {
- return mCache.get(userId);
- }
-
- public void put(int userId, Drawable image) {
- mCache.put(userId, image);
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
deleted file mode 100644
index a0038bc..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.text.SpannableStringBuilder;
-import android.text.style.TextAppearanceSpan;
-import android.util.AttributeSet;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
-
-public class NumPadKey extends Button {
- // list of "ABC", etc per digit, starting with '0'
- static String sKlondike[];
-
- int mDigit = -1;
- int mTextViewResId;
- TextView mTextView = null;
- boolean mEnableHaptics;
-
- private View.OnClickListener mListener = new View.OnClickListener() {
- @Override
- public void onClick(View thisView) {
- if (mTextView == null) {
- if (mTextViewResId > 0) {
- final View v = NumPadKey.this.getRootView().findViewById(mTextViewResId);
- if (v != null && v instanceof TextView) {
- mTextView = (TextView) v;
- }
- }
- }
- // check for time-based lockouts
- if (mTextView != null && mTextView.isEnabled()) {
- mTextView.append(String.valueOf(mDigit));
- }
- doHapticKeyClick();
- }
- };
-
- public NumPadKey(Context context) {
- this(context, null);
- }
-
- public NumPadKey(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public NumPadKey(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumPadKey);
- mDigit = a.getInt(R.styleable.NumPadKey_digit, mDigit);
- setTextViewResId(a.getResourceId(R.styleable.NumPadKey_textView, 0));
-
- setOnClickListener(mListener);
- setOnHoverListener(new LiftToActivateListener(context));
- setAccessibilityDelegate(new ObscureSpeechDelegate(context));
-
- mEnableHaptics = new LockPatternUtils(context).isTactileFeedbackEnabled();
-
- SpannableStringBuilder builder = new SpannableStringBuilder();
- builder.append(String.valueOf(mDigit));
- if (mDigit >= 0) {
- if (sKlondike == null) {
- sKlondike = context.getResources().getStringArray(
- R.array.lockscreen_num_pad_klondike);
- }
- if (sKlondike != null && sKlondike.length > mDigit) {
- final String extra = sKlondike[mDigit];
- final int extraLen = extra.length();
- if (extraLen > 0) {
- builder.append(" ");
- builder.append(extra);
- builder.setSpan(
- new TextAppearanceSpan(context, R.style.TextAppearance_NumPadKey_Klondike),
- builder.length()-extraLen, builder.length(), 0);
- }
- }
- }
- setText(builder);
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- // Reset the "announced headset" flag when detached.
- ObscureSpeechDelegate.sAnnouncedHeadset = false;
- }
-
- public void setTextView(TextView tv) {
- mTextView = tv;
- }
-
- public void setTextViewResId(int resId) {
- mTextView = null;
- mTextViewResId = resId;
- }
-
- // Cause a VIRTUAL_KEY vibration
- public void doHapticKeyClick() {
- if (mEnableHaptics) {
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
- | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java
deleted file mode 100644
index af043ab..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.media.AudioManager;
-import android.provider.Settings;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import com.android.internal.R;
-
-/**
- * Accessibility delegate that obscures speech for a view when the user has
- * not turned on the "speak passwords" preference and is not listening
- * through headphones.
- */
-class ObscureSpeechDelegate extends AccessibilityDelegate {
- /** Whether any client has announced the "headset" notification. */
- static boolean sAnnouncedHeadset = false;
-
- private final ContentResolver mContentResolver;
- private final AudioManager mAudioManager;
-
- public ObscureSpeechDelegate(Context context) {
- mContentResolver = context.getContentResolver();
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- }
-
- @Override
- public void sendAccessibilityEvent(View host, int eventType) {
- super.sendAccessibilityEvent(host, eventType);
-
- // Play the "headset required" announcement the first time the user
- // places accessibility focus on a key.
- if ((eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED)
- && !sAnnouncedHeadset && shouldObscureSpeech()) {
- sAnnouncedHeadset = true;
- host.announceForAccessibility(host.getContext().getString(
- R.string.keyboard_headset_required_to_hear_password));
- }
- }
-
- @Override
- public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
- super.onPopulateAccessibilityEvent(host, event);
-
- if ((event.getEventType() != AccessibilityEvent.TYPE_ANNOUNCEMENT)
- && shouldObscureSpeech()) {
- event.getText().clear();
- event.setContentDescription(host.getContext().getString(
- R.string.keyboard_password_character_no_headset));
- }
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
-
- if (shouldObscureSpeech()) {
- final Context ctx = host.getContext();
- info.setText(null);
- info.setContentDescription(
- ctx.getString(R.string.keyboard_password_character_no_headset));
- }
- }
-
- @SuppressWarnings("deprecation")
- private boolean shouldObscureSpeech() {
- // The user can optionally force speaking passwords.
- if (Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0) {
- return false;
- }
-
- // Always speak if the user is listening through headphones.
- if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) {
- return false;
- }
-
- // Don't speak since this key is used to type a password.
- return true;
- }
-}
\ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
deleted file mode 100644
index 10562e8..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ /dev/null
@@ -1,2569 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.widget.Scroller;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-
-/**
- * An abstraction of the original Workspace which supports browsing through a
- * sequential list of "pages"
- */
-public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
- private static final String TAG = "WidgetPagedView";
- private static final boolean DEBUG = false;
- protected static final int INVALID_PAGE = -1;
-
- // the min drag distance for a fling to register, to prevent random page shifts
- private static final int MIN_LENGTH_FOR_FLING = 25;
-
- protected static final int PAGE_SNAP_ANIMATION_DURATION = 750;
- protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
- protected static final float NANOTIME_DIV = 1000000000.0f;
-
- private static final float OVERSCROLL_ACCELERATE_FACTOR = 2;
- private static final float OVERSCROLL_DAMP_FACTOR = 0.14f;
-
- private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
- // The page is moved more than halfway, automatically move to the next page on touch up.
- private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
-
- // The following constants need to be scaled based on density. The scaled versions will be
- // assigned to the corresponding member variables below.
- private static final int FLING_THRESHOLD_VELOCITY = 500;
- private static final int MIN_SNAP_VELOCITY = 1500;
- private static final int MIN_FLING_VELOCITY = 250;
-
- // We are disabling touch interaction of the widget region for factory ROM.
- private static final boolean DISABLE_TOUCH_INTERACTION = false;
- private static final boolean DISABLE_TOUCH_SIDE_PAGES = true;
- private static final boolean DISABLE_FLING_TO_DELETE = false;
-
- static final int AUTOMATIC_PAGE_SPACING = -1;
-
- protected int mFlingThresholdVelocity;
- protected int mMinFlingVelocity;
- protected int mMinSnapVelocity;
-
- protected float mDensity;
- protected float mSmoothingTime;
- protected float mTouchX;
-
- protected boolean mFirstLayout = true;
-
- protected int mCurrentPage;
- protected int mChildCountOnLastMeasure;
-
- protected int mNextPage = INVALID_PAGE;
- protected int mMaxScrollX;
- protected Scroller mScroller;
- private VelocityTracker mVelocityTracker;
-
- private float mParentDownMotionX;
- private float mParentDownMotionY;
- private float mDownMotionX;
- private float mDownMotionY;
- private float mDownScrollX;
- protected float mLastMotionX;
- protected float mLastMotionXRemainder;
- protected float mLastMotionY;
- protected float mTotalMotionX;
- private int mLastScreenCenter = -1;
- private int[] mChildOffsets;
- private int[] mChildRelativeOffsets;
- private int[] mChildOffsetsWithLayoutScale;
-
- protected final static int TOUCH_STATE_REST = 0;
- protected final static int TOUCH_STATE_SCROLLING = 1;
- protected final static int TOUCH_STATE_PREV_PAGE = 2;
- protected final static int TOUCH_STATE_NEXT_PAGE = 3;
- protected final static int TOUCH_STATE_REORDERING = 4;
-
- protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
-
- protected int mTouchState = TOUCH_STATE_REST;
- protected boolean mForceScreenScrolled = false;
-
- protected OnLongClickListener mLongClickListener;
-
- protected int mTouchSlop;
- private int mPagingTouchSlop;
- private int mMaximumVelocity;
- private int mMinimumWidth;
- protected int mPageSpacing;
- protected int mCellCountX = 0;
- protected int mCellCountY = 0;
- protected boolean mAllowOverScroll = true;
- protected int mUnboundedScrollX;
- protected int[] mTempVisiblePagesRange = new int[2];
- protected boolean mForceDrawAllChildrenNextFrame;
-
- // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise
- // it is equal to the scaled overscroll position. We use a separate value so as to prevent
- // the screens from continuing to translate beyond the normal bounds.
- protected int mOverScrollX;
-
- // parameter that adjusts the layout to be optimized for pages with that scale factor
- protected float mLayoutScale = 1.0f;
-
- protected static final int INVALID_POINTER = -1;
-
- protected int mActivePointerId = INVALID_POINTER;
-
- private PageSwitchListener mPageSwitchListener;
-
- protected ArrayList<Boolean> mDirtyPageContent;
-
- // If true, syncPages and syncPageItems will be called to refresh pages
- protected boolean mContentIsRefreshable = true;
-
- // If true, modify alpha of neighboring pages as user scrolls left/right
- protected boolean mFadeInAdjacentScreens = false;
-
- // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
- // to switch to a new page
- protected boolean mUsePagingTouchSlop = true;
-
- // If true, the subclass should directly update scrollX itself in its computeScroll method
- // (SmoothPagedView does this)
- protected boolean mDeferScrollUpdate = false;
-
- protected boolean mIsPageMoving = false;
-
- // All syncs and layout passes are deferred until data is ready.
- protected boolean mIsDataReady = true;
-
- // Scrolling indicator
- private ValueAnimator mScrollIndicatorAnimator;
- private View mScrollIndicator;
- private int mScrollIndicatorPaddingLeft;
- private int mScrollIndicatorPaddingRight;
- private boolean mShouldShowScrollIndicator = false;
- private boolean mShouldShowScrollIndicatorImmediately = false;
- protected static final int sScrollIndicatorFadeInDuration = 150;
- protected static final int sScrollIndicatorFadeOutDuration = 650;
- protected static final int sScrollIndicatorFlashDuration = 650;
-
- // The viewport whether the pages are to be contained (the actual view may be larger than the
- // viewport)
- private Rect mViewport = new Rect();
-
- // Reordering
- // We use the min scale to determine how much to expand the actually PagedView measured
- // dimensions such that when we are zoomed out, the view is not clipped
- private int REORDERING_DROP_REPOSITION_DURATION = 200;
- protected int REORDERING_REORDER_REPOSITION_DURATION = 300;
- protected int REORDERING_ZOOM_IN_OUT_DURATION = 250;
- private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 300;
- private float REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE = 0.1f;
- private long REORDERING_DELETE_DROP_TARGET_FADE_DURATION = 150;
- private float mMinScale = 1f;
- protected View mDragView;
- protected AnimatorSet mZoomInOutAnim;
- private Runnable mSidePageHoverRunnable;
- private int mSidePageHoverIndex = -1;
- // This variable's scope is only for the duration of startReordering() and endReordering()
- private boolean mReorderingStarted = false;
- // This variable's scope is for the duration of startReordering() and after the zoomIn()
- // animation after endReordering()
- private boolean mIsReordering;
- // The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition
- private int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
- private int mPostReorderingPreZoomInRemainingAnimationCount;
- private Runnable mPostReorderingPreZoomInRunnable;
-
- // Edge swiping
- private boolean mOnlyAllowEdgeSwipes = false;
- private boolean mDownEventOnEdge = false;
- private int mEdgeSwipeRegionSize = 0;
-
- // Convenience/caching
- private Matrix mTmpInvMatrix = new Matrix();
- private float[] mTmpPoint = new float[2];
- private Rect mTmpRect = new Rect();
- private Rect mAltTmpRect = new Rect();
-
- // Fling to delete
- private int FLING_TO_DELETE_FADE_OUT_DURATION = 350;
- private float FLING_TO_DELETE_FRICTION = 0.035f;
- // The degrees specifies how much deviation from the up vector to still consider a fling "up"
- private float FLING_TO_DELETE_MAX_FLING_DEGREES = 65f;
- protected int mFlingToDeleteThresholdVelocity = -1400;
- // Drag to delete
- private boolean mDeferringForDelete = false;
- private int DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250;
- private int DRAG_TO_DELETE_FADE_OUT_DURATION = 350;
-
- // Drop to delete
- private View mDeleteDropTarget;
-
- // Bouncer
- private boolean mTopAlignPageWhenShrinkingForBouncer = false;
-
- public interface PageSwitchListener {
- void onPageSwitching(View newPage, int newPageIndex);
- void onPageSwitched(View newPage, int newPageIndex);
- }
-
- public PagedView(Context context) {
- this(context, null);
- }
-
- public PagedView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PagedView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.PagedView, defStyle, 0);
- setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
- mScrollIndicatorPaddingLeft =
- a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0);
- mScrollIndicatorPaddingRight =
- a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
- a.recycle();
-
- Resources r = getResources();
- mEdgeSwipeRegionSize = r.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
- mTopAlignPageWhenShrinkingForBouncer =
- r.getBoolean(R.bool.kg_top_align_page_shrink_on_bouncer_visible);
-
- setHapticFeedbackEnabled(false);
- init();
- }
-
- /**
- * Initializes various states for this workspace.
- */
- protected void init() {
- mDirtyPageContent = new ArrayList<Boolean>();
- mDirtyPageContent.ensureCapacity(32);
- mScroller = new Scroller(getContext(), new ScrollInterpolator());
- mCurrentPage = 0;
-
- final ViewConfiguration configuration = ViewConfiguration.get(getContext());
- mTouchSlop = configuration.getScaledTouchSlop();
- mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
- mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
- mDensity = getResources().getDisplayMetrics().density;
-
- // Scale the fling-to-delete threshold by the density
- mFlingToDeleteThresholdVelocity =
- (int) (mFlingToDeleteThresholdVelocity * mDensity);
-
- mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
- mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
- mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
- setOnHierarchyChangeListener(this);
- }
-
- void setDeleteDropTarget(View v) {
- mDeleteDropTarget = v;
- }
-
- // Convenience methods to map points from self to parent and vice versa
- float[] mapPointFromViewToParent(View v, float x, float y) {
- mTmpPoint[0] = x;
- mTmpPoint[1] = y;
- v.getMatrix().mapPoints(mTmpPoint);
- mTmpPoint[0] += v.getLeft();
- mTmpPoint[1] += v.getTop();
- return mTmpPoint;
- }
- float[] mapPointFromParentToView(View v, float x, float y) {
- mTmpPoint[0] = x - v.getLeft();
- mTmpPoint[1] = y - v.getTop();
- v.getMatrix().invert(mTmpInvMatrix);
- mTmpInvMatrix.mapPoints(mTmpPoint);
- return mTmpPoint;
- }
-
- void updateDragViewTranslationDuringDrag() {
- float x = mLastMotionX - mDownMotionX + getScrollX() - mDownScrollX;
- float y = mLastMotionY - mDownMotionY;
- mDragView.setTranslationX(x);
- mDragView.setTranslationY(y);
-
- if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): " + x + ", " + y);
- }
-
- public void setMinScale(float f) {
- mMinScale = f;
- requestLayout();
- }
-
- @Override
- public void setScaleX(float scaleX) {
- super.setScaleX(scaleX);
- if (isReordering(true)) {
- float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
- mLastMotionX = p[0];
- mLastMotionY = p[1];
- updateDragViewTranslationDuringDrag();
- }
- }
-
- // Convenience methods to get the actual width/height of the PagedView (since it is measured
- // to be larger to account for the minimum possible scale)
- int getViewportWidth() {
- return mViewport.width();
- }
- int getViewportHeight() {
- return mViewport.height();
- }
-
- // Convenience methods to get the offset ASSUMING that we are centering the pages in the
- // PagedView both horizontally and vertically
- int getViewportOffsetX() {
- return (getMeasuredWidth() - getViewportWidth()) / 2;
- }
- int getViewportOffsetY() {
- return (getMeasuredHeight() - getViewportHeight()) / 2;
- }
-
- public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
- mPageSwitchListener = pageSwitchListener;
- if (mPageSwitchListener != null) {
- mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage);
- }
- }
-
- /**
- * Called by subclasses to mark that data is ready, and that we can begin loading and laying
- * out pages.
- */
- protected void setDataIsReady() {
- mIsDataReady = true;
- }
-
- protected boolean isDataReady() {
- return mIsDataReady;
- }
-
- /**
- * Returns the index of the currently displayed page.
- *
- * @return The index of the currently displayed page.
- */
- int getCurrentPage() {
- return mCurrentPage;
- }
-
- int getNextPage() {
- return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
- }
-
- int getPageCount() {
- return getChildCount();
- }
-
- View getPageAt(int index) {
- return getChildAt(index);
- }
-
- protected int indexToPage(int index) {
- return index;
- }
-
- /**
- * Updates the scroll of the current page immediately to its final scroll position. We use this
- * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
- * the previous tab page.
- */
- protected void updateCurrentPageScroll() {
- int offset = getChildOffset(mCurrentPage);
- int relOffset = getRelativeChildOffset(mCurrentPage);
- int newX = offset - relOffset;
- scrollTo(newX, 0);
- mScroller.setFinalX(newX);
- mScroller.forceFinished(true);
- }
-
- /**
- * Sets the current page.
- */
- void setCurrentPage(int currentPage) {
- notifyPageSwitching(currentPage);
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
- // don't introduce any checks like mCurrentPage == currentPage here-- if we change the
- // the default
- if (getChildCount() == 0) {
- return;
- }
-
- mForceScreenScrolled = true;
- mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
- updateCurrentPageScroll();
- updateScrollingIndicator();
- notifyPageSwitched();
- invalidate();
- }
-
- public void setOnlyAllowEdgeSwipes(boolean enable) {
- mOnlyAllowEdgeSwipes = enable;
- }
-
- protected void notifyPageSwitching(int whichPage) {
- if (mPageSwitchListener != null) {
- mPageSwitchListener.onPageSwitching(getPageAt(whichPage), whichPage);
- }
- }
-
- protected void notifyPageSwitched() {
- if (mPageSwitchListener != null) {
- mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage);
- }
- }
-
- protected void pageBeginMoving() {
- if (!mIsPageMoving) {
- mIsPageMoving = true;
- onPageBeginMoving();
- }
- }
-
- protected void pageEndMoving() {
- if (mIsPageMoving) {
- mIsPageMoving = false;
- onPageEndMoving();
- }
- }
-
- protected boolean isPageMoving() {
- return mIsPageMoving;
- }
-
- // a method that subclasses can override to add behavior
- protected void onPageBeginMoving() {
- }
-
- // a method that subclasses can override to add behavior
- protected void onPageEndMoving() {
- }
-
- /**
- * Registers the specified listener on each page contained in this workspace.
- *
- * @param l The listener used to respond to long clicks.
- */
- @Override
- public void setOnLongClickListener(OnLongClickListener l) {
- mLongClickListener = l;
- final int count = getPageCount();
- for (int i = 0; i < count; i++) {
- getPageAt(i).setOnLongClickListener(l);
- }
- }
-
- @Override
- public void scrollBy(int x, int y) {
- scrollTo(mUnboundedScrollX + x, getScrollY() + y);
- }
-
- @Override
- public void scrollTo(int x, int y) {
- mUnboundedScrollX = x;
-
- if (x < 0) {
- super.scrollTo(0, y);
- if (mAllowOverScroll) {
- overScroll(x);
- }
- } else if (x > mMaxScrollX) {
- super.scrollTo(mMaxScrollX, y);
- if (mAllowOverScroll) {
- overScroll(x - mMaxScrollX);
- }
- } else {
- mOverScrollX = x;
- super.scrollTo(x, y);
- }
-
- mTouchX = x;
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-
- // Update the last motion events when scrolling
- if (isReordering(true)) {
- float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
- mLastMotionX = p[0];
- mLastMotionY = p[1];
- updateDragViewTranslationDuringDrag();
- }
- }
-
- // we moved this functionality to a helper function so SmoothPagedView can reuse it
- protected boolean computeScrollHelper() {
- if (mScroller.computeScrollOffset()) {
- // Don't bother scrolling if the page does not need to be moved
- if (getScrollX() != mScroller.getCurrX()
- || getScrollY() != mScroller.getCurrY()
- || mOverScrollX != mScroller.getCurrX()) {
- scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
- }
- invalidate();
- return true;
- } else if (mNextPage != INVALID_PAGE) {
- mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
- mNextPage = INVALID_PAGE;
- notifyPageSwitched();
-
- // We don't want to trigger a page end moving unless the page has settled
- // and the user has stopped scrolling
- if (mTouchState == TOUCH_STATE_REST) {
- pageEndMoving();
- }
-
- onPostReorderingAnimationCompleted();
- return true;
- }
- return false;
- }
-
- @Override
- public void computeScroll() {
- computeScrollHelper();
- }
-
- protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
- return mTopAlignPageWhenShrinkingForBouncer;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (!mIsDataReady || getChildCount() == 0) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- return;
- }
-
- // We measure the dimensions of the PagedView to be larger than the pages so that when we
- // zoom out (and scale down), the view is still contained in the parent
- View parent = (View) getParent();
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the
- // viewport, we can be at most one and a half screens offset once we scale down
- DisplayMetrics dm = getResources().getDisplayMetrics();
- int maxSize = Math.max(dm.widthPixels, dm.heightPixels);
- int parentWidthSize = (int) (1.5f * maxSize);
- int parentHeightSize = maxSize;
- int scaledWidthSize = (int) (parentWidthSize / mMinScale);
- int scaledHeightSize = (int) (parentHeightSize / mMinScale);
- mViewport.set(0, 0, widthSize, heightSize);
-
- if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- return;
- }
-
- // Return early if we aren't given a proper dimension
- if (widthSize <= 0 || heightSize <= 0) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- return;
- }
-
- /* Allow the height to be set as WRAP_CONTENT. This allows the particular case
- * of the All apps view on XLarge displays to not take up more space then it needs. Width
- * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect
- * each page to have the same width.
- */
- final int verticalPadding = getPaddingTop() + getPaddingBottom();
- final int horizontalPadding = getPaddingLeft() + getPaddingRight();
-
- // The children are given the same width and height as the workspace
- // unless they were set to WRAP_CONTENT
- if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
- if (DEBUG) Log.d(TAG, "PagedView.scaledSize: " + scaledWidthSize + ", " + scaledHeightSize);
- if (DEBUG) Log.d(TAG, "PagedView.parentSize: " + parentWidthSize + ", " + parentHeightSize);
- if (DEBUG) Log.d(TAG, "PagedView.horizontalPadding: " + horizontalPadding);
- if (DEBUG) Log.d(TAG, "PagedView.verticalPadding: " + verticalPadding);
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- // disallowing padding in paged view (just pass 0)
- final View child = getPageAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- int childWidthMode;
- if (lp.width == LayoutParams.WRAP_CONTENT) {
- childWidthMode = MeasureSpec.AT_MOST;
- } else {
- childWidthMode = MeasureSpec.EXACTLY;
- }
-
- int childHeightMode;
- if (lp.height == LayoutParams.WRAP_CONTENT) {
- childHeightMode = MeasureSpec.AT_MOST;
- } else {
- childHeightMode = MeasureSpec.EXACTLY;
- }
-
- final int childWidthMeasureSpec =
- MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode);
- final int childHeightMeasureSpec =
- MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);
-
- child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- }
- setMeasuredDimension(scaledWidthSize, scaledHeightSize);
-
- // We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions.
- // We also wait until we set the measured dimensions before flushing the cache as well, to
- // ensure that the cache is filled with good values.
- invalidateCachedOffsets();
-
- if (mChildCountOnLastMeasure != getChildCount() && !mDeferringForDelete) {
- setCurrentPage(mCurrentPage);
- }
- mChildCountOnLastMeasure = getChildCount();
-
- if (childCount > 0) {
- if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getViewportWidth() + ", "
- + getChildWidth(0));
-
- // Calculate the variable page spacing if necessary
- if (mPageSpacing == AUTOMATIC_PAGE_SPACING) {
- // The gap between pages in the PagedView should be equal to the gap from the page
- // to the edge of the screen (so it is not visible in the current screen). To
- // account for unequal padding on each side of the paged view, we take the maximum
- // of the left/right gap and use that as the gap between each page.
- int offset = getRelativeChildOffset(0);
- int spacing = Math.max(offset, widthSize - offset -
- getChildAt(0).getMeasuredWidth());
- setPageSpacing(spacing);
- }
- }
-
- updateScrollingIndicatorPosition();
-
- if (childCount > 0) {
- mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
- } else {
- mMaxScrollX = 0;
- }
- }
-
- public void setPageSpacing(int pageSpacing) {
- mPageSpacing = pageSpacing;
- invalidateCachedOffsets();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (!mIsDataReady || getChildCount() == 0) {
- return;
- }
-
- if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
- final int childCount = getChildCount();
-
- int offsetX = getViewportOffsetX();
- int offsetY = getViewportOffsetY();
-
- // Update the viewport offsets
- mViewport.offset(offsetX, offsetY);
-
- int childLeft = offsetX + getRelativeChildOffset(0);
- for (int i = 0; i < childCount; i++) {
- final View child = getPageAt(i);
- int childTop = offsetY + getPaddingTop();
- if (child.getVisibility() != View.GONE) {
- final int childWidth = getScaledMeasuredWidth(child);
- final int childHeight = child.getMeasuredHeight();
-
- if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(), childTop + childHeight);
- childLeft += childWidth + mPageSpacing;
- }
- }
-
- if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
- setHorizontalScrollBarEnabled(false);
- updateCurrentPageScroll();
- setHorizontalScrollBarEnabled(true);
- mFirstLayout = false;
- }
- }
-
- protected void screenScrolled(int screenCenter) {
- }
-
- @Override
- public void onChildViewAdded(View parent, View child) {
- // This ensures that when children are added, they get the correct transforms / alphas
- // in accordance with any scroll effects.
- mForceScreenScrolled = true;
- invalidate();
- invalidateCachedOffsets();
- }
-
- @Override
- public void onChildViewRemoved(View parent, View child) {
- mForceScreenScrolled = true;
- invalidate();
- invalidateCachedOffsets();
- }
-
- protected void invalidateCachedOffsets() {
- int count = getChildCount();
- if (count == 0) {
- mChildOffsets = null;
- mChildRelativeOffsets = null;
- mChildOffsetsWithLayoutScale = null;
- return;
- }
-
- mChildOffsets = new int[count];
- mChildRelativeOffsets = new int[count];
- mChildOffsetsWithLayoutScale = new int[count];
- for (int i = 0; i < count; i++) {
- mChildOffsets[i] = -1;
- mChildRelativeOffsets[i] = -1;
- mChildOffsetsWithLayoutScale[i] = -1;
- }
- }
-
- protected int getChildOffset(int index) {
- if (index < 0 || index > getChildCount() - 1) return 0;
-
- int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ?
- mChildOffsets : mChildOffsetsWithLayoutScale;
-
- if (childOffsets != null && childOffsets[index] != -1) {
- return childOffsets[index];
- } else {
- if (getChildCount() == 0)
- return 0;
-
- int offset = getRelativeChildOffset(0);
- for (int i = 0; i < index; ++i) {
- offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing;
- }
- if (childOffsets != null) {
- childOffsets[index] = offset;
- }
- return offset;
- }
- }
-
- protected int getRelativeChildOffset(int index) {
- if (index < 0 || index > getChildCount() - 1) return 0;
-
- if (mChildRelativeOffsets != null && mChildRelativeOffsets[index] != -1) {
- return mChildRelativeOffsets[index];
- } else {
- final int padding = getPaddingLeft() + getPaddingRight();
- final int offset = getPaddingLeft() +
- (getViewportWidth() - padding - getChildWidth(index)) / 2;
- if (mChildRelativeOffsets != null) {
- mChildRelativeOffsets[index] = offset;
- }
- return offset;
- }
- }
-
- protected int getScaledMeasuredWidth(View child) {
- // This functions are called enough times that it actually makes a difference in the
- // profiler -- so just inline the max() here
- final int measuredWidth = child.getMeasuredWidth();
- final int minWidth = mMinimumWidth;
- final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth;
- return (int) (maxWidth * mLayoutScale + 0.5f);
- }
-
- void boundByReorderablePages(boolean isReordering, int[] range) {
- // Do nothing
- }
-
- // TODO: Fix this
- protected void getVisiblePages(int[] range) {
- range[0] = 0;
- range[1] = getPageCount() - 1;
-
- /*
- final int pageCount = getChildCount();
-
- if (pageCount > 0) {
- final int screenWidth = getViewportWidth();
- int leftScreen = 0;
- int rightScreen = 0;
- int offsetX = getViewportOffsetX() + getScrollX();
- View currPage = getPageAt(leftScreen);
- while (leftScreen < pageCount - 1 &&
- currPage.getX() + currPage.getWidth() -
- currPage.getPaddingRight() < offsetX) {
- leftScreen++;
- currPage = getPageAt(leftScreen);
- }
- rightScreen = leftScreen;
- currPage = getPageAt(rightScreen + 1);
- while (rightScreen < pageCount - 1 &&
- currPage.getX() - currPage.getPaddingLeft() < offsetX + screenWidth) {
- rightScreen++;
- currPage = getPageAt(rightScreen + 1);
- }
-
- // TEMP: this is a hacky way to ensure that animations to new pages are not clipped
- // because we don't draw them while scrolling?
- range[0] = Math.max(0, leftScreen - 1);
- range[1] = Math.min(rightScreen + 1, getChildCount() - 1);
- } else {
- range[0] = -1;
- range[1] = -1;
- }
- */
- }
-
- protected boolean shouldDrawChild(View child) {
- return child.getAlpha() > 0;
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- int halfScreenSize = getViewportWidth() / 2;
- // mOverScrollX is equal to getScrollX() when we're within the normal scroll range.
- // Otherwise it is equal to the scaled overscroll position.
- int screenCenter = mOverScrollX + halfScreenSize;
-
- if (screenCenter != mLastScreenCenter || mForceScreenScrolled) {
- // set mForceScreenScrolled before calling screenScrolled so that screenScrolled can
- // set it for the next frame
- mForceScreenScrolled = false;
- screenScrolled(screenCenter);
- mLastScreenCenter = screenCenter;
- }
-
- // Find out which screens are visible; as an optimization we only call draw on them
- final int pageCount = getChildCount();
- if (pageCount > 0) {
- getVisiblePages(mTempVisiblePagesRange);
- final int leftScreen = mTempVisiblePagesRange[0];
- final int rightScreen = mTempVisiblePagesRange[1];
- if (leftScreen != -1 && rightScreen != -1) {
- final long drawingTime = getDrawingTime();
- // Clip to the bounds
- canvas.save();
- canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(),
- getScrollY() + getBottom() - getTop());
-
- // Draw all the children, leaving the drag view for last
- for (int i = pageCount - 1; i >= 0; i--) {
- final View v = getPageAt(i);
- if (v == mDragView) continue;
- if (mForceDrawAllChildrenNextFrame ||
- (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) {
- drawChild(canvas, v, drawingTime);
- }
- }
- // Draw the drag view on top (if there is one)
- if (mDragView != null) {
- drawChild(canvas, mDragView, drawingTime);
- }
-
- mForceDrawAllChildrenNextFrame = false;
- canvas.restore();
- }
- }
- }
-
- @Override
- public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
- int page = indexToPage(indexOfChild(child));
- if (page != mCurrentPage || !mScroller.isFinished()) {
- snapToPage(page);
- return true;
- }
- return false;
- }
-
- @Override
- protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- int focusablePage;
- if (mNextPage != INVALID_PAGE) {
- focusablePage = mNextPage;
- } else {
- focusablePage = mCurrentPage;
- }
- View v = getPageAt(focusablePage);
- if (v != null) {
- return v.requestFocus(direction, previouslyFocusedRect);
- }
- return false;
- }
-
- @Override
- public boolean dispatchUnhandledMove(View focused, int direction) {
- if (direction == View.FOCUS_LEFT) {
- if (getCurrentPage() > 0) {
- snapToPage(getCurrentPage() - 1);
- return true;
- }
- } else if (direction == View.FOCUS_RIGHT) {
- if (getCurrentPage() < getPageCount() - 1) {
- snapToPage(getCurrentPage() + 1);
- return true;
- }
- }
- return super.dispatchUnhandledMove(focused, direction);
- }
-
- @Override
- public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
- if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
- getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
- }
- if (direction == View.FOCUS_LEFT) {
- if (mCurrentPage > 0) {
- getPageAt(mCurrentPage - 1).addFocusables(views, direction, focusableMode);
- }
- } else if (direction == View.FOCUS_RIGHT){
- if (mCurrentPage < getPageCount() - 1) {
- getPageAt(mCurrentPage + 1).addFocusables(views, direction, focusableMode);
- }
- }
- }
-
- /**
- * If one of our descendant views decides that it could be focused now, only
- * pass that along if it's on the current page.
- *
- * This happens when live folders requery, and if they're off page, they
- * end up calling requestFocus, which pulls it on page.
- */
- @Override
- public void focusableViewAvailable(View focused) {
- View current = getPageAt(mCurrentPage);
- View v = focused;
- while (true) {
- if (v == current) {
- super.focusableViewAvailable(focused);
- return;
- }
- if (v == this) {
- return;
- }
- ViewParent parent = v.getParent();
- if (parent instanceof View) {
- v = (View)v.getParent();
- } else {
- return;
- }
- }
- }
-
- /**
- * Return true if a tap at (x, y) should trigger a flip to the previous page.
- */
- protected boolean hitsPreviousPage(float x, float y) {
- return (x < getViewportOffsetX() + getRelativeChildOffset(mCurrentPage) - mPageSpacing);
- }
-
- /**
- * Return true if a tap at (x, y) should trigger a flip to the next page.
- */
- protected boolean hitsNextPage(float x, float y) {
- return (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
- }
-
- /** Returns whether x and y originated within the buffered viewport */
- private boolean isTouchPointInViewportWithBuffer(int x, int y) {
- mTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top,
- mViewport.right + mViewport.width() / 2, mViewport.bottom);
- return mTmpRect.contains(x, y);
- }
-
- /** Returns whether x and y originated within the current page view bounds */
- private boolean isTouchPointInCurrentPage(int x, int y) {
- View v = getPageAt(getCurrentPage());
- if (v != null) {
- mTmpRect.set((v.getLeft() - getScrollX()), 0, (v.getRight() - getScrollX()),
- v.getBottom());
- return mTmpRect.contains(x, y);
- }
- return false;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (DISABLE_TOUCH_INTERACTION) {
- return false;
- }
-
- /*
- * This method JUST determines whether we want to intercept the motion.
- * If we return true, onTouchEvent will be called and we do the actual
- * scrolling there.
- */
- acquireVelocityTrackerAndAddMovement(ev);
-
- // Skip touch handling if there are no pages to swipe
- if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
-
- /*
- * Shortcut the most recurring case: the user is in the dragging
- * state and he is moving his finger. We want to intercept this
- * motion.
- */
- final int action = ev.getAction();
- if ((action == MotionEvent.ACTION_MOVE) &&
- (mTouchState == TOUCH_STATE_SCROLLING)) {
- return true;
- }
-
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_MOVE: {
- /*
- * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
- * whether the user has moved far enough from his original down touch.
- */
- if (mActivePointerId != INVALID_POINTER) {
- determineScrollingStart(ev);
- break;
- }
- // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
- // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
- // i.e. fall through to the next case (don't break)
- // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
- // while it's small- this was causing a crash before we checked for INVALID_POINTER)
- }
-
- case MotionEvent.ACTION_DOWN: {
- final float x = ev.getX();
- final float y = ev.getY();
- // Remember location of down touch
- mDownMotionX = x;
- mDownMotionY = y;
- mDownScrollX = getScrollX();
- mLastMotionX = x;
- mLastMotionY = y;
- float[] p = mapPointFromViewToParent(this, x, y);
- mParentDownMotionX = p[0];
- mParentDownMotionY = p[1];
- mLastMotionXRemainder = 0;
- mTotalMotionX = 0;
- mActivePointerId = ev.getPointerId(0);
-
- // Determine if the down event is within the threshold to be an edge swipe
- int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
- int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
- if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
- mDownEventOnEdge = true;
- }
-
- /*
- * If being flinged and user touches the screen, initiate drag;
- * otherwise don't. mScroller.isFinished should be false when
- * being flinged.
- */
- final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
- final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
- if (finishedScrolling) {
- mTouchState = TOUCH_STATE_REST;
- mScroller.abortAnimation();
- } else {
- if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) {
- mTouchState = TOUCH_STATE_SCROLLING;
- } else {
- mTouchState = TOUCH_STATE_REST;
- }
- }
-
- // check if this can be the beginning of a tap on the side of the pages
- // to scroll the current page
- if (!DISABLE_TOUCH_SIDE_PAGES) {
- if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
- if (getChildCount() > 0) {
- if (hitsPreviousPage(x, y)) {
- mTouchState = TOUCH_STATE_PREV_PAGE;
- } else if (hitsNextPage(x, y)) {
- mTouchState = TOUCH_STATE_NEXT_PAGE;
- }
- }
- }
- }
- break;
- }
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- resetTouchState();
- // Just intercept the touch event on up if we tap outside the strict viewport
- if (!isTouchPointInCurrentPage((int) mLastMotionX, (int) mLastMotionY)) {
- return true;
- }
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- releaseVelocityTracker();
- break;
- }
-
- /*
- * The only time we want to intercept motion events is if we are in the
- * drag mode.
- */
- return mTouchState != TOUCH_STATE_REST;
- }
-
- protected void determineScrollingStart(MotionEvent ev) {
- determineScrollingStart(ev, 1.0f);
- }
-
- /*
- * Determines if we should change the touch state to start scrolling after the
- * user moves their touch point too far.
- */
- protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
- // Disallow scrolling if we don't have a valid pointer index
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == -1) return;
-
- // Disallow scrolling if we started the gesture from outside the viewport
- final float x = ev.getX(pointerIndex);
- final float y = ev.getY(pointerIndex);
- if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return;
-
- // If we're only allowing edge swipes, we break out early if the down event wasn't
- // at the edge.
- if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return;
-
- final int xDiff = (int) Math.abs(x - mLastMotionX);
- final int yDiff = (int) Math.abs(y - mLastMotionY);
-
- final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
- boolean xPaged = xDiff > mPagingTouchSlop;
- boolean xMoved = xDiff > touchSlop;
- boolean yMoved = yDiff > touchSlop;
-
- if (xMoved || xPaged || yMoved) {
- if (mUsePagingTouchSlop ? xPaged : xMoved) {
- // Scroll if the user moved far enough along the X axis
- mTouchState = TOUCH_STATE_SCROLLING;
- mTotalMotionX += Math.abs(mLastMotionX - x);
- mLastMotionX = x;
- mLastMotionXRemainder = 0;
- mTouchX = getViewportOffsetX() + getScrollX();
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
- pageBeginMoving();
- }
- }
- }
-
- protected float getMaxScrollProgress() {
- return 1.0f;
- }
-
- protected float getBoundedScrollProgress(int screenCenter, View v, int page) {
- final int halfScreenSize = getViewportWidth() / 2;
-
- screenCenter = Math.min(mScrollX + halfScreenSize, screenCenter);
- screenCenter = Math.max(halfScreenSize, screenCenter);
-
- return getScrollProgress(screenCenter, v, page);
- }
-
- protected float getScrollProgress(int screenCenter, View v, int page) {
- final int halfScreenSize = getViewportWidth() / 2;
-
- int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing;
- int delta = screenCenter - (getChildOffset(page) -
- getRelativeChildOffset(page) + halfScreenSize);
-
- float scrollProgress = delta / (totalDistance * 1.0f);
- scrollProgress = Math.min(scrollProgress, getMaxScrollProgress());
- scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress());
- return scrollProgress;
- }
-
- // This curve determines how the effect of scrolling over the limits of the page dimishes
- // as the user pulls further and further from the bounds
- private float overScrollInfluenceCurve(float f) {
- f -= 1.0f;
- return f * f * f + 1.0f;
- }
-
- protected void acceleratedOverScroll(float amount) {
- int screenSize = getViewportWidth();
-
- // We want to reach the max over scroll effect when the user has
- // over scrolled half the size of the screen
- float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);
-
- if (f == 0) return;
-
- // Clamp this factor, f, to -1 < f < 1
- if (Math.abs(f) >= 1) {
- f /= Math.abs(f);
- }
-
- int overScrollAmount = (int) Math.round(f * screenSize);
- if (amount < 0) {
- mOverScrollX = overScrollAmount;
- super.scrollTo(0, getScrollY());
- } else {
- mOverScrollX = mMaxScrollX + overScrollAmount;
- super.scrollTo(mMaxScrollX, getScrollY());
- }
- invalidate();
- }
-
- protected void dampedOverScroll(float amount) {
- int screenSize = getViewportWidth();
-
- float f = (amount / screenSize);
-
- if (f == 0) return;
- f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
-
- // Clamp this factor, f, to -1 < f < 1
- if (Math.abs(f) >= 1) {
- f /= Math.abs(f);
- }
-
- int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
- if (amount < 0) {
- mOverScrollX = overScrollAmount;
- super.scrollTo(0, getScrollY());
- } else {
- mOverScrollX = mMaxScrollX + overScrollAmount;
- super.scrollTo(mMaxScrollX, getScrollY());
- }
- invalidate();
- }
-
- protected void overScroll(float amount) {
- dampedOverScroll(amount);
- }
-
- protected float maxOverScroll() {
- // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
- // exceed). Used to find out how much extra wallpaper we need for the over scroll effect
- float f = 1.0f;
- f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
- return OVERSCROLL_DAMP_FACTOR * f;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (DISABLE_TOUCH_INTERACTION) {
- return false;
- }
-
- // Skip touch handling if there are no pages to swipe
- if (getChildCount() <= 0) return super.onTouchEvent(ev);
-
- acquireVelocityTrackerAndAddMovement(ev);
-
- final int action = ev.getAction();
-
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN:
- /*
- * If being flinged and user touches, stop the fling. isFinished
- * will be false if being flinged.
- */
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
-
- // Remember where the motion event started
- mDownMotionX = mLastMotionX = ev.getX();
- mDownMotionY = mLastMotionY = ev.getY();
- mDownScrollX = getScrollX();
- float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
- mParentDownMotionX = p[0];
- mParentDownMotionY = p[1];
- mLastMotionXRemainder = 0;
- mTotalMotionX = 0;
- mActivePointerId = ev.getPointerId(0);
-
- // Determine if the down event is within the threshold to be an edge swipe
- int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
- int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
- if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
- mDownEventOnEdge = true;
- }
-
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- pageBeginMoving();
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- // Scroll to follow the motion event
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
-
- if (pointerIndex == -1) return true;
-
- final float x = ev.getX(pointerIndex);
- final float deltaX = mLastMotionX + mLastMotionXRemainder - x;
-
- mTotalMotionX += Math.abs(deltaX);
-
- // Only scroll and update mLastMotionX if we have moved some discrete amount. We
- // keep the remainder because we are actually testing if we've moved from the last
- // scrolled position (which is discrete).
- if (Math.abs(deltaX) >= 1.0f) {
- mTouchX += deltaX;
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
- if (!mDeferScrollUpdate) {
- scrollBy((int) deltaX, 0);
- if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX);
- } else {
- invalidate();
- }
- mLastMotionX = x;
- mLastMotionXRemainder = deltaX - (int) deltaX;
- } else {
- awakenScrollBars();
- }
- } else if (mTouchState == TOUCH_STATE_REORDERING) {
- // Update the last motion position
- mLastMotionX = ev.getX();
- mLastMotionY = ev.getY();
-
- // Update the parent down so that our zoom animations take this new movement into
- // account
- float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
- mParentDownMotionX = pt[0];
- mParentDownMotionY = pt[1];
- updateDragViewTranslationDuringDrag();
-
- // Find the closest page to the touch point
- final int dragViewIndex = indexOfChild(mDragView);
- int bufferSize = (int) (REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE *
- getViewportWidth());
- int leftBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.left, 0)[0]
- + bufferSize);
- int rightBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.right, 0)[0]
- - bufferSize);
-
- // Change the drag view if we are hovering over the drop target
- boolean isHoveringOverDelete = isHoveringOverDeleteDropTarget(
- (int) mParentDownMotionX, (int) mParentDownMotionY);
- setPageHoveringOverDeleteDropTarget(dragViewIndex, isHoveringOverDelete);
-
- if (DEBUG) Log.d(TAG, "leftBufferEdge: " + leftBufferEdge);
- if (DEBUG) Log.d(TAG, "rightBufferEdge: " + rightBufferEdge);
- if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX);
- if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY);
- if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX);
- if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
-
- float parentX = mParentDownMotionX;
- int pageIndexToSnapTo = -1;
- if (parentX < leftBufferEdge && dragViewIndex > 0) {
- pageIndexToSnapTo = dragViewIndex - 1;
- } else if (parentX > rightBufferEdge && dragViewIndex < getChildCount() - 1) {
- pageIndexToSnapTo = dragViewIndex + 1;
- }
-
- final int pageUnderPointIndex = pageIndexToSnapTo;
- if (pageUnderPointIndex > -1 && !isHoveringOverDelete) {
- mTempVisiblePagesRange[0] = 0;
- mTempVisiblePagesRange[1] = getPageCount() - 1;
- boundByReorderablePages(true, mTempVisiblePagesRange);
- if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
- pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
- pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
- mSidePageHoverIndex = pageUnderPointIndex;
- mSidePageHoverRunnable = new Runnable() {
- @Override
- public void run() {
- // Update the down scroll position to account for the fact that the
- // current page is moved
- mDownScrollX = getChildOffset(pageUnderPointIndex)
- - getRelativeChildOffset(pageUnderPointIndex);
-
- // Setup the scroll to the correct page before we swap the views
- snapToPage(pageUnderPointIndex);
-
- // For each of the pages between the paged view and the drag view,
- // animate them from the previous position to the new position in
- // the layout (as a result of the drag view moving in the layout)
- int shiftDelta = (dragViewIndex < pageUnderPointIndex) ? -1 : 1;
- int lowerIndex = (dragViewIndex < pageUnderPointIndex) ?
- dragViewIndex + 1 : pageUnderPointIndex;
- int upperIndex = (dragViewIndex > pageUnderPointIndex) ?
- dragViewIndex - 1 : pageUnderPointIndex;
- for (int i = lowerIndex; i <= upperIndex; ++i) {
- View v = getChildAt(i);
- // dragViewIndex < pageUnderPointIndex, so after we remove the
- // drag view all subsequent views to pageUnderPointIndex will
- // shift down.
- int oldX = getViewportOffsetX() + getChildOffset(i);
- int newX = getViewportOffsetX() + getChildOffset(i + shiftDelta);
-
- // Animate the view translation from its old position to its new
- // position
- AnimatorSet anim = (AnimatorSet) v.getTag();
- if (anim != null) {
- anim.cancel();
- }
-
- v.setTranslationX(oldX - newX);
- anim = new AnimatorSet();
- anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
- anim.playTogether(
- ObjectAnimator.ofFloat(v, "translationX", 0f));
- anim.start();
- v.setTag(anim);
- }
-
- removeView(mDragView);
- onRemoveView(mDragView, false);
- addView(mDragView, pageUnderPointIndex);
- onAddView(mDragView, pageUnderPointIndex);
- mSidePageHoverIndex = -1;
- }
- };
- postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
- }
- } else {
- removeCallbacks(mSidePageHoverRunnable);
- mSidePageHoverIndex = -1;
- }
- } else {
- determineScrollingStart(ev);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- final int activePointerId = mActivePointerId;
- final int pointerIndex = ev.findPointerIndex(activePointerId);
- final float x = ev.getX(pointerIndex);
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
- final int deltaX = (int) (x - mDownMotionX);
- final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage));
- boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
- SIGNIFICANT_MOVE_THRESHOLD;
-
- mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
-
- boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
- Math.abs(velocityX) > mFlingThresholdVelocity;
-
- // In the case that the page is moved far to one direction and then is flung
- // in the opposite direction, we use a threshold to determine whether we should
- // just return to the starting page, or if we should skip one further.
- boolean returnToOriginalPage = false;
- if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
- Math.signum(velocityX) != Math.signum(deltaX) && isFling) {
- returnToOriginalPage = true;
- }
-
- int finalPage;
- // We give flings precedence over large moves, which is why we short-circuit our
- // test for a large move if a fling has been registered. That is, a large
- // move to the left and fling to the right will register as a fling to the right.
- if (((isSignificantMove && deltaX > 0 && !isFling) ||
- (isFling && velocityX > 0)) && mCurrentPage > 0) {
- finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
- snapToPageWithVelocity(finalPage, velocityX);
- } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
- (isFling && velocityX < 0)) &&
- mCurrentPage < getChildCount() - 1) {
- finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
- snapToPageWithVelocity(finalPage, velocityX);
- } else {
- snapToDestination();
- }
- } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
- // at this point we have not moved beyond the touch slop
- // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
- // we can just page
- int nextPage = Math.max(0, mCurrentPage - 1);
- if (nextPage != mCurrentPage) {
- snapToPage(nextPage);
- } else {
- snapToDestination();
- }
- } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
- // at this point we have not moved beyond the touch slop
- // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
- // we can just page
- int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
- if (nextPage != mCurrentPage) {
- snapToPage(nextPage);
- } else {
- snapToDestination();
- }
- } else if (mTouchState == TOUCH_STATE_REORDERING) {
- // Update the last motion position
- mLastMotionX = ev.getX();
- mLastMotionY = ev.getY();
-
- // Update the parent down so that our zoom animations take this new movement into
- // account
- float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
- mParentDownMotionX = pt[0];
- mParentDownMotionY = pt[1];
- updateDragViewTranslationDuringDrag();
- boolean handledFling = false;
- if (!DISABLE_FLING_TO_DELETE) {
- // Check the velocity and see if we are flinging-to-delete
- PointF flingToDeleteVector = isFlingingToDelete();
- if (flingToDeleteVector != null) {
- onFlingToDelete(flingToDeleteVector);
- handledFling = true;
- }
- }
- if (!handledFling && isHoveringOverDeleteDropTarget((int) mParentDownMotionX,
- (int) mParentDownMotionY)) {
- onDropToDelete();
- }
- } else {
- onUnhandledTap(ev);
- }
-
- // Remove the callback to wait for the side page hover timeout
- removeCallbacks(mSidePageHoverRunnable);
- // End any intermediate reordering states
- resetTouchState();
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- snapToDestination();
- }
- resetTouchState();
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- break;
- }
-
- return true;
- }
-
- //public abstract void onFlingToDelete(View v);
- public abstract void onRemoveView(View v, boolean deletePermanently);
- public abstract void onRemoveViewAnimationCompleted();
- public abstract void onAddView(View v, int index);
-
- private void resetTouchState() {
- releaseVelocityTracker();
- endReordering();
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- mDownEventOnEdge = false;
- }
-
- protected void onUnhandledTap(MotionEvent ev) {}
-
- @Override
- public boolean onGenericMotionEvent(MotionEvent event) {
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_SCROLL: {
- // Handle mouse (or ext. device) by shifting the page depending on the scroll
- final float vscroll;
- final float hscroll;
- if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
- vscroll = 0;
- hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
- } else {
- vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
- hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
- }
- if (hscroll != 0 || vscroll != 0) {
- if (hscroll > 0 || vscroll > 0) {
- scrollRight();
- } else {
- scrollLeft();
- }
- return true;
- }
- }
- }
- }
- return super.onGenericMotionEvent(event);
- }
-
- private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
- }
-
- private void releaseVelocityTracker() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- private void onSecondaryPointerUp(MotionEvent ev) {
- final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
- MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- final int pointerId = ev.getPointerId(pointerIndex);
- if (pointerId == mActivePointerId) {
- // This was our active pointer going up. Choose a new
- // active pointer and adjust accordingly.
- // TODO: Make this decision more intelligent.
- final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
- mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
- mLastMotionY = ev.getY(newPointerIndex);
- mLastMotionXRemainder = 0;
- mActivePointerId = ev.getPointerId(newPointerIndex);
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- }
- }
-
- @Override
- public void requestChildFocus(View child, View focused) {
- super.requestChildFocus(child, focused);
- int page = indexToPage(indexOfChild(child));
- if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
- snapToPage(page);
- }
- }
-
- protected int getChildIndexForRelativeOffset(int relativeOffset) {
- final int childCount = getChildCount();
- int left;
- int right;
- for (int i = 0; i < childCount; ++i) {
- left = getRelativeChildOffset(i);
- right = (left + getScaledMeasuredWidth(getPageAt(i)));
- if (left <= relativeOffset && relativeOffset <= right) {
- return i;
- }
- }
- return -1;
- }
-
- protected int getChildWidth(int index) {
- // This functions are called enough times that it actually makes a difference in the
- // profiler -- so just inline the max() here
- final int measuredWidth = getPageAt(index).getMeasuredWidth();
- final int minWidth = mMinimumWidth;
- return (minWidth > measuredWidth) ? minWidth : measuredWidth;
- }
-
- int getPageNearestToPoint(float x) {
- int index = 0;
- for (int i = 0; i < getChildCount(); ++i) {
- if (x < getChildAt(i).getRight() - getScrollX()) {
- return index;
- } else {
- index++;
- }
- }
- return Math.min(index, getChildCount() - 1);
- }
-
- int getPageNearestToCenterOfScreen() {
- int minDistanceFromScreenCenter = Integer.MAX_VALUE;
- int minDistanceFromScreenCenterIndex = -1;
- int screenCenter = getViewportOffsetX() + getScrollX() + (getViewportWidth() / 2);
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; ++i) {
- View layout = (View) getPageAt(i);
- int childWidth = getScaledMeasuredWidth(layout);
- int halfChildWidth = (childWidth / 2);
- int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth;
- int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
- if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
- minDistanceFromScreenCenter = distanceFromScreenCenter;
- minDistanceFromScreenCenterIndex = i;
- }
- }
- return minDistanceFromScreenCenterIndex;
- }
-
- protected void snapToDestination() {
- snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
- }
-
- private static class ScrollInterpolator implements Interpolator {
- public ScrollInterpolator() {
- }
-
- public float getInterpolation(float t) {
- t -= 1.0f;
- return t*t*t*t*t + 1;
- }
- }
-
- // We want the duration of the page snap animation to be influenced by the distance that
- // the screen has to travel, however, we don't want this duration to be effected in a
- // purely linear fashion. Instead, we use this method to moderate the effect that the distance
- // of travel has on the overall snap duration.
- float distanceInfluenceForSnapDuration(float f) {
- f -= 0.5f; // center the values about 0.
- f *= 0.3f * Math.PI / 2.0f;
- return (float) Math.sin(f);
- }
-
- protected void snapToPageWithVelocity(int whichPage, int velocity) {
- whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
- int halfScreenSize = getViewportWidth() / 2;
-
- if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
- if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
- + getViewportWidth() + ", " + getChildWidth(whichPage));
- final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
- int delta = newX - mUnboundedScrollX;
- int duration = 0;
-
- if (Math.abs(velocity) < mMinFlingVelocity) {
- // If the velocity is low enough, then treat this more as an automatic page advance
- // as opposed to an apparent physical response to flinging
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
- return;
- }
-
- // Here we compute a "distance" that will be used in the computation of the overall
- // snap duration. This is a function of the actual distance that needs to be traveled;
- // we keep this value close to half screen size in order to reduce the variance in snap
- // duration as a function of the distance the page needs to travel.
- float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize));
- float distance = halfScreenSize + halfScreenSize *
- distanceInfluenceForSnapDuration(distanceRatio);
-
- velocity = Math.abs(velocity);
- velocity = Math.max(mMinSnapVelocity, velocity);
-
- // we want the page's snap velocity to approximately match the velocity at which the
- // user flings, so we scale the duration by a value near to the derivative of the scroll
- // interpolator at zero, ie. 5. We use 4 to make it a little slower.
- duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
-
- snapToPage(whichPage, delta, duration);
- }
-
- protected void snapToPage(int whichPage) {
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
- }
- protected void snapToPageImmediately(int whichPage) {
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
- }
-
- protected void snapToPage(int whichPage, int duration) {
- snapToPage(whichPage, duration, false);
- }
- protected void snapToPage(int whichPage, int duration, boolean immediate) {
- whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
-
- if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
- if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getViewportWidth() + ", "
- + getChildWidth(whichPage));
- int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
- final int sX = mUnboundedScrollX;
- final int delta = newX - sX;
- snapToPage(whichPage, delta, duration, immediate);
- }
-
- protected void snapToPage(int whichPage, int delta, int duration) {
- snapToPage(whichPage, delta, duration, false);
- }
- protected void snapToPage(int whichPage, int delta, int duration, boolean immediate) {
- mNextPage = whichPage;
- notifyPageSwitching(whichPage);
- View focusedChild = getFocusedChild();
- if (focusedChild != null && whichPage != mCurrentPage &&
- focusedChild == getPageAt(mCurrentPage)) {
- focusedChild.clearFocus();
- }
-
- pageBeginMoving();
- awakenScrollBars(duration);
- if (immediate) {
- duration = 0;
- } else if (duration == 0) {
- duration = Math.abs(delta);
- }
-
- if (!mScroller.isFinished()) mScroller.abortAnimation();
- mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
-
- notifyPageSwitched();
-
- // Trigger a compute() to finish switching pages if necessary
- if (immediate) {
- computeScroll();
- }
-
- mForceScreenScrolled = true;
- invalidate();
- }
-
- public void scrollLeft() {
- if (mScroller.isFinished()) {
- if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
- } else {
- if (mNextPage > 0) snapToPage(mNextPage - 1);
- }
- }
-
- public void scrollRight() {
- if (mScroller.isFinished()) {
- if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
- } else {
- if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
- }
- }
-
- public int getPageForView(View v) {
- int result = -1;
- if (v != null) {
- ViewParent vp = v.getParent();
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- if (vp == getPageAt(i)) {
- return i;
- }
- }
- }
- return result;
- }
-
- public static class SavedState extends BaseSavedState {
- int currentPage = -1;
-
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- private SavedState(Parcel in) {
- super(in);
- currentPage = in.readInt();
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(currentPage);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR =
- new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- protected View getScrollingIndicator() {
- return null;
- }
-
- protected boolean isScrollingIndicatorEnabled() {
- return false;
- }
-
- Runnable hideScrollingIndicatorRunnable = new Runnable() {
- @Override
- public void run() {
- hideScrollingIndicator(false);
- }
- };
-
- protected void flashScrollingIndicator(boolean animated) {
- removeCallbacks(hideScrollingIndicatorRunnable);
- showScrollingIndicator(!animated);
- postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration);
- }
-
- protected void showScrollingIndicator(boolean immediately) {
- mShouldShowScrollIndicator = true;
- mShouldShowScrollIndicatorImmediately = true;
- if (getChildCount() <= 1) return;
- if (!isScrollingIndicatorEnabled()) return;
-
- mShouldShowScrollIndicator = false;
- getScrollingIndicator();
- if (mScrollIndicator != null) {
- // Fade the indicator in
- updateScrollingIndicatorPosition();
- mScrollIndicator.setVisibility(View.VISIBLE);
- cancelScrollingIndicatorAnimations();
- if (immediately) {
- mScrollIndicator.setAlpha(1f);
- } else {
- mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f);
- mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration);
- mScrollIndicatorAnimator.start();
- }
- }
- }
-
- protected void cancelScrollingIndicatorAnimations() {
- if (mScrollIndicatorAnimator != null) {
- mScrollIndicatorAnimator.cancel();
- }
- }
-
- protected void hideScrollingIndicator(boolean immediately) {
- if (getChildCount() <= 1) return;
- if (!isScrollingIndicatorEnabled()) return;
-
- getScrollingIndicator();
- if (mScrollIndicator != null) {
- // Fade the indicator out
- updateScrollingIndicatorPosition();
- cancelScrollingIndicatorAnimations();
- if (immediately) {
- mScrollIndicator.setVisibility(View.INVISIBLE);
- mScrollIndicator.setAlpha(0f);
- } else {
- mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f);
- mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration);
- mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean cancelled = false;
- @Override
- public void onAnimationCancel(android.animation.Animator animation) {
- cancelled = true;
- }
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!cancelled) {
- mScrollIndicator.setVisibility(View.INVISIBLE);
- }
- }
- });
- mScrollIndicatorAnimator.start();
- }
- }
- }
-
- /**
- * To be overridden by subclasses to determine whether the scroll indicator should stretch to
- * fill its space on the track or not.
- */
- protected boolean hasElasticScrollIndicator() {
- return true;
- }
-
- private void updateScrollingIndicator() {
- if (getChildCount() <= 1) return;
- if (!isScrollingIndicatorEnabled()) return;
-
- getScrollingIndicator();
- if (mScrollIndicator != null) {
- updateScrollingIndicatorPosition();
- }
- if (mShouldShowScrollIndicator) {
- showScrollingIndicator(mShouldShowScrollIndicatorImmediately);
- }
- }
-
- private void updateScrollingIndicatorPosition() {
- if (!isScrollingIndicatorEnabled()) return;
- if (mScrollIndicator == null) return;
- int numPages = getChildCount();
- int pageWidth = getViewportWidth();
- int lastChildIndex = Math.max(0, getChildCount() - 1);
- int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
- int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
- int indicatorWidth = mScrollIndicator.getMeasuredWidth() -
- mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight();
-
- float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX));
- int indicatorSpace = trackWidth / numPages;
- int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft;
- if (hasElasticScrollIndicator()) {
- if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) {
- mScrollIndicator.getLayoutParams().width = indicatorSpace;
- mScrollIndicator.requestLayout();
- }
- } else {
- int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2;
- indicatorPos += indicatorCenterOffset;
- }
- mScrollIndicator.setTranslationX(indicatorPos);
- }
-
- // Animate the drag view back to the original position
- void animateDragViewToOriginalPosition() {
- if (mDragView != null) {
- AnimatorSet anim = new AnimatorSet();
- anim.setDuration(REORDERING_DROP_REPOSITION_DURATION);
- anim.playTogether(
- ObjectAnimator.ofFloat(mDragView, "translationX", 0f),
- ObjectAnimator.ofFloat(mDragView, "translationY", 0f));
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- onPostReorderingAnimationCompleted();
- }
- });
- anim.start();
- }
- }
-
- // "Zooms out" the PagedView to reveal more side pages
- protected boolean zoomOut() {
- if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
- mZoomInOutAnim.cancel();
- }
-
- if (!(getScaleX() < 1f || getScaleY() < 1f)) {
- mZoomInOutAnim = new AnimatorSet();
- mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
- mZoomInOutAnim.playTogether(
- ObjectAnimator.ofFloat(this, "scaleX", mMinScale),
- ObjectAnimator.ofFloat(this, "scaleY", mMinScale));
- mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- // Show the delete drop target
- if (mDeleteDropTarget != null) {
- mDeleteDropTarget.setVisibility(View.VISIBLE);
- mDeleteDropTarget.animate().alpha(1f)
- .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mDeleteDropTarget.setAlpha(0f);
- }
- });
- }
- }
- });
- mZoomInOutAnim.start();
- return true;
- }
- return false;
- }
-
- protected void onStartReordering() {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- announceForAccessibility(mContext.getString(
- R.string.keyguard_accessibility_widget_reorder_start));
- }
-
- // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
- mTouchState = TOUCH_STATE_REORDERING;
- mIsReordering = true;
-
- // Mark all the non-widget pages as invisible
- getVisiblePages(mTempVisiblePagesRange);
- boundByReorderablePages(true, mTempVisiblePagesRange);
- for (int i = 0; i < getPageCount(); ++i) {
- if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
- getPageAt(i).setAlpha(0f);
- }
- }
-
- // We must invalidate to trigger a redraw to update the layers such that the drag view
- // is always drawn on top
- invalidate();
- }
-
- private void onPostReorderingAnimationCompleted() {
- // Trigger the callback when reordering has settled
- --mPostReorderingPreZoomInRemainingAnimationCount;
- if (mPostReorderingPreZoomInRunnable != null &&
- mPostReorderingPreZoomInRemainingAnimationCount == 0) {
- mPostReorderingPreZoomInRunnable.run();
- mPostReorderingPreZoomInRunnable = null;
- }
- }
-
- protected void onEndReordering() {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- announceForAccessibility(mContext.getString(
- R.string.keyguard_accessibility_widget_reorder_end));
- }
- mIsReordering = false;
-
- // Mark all the non-widget pages as visible again
- getVisiblePages(mTempVisiblePagesRange);
- boundByReorderablePages(true, mTempVisiblePagesRange);
- for (int i = 0; i < getPageCount(); ++i) {
- if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
- getPageAt(i).setAlpha(1f);
- }
- }
- }
-
- public boolean startReordering() {
- int dragViewIndex = getPageNearestToCenterOfScreen();
- mTempVisiblePagesRange[0] = 0;
- mTempVisiblePagesRange[1] = getPageCount() - 1;
- boundByReorderablePages(true, mTempVisiblePagesRange);
- mReorderingStarted = true;
-
- // Check if we are within the reordering range
- if (mTempVisiblePagesRange[0] <= dragViewIndex &&
- dragViewIndex <= mTempVisiblePagesRange[1]) {
- if (zoomOut()) {
- // Find the drag view under the pointer
- mDragView = getChildAt(dragViewIndex);
-
- onStartReordering();
- }
- return true;
- }
- return false;
- }
-
- boolean isReordering(boolean testTouchState) {
- boolean state = mIsReordering;
- if (testTouchState) {
- state &= (mTouchState == TOUCH_STATE_REORDERING);
- }
- return state;
- }
- void endReordering() {
- // For simplicity, we call endReordering sometimes even if reordering was never started.
- // In that case, we don't want to do anything.
- if (!mReorderingStarted) return;
- mReorderingStarted = false;
-
- // If we haven't flung-to-delete the current child, then we just animate the drag view
- // back into position
- final Runnable onCompleteRunnable = new Runnable() {
- @Override
- public void run() {
- onEndReordering();
- }
- };
- if (!mDeferringForDelete) {
- mPostReorderingPreZoomInRunnable = new Runnable() {
- public void run() {
- zoomIn(onCompleteRunnable);
- };
- };
-
- mPostReorderingPreZoomInRemainingAnimationCount =
- NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT;
- // Snap to the current page
- snapToPage(indexOfChild(mDragView), 0);
- // Animate the drag view back to the front position
- animateDragViewToOriginalPosition();
- } else {
- // Handled in post-delete-animation-callbacks
- }
- }
-
- // "Zooms in" the PagedView to highlight the current page
- protected boolean zoomIn(final Runnable onCompleteRunnable) {
- if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
- mZoomInOutAnim.cancel();
- }
- if (getScaleX() < 1f || getScaleY() < 1f) {
- mZoomInOutAnim = new AnimatorSet();
- mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
- mZoomInOutAnim.playTogether(
- ObjectAnimator.ofFloat(this, "scaleX", 1f),
- ObjectAnimator.ofFloat(this, "scaleY", 1f));
- mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- // Hide the delete drop target
- if (mDeleteDropTarget != null) {
- mDeleteDropTarget.animate().alpha(0f)
- .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mDeleteDropTarget.setVisibility(View.GONE);
- }
- });
- }
- }
- @Override
- public void onAnimationCancel(Animator animation) {
- mDragView = null;
- }
- @Override
- public void onAnimationEnd(Animator animation) {
- mDragView = null;
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
- }
- });
- mZoomInOutAnim.start();
- return true;
- } else {
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
- }
- return false;
- }
-
- /*
- * Flinging to delete - IN PROGRESS
- */
- private PointF isFlingingToDelete() {
- ViewConfiguration config = ViewConfiguration.get(getContext());
- mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
-
- if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
- // Do a quick dot product test to ensure that we are flinging upwards
- PointF vel = new PointF(mVelocityTracker.getXVelocity(),
- mVelocityTracker.getYVelocity());
- PointF upVec = new PointF(0f, -1f);
- float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
- (vel.length() * upVec.length()));
- if (theta <= Math.toRadians(FLING_TO_DELETE_MAX_FLING_DEGREES)) {
- return vel;
- }
- }
- return null;
- }
-
- /**
- * Creates an animation from the current drag view along its current velocity vector.
- * For this animation, the alpha runs for a fixed duration and we update the position
- * progressively.
- */
- private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener {
- private View mDragView;
- private PointF mVelocity;
- private Rect mFrom;
- private long mPrevTime;
- private float mFriction;
-
- private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
-
- public FlingAlongVectorAnimatorUpdateListener(View dragView, PointF vel, Rect from,
- long startTime, float friction) {
- mDragView = dragView;
- mVelocity = vel;
- mFrom = from;
- mPrevTime = startTime;
- mFriction = 1f - (mDragView.getResources().getDisplayMetrics().density * friction);
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = ((Float) animation.getAnimatedValue()).floatValue();
- long curTime = AnimationUtils.currentAnimationTimeMillis();
-
- mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f);
- mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f);
-
- mDragView.setTranslationX(mFrom.left);
- mDragView.setTranslationY(mFrom.top);
- mDragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
-
- mVelocity.x *= mFriction;
- mVelocity.y *= mFriction;
- mPrevTime = curTime;
- }
- };
-
- private Runnable createPostDeleteAnimationRunnable(final View dragView) {
- return new Runnable() {
- @Override
- public void run() {
- int dragViewIndex = indexOfChild(dragView);
-
- // For each of the pages around the drag view, animate them from the previous
- // position to the new position in the layout (as a result of the drag view moving
- // in the layout)
- // NOTE: We can make an assumption here because we have side-bound pages that we
- // will always have pages to animate in from the left
- getVisiblePages(mTempVisiblePagesRange);
- boundByReorderablePages(true, mTempVisiblePagesRange);
- boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]);
- boolean slideFromLeft = (isLastWidgetPage ||
- dragViewIndex > mTempVisiblePagesRange[0]);
-
- // Setup the scroll to the correct page before we swap the views
- if (slideFromLeft) {
- snapToPageImmediately(dragViewIndex - 1);
- }
-
- int firstIndex = (isLastWidgetPage ? 0 : mTempVisiblePagesRange[0]);
- int lastIndex = Math.min(mTempVisiblePagesRange[1], getPageCount() - 1);
- int lowerIndex = (slideFromLeft ? firstIndex : dragViewIndex + 1 );
- int upperIndex = (slideFromLeft ? dragViewIndex - 1 : lastIndex);
- ArrayList<Animator> animations = new ArrayList<Animator>();
- for (int i = lowerIndex; i <= upperIndex; ++i) {
- View v = getChildAt(i);
- // dragViewIndex < pageUnderPointIndex, so after we remove the
- // drag view all subsequent views to pageUnderPointIndex will
- // shift down.
- int oldX = 0;
- int newX = 0;
- if (slideFromLeft) {
- if (i == 0) {
- // Simulate the page being offscreen with the page spacing
- oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i)
- - mPageSpacing;
- } else {
- oldX = getViewportOffsetX() + getChildOffset(i - 1);
- }
- newX = getViewportOffsetX() + getChildOffset(i);
- } else {
- oldX = getChildOffset(i) - getChildOffset(i - 1);
- newX = 0;
- }
-
- // Animate the view translation from its old position to its new
- // position
- AnimatorSet anim = (AnimatorSet) v.getTag();
- if (anim != null) {
- anim.cancel();
- }
-
- // Note: Hacky, but we want to skip any optimizations to not draw completely
- // hidden views
- v.setAlpha(Math.max(v.getAlpha(), 0.01f));
- v.setTranslationX(oldX - newX);
- anim = new AnimatorSet();
- anim.playTogether(
- ObjectAnimator.ofFloat(v, "translationX", 0f),
- ObjectAnimator.ofFloat(v, "alpha", 1f));
- animations.add(anim);
- v.setTag(anim);
- }
-
- AnimatorSet slideAnimations = new AnimatorSet();
- slideAnimations.playTogether(animations);
- slideAnimations.setDuration(DELETE_SLIDE_IN_SIDE_PAGE_DURATION);
- slideAnimations.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- final Runnable onCompleteRunnable = new Runnable() {
- @Override
- public void run() {
- mDeferringForDelete = false;
- onEndReordering();
- onRemoveViewAnimationCompleted();
- }
- };
- zoomIn(onCompleteRunnable);
- }
- });
- slideAnimations.start();
-
- removeView(dragView);
- onRemoveView(dragView, true);
- }
- };
- }
-
- public void onFlingToDelete(PointF vel) {
- final long startTime = AnimationUtils.currentAnimationTimeMillis();
-
- // NOTE: Because it takes time for the first frame of animation to actually be
- // called and we expect the animation to be a continuation of the fling, we have
- // to account for the time that has elapsed since the fling finished. And since
- // we don't have a startDelay, we will always get call to update when we call
- // start() (which we want to ignore).
- final TimeInterpolator tInterpolator = new TimeInterpolator() {
- private int mCount = -1;
- private long mStartTime;
- private float mOffset;
- /* Anonymous inner class ctor */ {
- mStartTime = startTime;
- }
-
- @Override
- public float getInterpolation(float t) {
- if (mCount < 0) {
- mCount++;
- } else if (mCount == 0) {
- mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
- mStartTime) / FLING_TO_DELETE_FADE_OUT_DURATION);
- mCount++;
- }
- return Math.min(1f, mOffset + t);
- }
- };
-
- final Rect from = new Rect();
- final View dragView = mDragView;
- from.left = (int) dragView.getTranslationX();
- from.top = (int) dragView.getTranslationY();
- AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel,
- from, startTime, FLING_TO_DELETE_FRICTION);
-
- final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);
-
- // Create and start the animation
- ValueAnimator mDropAnim = new ValueAnimator();
- mDropAnim.setInterpolator(tInterpolator);
- mDropAnim.setDuration(FLING_TO_DELETE_FADE_OUT_DURATION);
- mDropAnim.setFloatValues(0f, 1f);
- mDropAnim.addUpdateListener(updateCb);
- mDropAnim.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- onAnimationEndRunnable.run();
- }
- });
- mDropAnim.start();
- mDeferringForDelete = true;
- }
-
- /* Drag to delete */
- private boolean isHoveringOverDeleteDropTarget(int x, int y) {
- if (mDeleteDropTarget != null) {
- mAltTmpRect.set(0, 0, 0, 0);
- View parent = (View) mDeleteDropTarget.getParent();
- if (parent != null) {
- parent.getGlobalVisibleRect(mAltTmpRect);
- }
- mDeleteDropTarget.getGlobalVisibleRect(mTmpRect);
- mTmpRect.offset(-mAltTmpRect.left, -mAltTmpRect.top);
- return mTmpRect.contains(x, y);
- }
- return false;
- }
-
- protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {}
-
- private void onDropToDelete() {
- final View dragView = mDragView;
-
- final float toScale = 0f;
- final float toAlpha = 0f;
-
- // Create and start the complex animation
- ArrayList<Animator> animations = new ArrayList<Animator>();
- AnimatorSet motionAnim = new AnimatorSet();
- motionAnim.setInterpolator(new DecelerateInterpolator(2));
- motionAnim.playTogether(
- ObjectAnimator.ofFloat(dragView, "scaleX", toScale),
- ObjectAnimator.ofFloat(dragView, "scaleY", toScale));
- animations.add(motionAnim);
-
- AnimatorSet alphaAnim = new AnimatorSet();
- alphaAnim.setInterpolator(new LinearInterpolator());
- alphaAnim.playTogether(
- ObjectAnimator.ofFloat(dragView, "alpha", toAlpha));
- animations.add(alphaAnim);
-
- final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);
-
- AnimatorSet anim = new AnimatorSet();
- anim.playTogether(animations);
- anim.setDuration(DRAG_TO_DELETE_FADE_OUT_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- onAnimationEndRunnable.run();
- }
- });
- anim.start();
-
- mDeferringForDelete = true;
- }
-
- /* Accessibility */
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- info.setScrollable(getPageCount() > 1);
- if (getCurrentPage() < getPageCount() - 1) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
- }
- if (getCurrentPage() > 0) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
- }
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- event.setScrollable(true);
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
- event.setFromIndex(mCurrentPage);
- event.setToIndex(mCurrentPage);
- event.setItemCount(getChildCount());
- }
- }
-
- @Override
- public boolean performAccessibilityAction(int action, Bundle arguments) {
- if (super.performAccessibilityAction(action, arguments)) {
- return true;
- }
- switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
- if (getCurrentPage() < getPageCount() - 1) {
- scrollRight();
- return true;
- }
- } break;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
- if (getCurrentPage() > 0) {
- scrollLeft();
- return true;
- }
- } break;
- }
- return false;
- }
-
- @Override
- public boolean onHoverEvent(android.view.MotionEvent event) {
- return true;
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java b/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java
deleted file mode 100644
index 7760279..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-public interface SecurityMessageDisplay {
- public void setMessage(CharSequence msg, boolean important);
-
- public void setMessage(int resId, boolean important);
-
- public void setMessage(int resId, boolean important, Object... formatArgs);
-
- public void setTimeout(int timeout_ms);
-
- public void showBouncer(int animationDuration);
-
- public void hideBouncer(int animationDuration);
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
deleted file mode 100644
index 073225f..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
+++ /dev/null
@@ -1,1244 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import com.android.internal.R;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.util.Property;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Interpolator;
-import android.widget.Scroller;
-
-/**
- * This layout handles interaction with the sliding security challenge views
- * that overlay/resize other keyguard contents.
- */
-public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
- private static final String TAG = "SlidingChallengeLayout";
- private static final boolean DEBUG = false;
-
- // The drag handle is measured in dp above & below the top edge of the
- // challenge view; these parameters change based on whether the challenge
- // is open or closed.
- private static final int DRAG_HANDLE_CLOSED_ABOVE = 8; // dp
- private static final int DRAG_HANDLE_CLOSED_BELOW = 0; // dp
- private static final int DRAG_HANDLE_OPEN_ABOVE = 8; // dp
- private static final int DRAG_HANDLE_OPEN_BELOW = 0; // dp
-
- private static final int HANDLE_ANIMATE_DURATION = 250; // ms
-
- // Drawn to show the drag handle in closed state; crossfades to the challenge view
- // when challenge is fully visible
- private boolean mEdgeCaptured;
-
- private DisplayMetrics mDisplayMetrics;
-
- // Initialized during measurement from child layoutparams
- private View mExpandChallengeView;
- private KeyguardSecurityContainer mChallengeView;
- private View mScrimView;
- private View mWidgetsView;
-
- // Range: 0 (fully hidden) to 1 (fully visible)
- private float mChallengeOffset = 1.f;
- private boolean mChallengeShowing = true;
- private boolean mChallengeShowingTargetState = true;
- private boolean mWasChallengeShowing = true;
- private boolean mIsBouncing = false;
-
- private final Scroller mScroller;
- private ObjectAnimator mFader;
- private int mScrollState;
- private OnChallengeScrolledListener mScrollListener;
- private OnBouncerStateChangedListener mBouncerListener;
-
- public static final int SCROLL_STATE_IDLE = 0;
- public static final int SCROLL_STATE_DRAGGING = 1;
- public static final int SCROLL_STATE_SETTLING = 2;
- public static final int SCROLL_STATE_FADING = 3;
-
- private static final int CHALLENGE_FADE_OUT_DURATION = 100;
- private static final int CHALLENGE_FADE_IN_DURATION = 160;
-
- private static final int MAX_SETTLE_DURATION = 600; // ms
-
- // ID of the pointer in charge of a current drag
- private int mActivePointerId = INVALID_POINTER;
- private static final int INVALID_POINTER = -1;
-
- // True if the user is currently dragging the slider
- private boolean mDragging;
- // True if the user may not drag until a new gesture begins
- private boolean mBlockDrag;
-
- private VelocityTracker mVelocityTracker;
- private int mMinVelocity;
- private int mMaxVelocity;
- private float mGestureStartX, mGestureStartY; // where did you first touch the screen?
- private int mGestureStartChallengeBottom; // where was the challenge at that time?
-
- private int mDragHandleClosedBelow; // handle hitrect extension into the challenge view
- private int mDragHandleClosedAbove; // extend the handle's hitrect this far above the line
- private int mDragHandleOpenBelow; // handle hitrect extension into the challenge view
- private int mDragHandleOpenAbove; // extend the handle's hitrect this far above the line
-
- private int mDragHandleEdgeSlop;
- private int mChallengeBottomBound; // Number of pixels from the top of the challenge view
- // that should remain on-screen
-
- private int mTouchSlop;
- private int mTouchSlopSquare;
-
- float mHandleAlpha;
- float mFrameAlpha;
- float mFrameAnimationTarget = Float.MIN_VALUE;
- private ObjectAnimator mHandleAnimation;
- private ObjectAnimator mFrameAnimation;
-
- private boolean mHasGlowpad;
-
- // We have an internal and external version, and we and them together.
- private boolean mChallengeInteractiveExternal = true;
- private boolean mChallengeInteractiveInternal = true;
-
- static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA =
- new FloatProperty<SlidingChallengeLayout>("handleAlpha") {
- @Override
- public void setValue(SlidingChallengeLayout view, float value) {
- view.mHandleAlpha = value;
- view.invalidate();
- }
-
- @Override
- public Float get(SlidingChallengeLayout view) {
- return view.mHandleAlpha;
- }
- };
-
- // True if at least one layout pass has happened since the view was attached.
- private boolean mHasLayout;
-
- private static final Interpolator sMotionInterpolator = new Interpolator() {
- public float getInterpolation(float t) {
- t -= 1.0f;
- return t * t * t * t * t + 1.0f;
- }
- };
-
- private static final Interpolator sHandleFadeInterpolator = new Interpolator() {
- public float getInterpolation(float t) {
- return t * t;
- }
- };
-
- private final Runnable mEndScrollRunnable = new Runnable () {
- public void run() {
- completeChallengeScroll();
- }
- };
-
- private final OnClickListener mScrimClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- hideBouncer();
- }
- };
-
- private final OnClickListener mExpandChallengeClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (!isChallengeShowing()) {
- showChallenge(true);
- }
- }
- };
-
- /**
- * Listener interface that reports changes in scroll state of the challenge area.
- */
- public interface OnChallengeScrolledListener {
- /**
- * The scroll state itself changed.
- *
- * <p>scrollState will be one of the following:</p>
- *
- * <ul>
- * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li>
- * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging
- * the challenge area.</li>
- * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating
- * into place.</li>
- * </ul>
- *
- * <p>Do not perform expensive operations (e.g. layout)
- * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p>
- *
- * @param scrollState The new scroll state of the challenge area.
- */
- public void onScrollStateChanged(int scrollState);
-
- /**
- * The precise position of the challenge area has changed.
- *
- * <p>NOTE: It is NOT safe to modify layout or call any View methods that may
- * result in a requestLayout anywhere in your view hierarchy as a result of this call.
- * It may be called during drawing.</p>
- *
- * @param scrollPosition New relative position of the challenge area.
- * 1.f = fully visible/ready to be interacted with.
- * 0.f = fully invisible/inaccessible to the user.
- * @param challengeTop Position of the top edge of the challenge view in px in the
- * SlidingChallengeLayout's coordinate system.
- */
- public void onScrollPositionChanged(float scrollPosition, int challengeTop);
- }
-
- public SlidingChallengeLayout(Context context) {
- this(context, null);
- }
-
- public SlidingChallengeLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- mScroller = new Scroller(context, sMotionInterpolator);
-
- final ViewConfiguration vc = ViewConfiguration.get(context);
- mMinVelocity = vc.getScaledMinimumFlingVelocity();
- mMaxVelocity = vc.getScaledMaximumFlingVelocity();
-
- final Resources res = getResources();
- mDragHandleEdgeSlop = res.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
-
- mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mTouchSlopSquare = mTouchSlop * mTouchSlop;
-
- mDisplayMetrics = res.getDisplayMetrics();
- final float density = mDisplayMetrics.density;
-
- // top half of the lock icon, plus another 25% to be sure
- mDragHandleClosedAbove = (int) (DRAG_HANDLE_CLOSED_ABOVE * density + 0.5f);
- mDragHandleClosedBelow = (int) (DRAG_HANDLE_CLOSED_BELOW * density + 0.5f);
- mDragHandleOpenAbove = (int) (DRAG_HANDLE_OPEN_ABOVE * density + 0.5f);
- mDragHandleOpenBelow = (int) (DRAG_HANDLE_OPEN_BELOW * density + 0.5f);
-
- // how much space to account for in the handle when closed
- mChallengeBottomBound = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding);
-
- setWillNotDraw(false);
- setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE);
- }
-
- public void setHandleAlpha(float alpha) {
- if (mExpandChallengeView != null) {
- mExpandChallengeView.setAlpha(alpha);
- }
- }
-
- public void setChallengeInteractive(boolean interactive) {
- mChallengeInteractiveExternal = interactive;
- if (mExpandChallengeView != null) {
- mExpandChallengeView.setEnabled(interactive);
- }
- }
-
- void animateHandle(boolean visible) {
- if (mHandleAnimation != null) {
- mHandleAnimation.cancel();
- mHandleAnimation = null;
- }
- final float targetAlpha = visible ? 1.f : 0.f;
- if (targetAlpha == mHandleAlpha) {
- return;
- }
- mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, targetAlpha);
- mHandleAnimation.setInterpolator(sHandleFadeInterpolator);
- mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION);
- mHandleAnimation.start();
- }
-
- private void sendInitialListenerUpdates() {
- if (mScrollListener != null) {
- int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
- mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop);
- mScrollListener.onScrollStateChanged(mScrollState);
- }
- }
-
- public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) {
- mScrollListener = listener;
- if (mHasLayout) {
- sendInitialListenerUpdates();
- }
- }
-
- public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
- mBouncerListener = listener;
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- mHasLayout = false;
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- removeCallbacks(mEndScrollRunnable);
- mHasLayout = false;
- }
-
- @Override
- public void requestChildFocus(View child, View focused) {
- if (mIsBouncing && child != mChallengeView) {
- // Clear out of the bouncer if the user tries to move focus outside of
- // the security challenge view.
- hideBouncer();
- }
- super.requestChildFocus(child, focused);
- }
-
- // We want the duration of the page snap animation to be influenced by the distance that
- // the screen has to travel, however, we don't want this duration to be effected in a
- // purely linear fashion. Instead, we use this method to moderate the effect that the distance
- // of travel has on the overall snap duration.
- float distanceInfluenceForSnapDuration(float f) {
- f -= 0.5f; // center the values about 0.
- f *= 0.3f * Math.PI / 2.0f;
- return (float) Math.sin(f);
- }
-
- void setScrollState(int state) {
- if (mScrollState != state) {
- mScrollState = state;
-
- animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
- if (mScrollListener != null) {
- mScrollListener.onScrollStateChanged(state);
- }
- }
- }
-
- void completeChallengeScroll() {
- setChallengeShowing(mChallengeShowingTargetState);
- mChallengeOffset = mChallengeShowing ? 1.f : 0.f;
- setScrollState(SCROLL_STATE_IDLE);
- mChallengeInteractiveInternal = true;
- mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
- }
-
- void setScrimView(View scrim) {
- if (mScrimView != null) {
- mScrimView.setOnClickListener(null);
- }
- mScrimView = scrim;
- mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
- mScrimView.setFocusable(true);
- mScrimView.setOnClickListener(mScrimClickListener);
- }
-
- /**
- * Animate the bottom edge of the challenge view to the given position.
- *
- * @param y desired final position for the bottom edge of the challenge view in px
- * @param velocity velocity in
- */
- void animateChallengeTo(int y, int velocity) {
- if (mChallengeView == null) {
- // Nothing to do.
- return;
- }
-
- cancelTransitionsInProgress();
-
- mChallengeInteractiveInternal = false;
- mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
- final int sy = mChallengeView.getBottom();
- final int dy = y - sy;
- if (dy == 0) {
- completeChallengeScroll();
- return;
- }
-
- setScrollState(SCROLL_STATE_SETTLING);
-
- final int childHeight = mChallengeView.getHeight();
- final int halfHeight = childHeight / 2;
- final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight);
- final float distance = halfHeight + halfHeight *
- distanceInfluenceForSnapDuration(distanceRatio);
-
- int duration = 0;
- velocity = Math.abs(velocity);
- if (velocity > 0) {
- duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
- } else {
- final float childDelta = (float) Math.abs(dy) / childHeight;
- duration = (int) ((childDelta + 1) * 100);
- }
- duration = Math.min(duration, MAX_SETTLE_DURATION);
-
- mScroller.startScroll(0, sy, 0, dy, duration);
- postInvalidateOnAnimation();
- }
-
- private void setChallengeShowing(boolean showChallenge) {
- if (mChallengeShowing == showChallenge) {
- return;
- }
- mChallengeShowing = showChallenge;
-
- if (mExpandChallengeView == null || mChallengeView == null) {
- // These might not be here yet if we haven't been through layout.
- // If we haven't, the first layout pass will set everything up correctly
- // based on mChallengeShowing as set above.
- return;
- }
-
- if (mChallengeShowing) {
- mExpandChallengeView.setVisibility(View.INVISIBLE);
- mChallengeView.setVisibility(View.VISIBLE);
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- mChallengeView.requestAccessibilityFocus();
- mChallengeView.announceForAccessibility(mContext.getString(
- R.string.keyguard_accessibility_unlock_area_expanded));
- }
- } else {
- mExpandChallengeView.setVisibility(View.VISIBLE);
- mChallengeView.setVisibility(View.INVISIBLE);
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- mExpandChallengeView.requestAccessibilityFocus();
- mChallengeView.announceForAccessibility(mContext.getString(
- R.string.keyguard_accessibility_unlock_area_collapsed));
- }
- }
- }
-
- /**
- * @return true if the challenge is at all visible.
- */
- public boolean isChallengeShowing() {
- return mChallengeShowing;
- }
-
- @Override
- public boolean isChallengeOverlapping() {
- return mChallengeShowing;
- }
-
- @Override
- public boolean isBouncing() {
- return mIsBouncing;
- }
-
- @Override
- public int getBouncerAnimationDuration() {
- return HANDLE_ANIMATE_DURATION;
- }
-
- @Override
- public void showBouncer() {
- if (mIsBouncing) return;
- mWasChallengeShowing = mChallengeShowing;
- mIsBouncing = true;
- showChallenge(true);
- if (mScrimView != null) {
- Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f);
- anim.setDuration(HANDLE_ANIMATE_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mScrimView.setVisibility(VISIBLE);
- }
- });
- anim.start();
- }
- if (mChallengeView != null) {
- mChallengeView.showBouncer(HANDLE_ANIMATE_DURATION);
- }
-
- if (mBouncerListener != null) {
- mBouncerListener.onBouncerStateChanged(true);
- }
- }
-
- @Override
- public void hideBouncer() {
- if (!mIsBouncing) return;
- if (!mWasChallengeShowing) showChallenge(false);
- mIsBouncing = false;
-
- if (mScrimView != null) {
- Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f);
- anim.setDuration(HANDLE_ANIMATE_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mScrimView.setVisibility(GONE);
- }
- });
- anim.start();
- }
- if (mChallengeView != null) {
- mChallengeView.hideBouncer(HANDLE_ANIMATE_DURATION);
- }
- if (mBouncerListener != null) {
- mBouncerListener.onBouncerStateChanged(false);
- }
- }
-
- private int getChallengeMargin(boolean expanded) {
- return expanded && mHasGlowpad ? 0 : mDragHandleEdgeSlop;
- }
-
- private float getChallengeAlpha() {
- float x = mChallengeOffset - 1;
- return x * x * x + 1.f;
- }
-
- @Override
- public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
- // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
- // If there are one or more pointers in the challenge view before we take over
- // touch events, onInterceptTouchEvent will set mBlockDrag.
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
-
- final int action = ev.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mGestureStartX = ev.getX();
- mGestureStartY = ev.getY();
- mBlockDrag = false;
- break;
-
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- resetTouch();
- break;
-
- case MotionEvent.ACTION_MOVE:
- final int count = ev.getPointerCount();
- for (int i = 0; i < count; i++) {
- final float x = ev.getX(i);
- final float y = ev.getY(i);
- if (!mIsBouncing && mActivePointerId == INVALID_POINTER
- && (crossedDragHandle(x, y, mGestureStartY)
- || (isInChallengeView(x, y) &&
- mScrollState == SCROLL_STATE_SETTLING))) {
- mActivePointerId = ev.getPointerId(i);
- mGestureStartX = x;
- mGestureStartY = y;
- mGestureStartChallengeBottom = getChallengeBottom();
- mDragging = true;
- mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
- } else if (mChallengeShowing && isInChallengeView(x, y)) {
- mBlockDrag = true;
- }
- }
- break;
- }
-
- if (mBlockDrag || isChallengeInteractionBlocked()) {
- mActivePointerId = INVALID_POINTER;
- mDragging = false;
- }
-
- return mDragging;
- }
-
- private boolean isChallengeInteractionBlocked() {
- return !mChallengeInteractiveExternal || !mChallengeInteractiveInternal;
- }
-
- private void resetTouch() {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- mActivePointerId = INVALID_POINTER;
- mDragging = mBlockDrag = false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
-
- final int action = ev.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mBlockDrag = false;
- mGestureStartX = ev.getX();
- mGestureStartY = ev.getY();
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (mDragging && !isChallengeInteractionBlocked()) {
- showChallenge(0);
- }
- resetTouch();
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
- break;
- }
- case MotionEvent.ACTION_UP:
- if (mDragging && !isChallengeInteractionBlocked()) {
- mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
- showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
- }
- resetTouch();
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (!mDragging && !mBlockDrag && !mIsBouncing) {
- final int count = ev.getPointerCount();
- for (int i = 0; i < count; i++) {
- final float x = ev.getX(i);
- final float y = ev.getY(i);
-
- if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
- (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
- && mActivePointerId == INVALID_POINTER
- && !isChallengeInteractionBlocked()) {
- mGestureStartX = x;
- mGestureStartY = y;
- mActivePointerId = ev.getPointerId(i);
- mGestureStartChallengeBottom = getChallengeBottom();
- mDragging = true;
- mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
- break;
- }
- }
- }
- // Not an else; this can be set above.
- if (mDragging) {
- // No-op if already in this state, but set it here in case we arrived
- // at this point from either intercept or the above.
- setScrollState(SCROLL_STATE_DRAGGING);
-
- final int index = ev.findPointerIndex(mActivePointerId);
- if (index < 0) {
- // Oops, bogus state. We lost some touch events somewhere.
- // Just drop it with no velocity and let things settle.
- resetTouch();
- showChallenge(0);
- return true;
- }
- final float y = ev.getY(index);
- final float pos = Math.min(y - mGestureStartY,
- getLayoutBottom() - mChallengeBottomBound);
-
- moveChallengeTo(mGestureStartChallengeBottom + (int) pos);
- }
- break;
- }
- return true;
- }
-
- /**
- * The lifecycle of touch events is subtle and it's very easy to do something
- * that will cause bugs that will be nasty to track when overriding this method.
- * Normally one should always override onInterceptTouchEvent instead.
- *
- * To put it another way, don't try this at home.
- */
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- final int action = ev.getActionMasked();
- boolean handled = false;
- if (action == MotionEvent.ACTION_DOWN) {
- // Defensive programming: if we didn't get the UP or CANCEL, reset anyway.
- mEdgeCaptured = false;
- }
- if (mWidgetsView != null && !mIsBouncing && (mEdgeCaptured || isEdgeSwipeBeginEvent(ev))) {
- // Normally we would need to do a lot of extra stuff here.
- // We can only get away with this because we haven't padded in
- // the widget pager or otherwise transformed it during layout.
- // We also don't support things like splitting MotionEvents.
-
- // We set handled to captured even if dispatch is returning false here so that
- // we don't send a different view a busted or incomplete event stream.
- handled = mEdgeCaptured |= mWidgetsView.dispatchTouchEvent(ev);
- }
-
- if (!handled && !mEdgeCaptured) {
- handled = super.dispatchTouchEvent(ev);
- }
-
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- mEdgeCaptured = false;
- }
-
- return handled;
- }
-
- private boolean isEdgeSwipeBeginEvent(MotionEvent ev) {
- if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
- return false;
- }
-
- final float x = ev.getX();
- return x < mDragHandleEdgeSlop || x >= getWidth() - mDragHandleEdgeSlop;
- }
-
- /**
- * We only want to add additional vertical space to the drag handle when the panel is fully
- * closed.
- */
- private int getDragHandleSizeAbove() {
- return isChallengeShowing() ? mDragHandleOpenAbove : mDragHandleClosedAbove;
- }
- private int getDragHandleSizeBelow() {
- return isChallengeShowing() ? mDragHandleOpenBelow : mDragHandleClosedBelow;
- }
-
- private boolean isInChallengeView(float x, float y) {
- return isPointInView(x, y, mChallengeView);
- }
-
- private boolean isInDragHandle(float x, float y) {
- return isPointInView(x, y, mExpandChallengeView);
- }
-
- private boolean isPointInView(float x, float y, View view) {
- if (view == null) {
- return false;
- }
- return x >= view.getLeft() && y >= view.getTop()
- && x < view.getRight() && y < view.getBottom();
- }
-
- private boolean crossedDragHandle(float x, float y, float initialY) {
-
- final int challengeTop = mChallengeView.getTop();
- final boolean horizOk = x >= 0 && x < getWidth();
-
- final boolean vertOk;
- if (mChallengeShowing) {
- vertOk = initialY < (challengeTop - getDragHandleSizeAbove()) &&
- y > challengeTop + getDragHandleSizeBelow();
- } else {
- vertOk = initialY > challengeTop + getDragHandleSizeBelow() &&
- y < challengeTop - getDragHandleSizeAbove();
- }
- return horizOk && vertOk;
- }
-
- private int makeChildMeasureSpec(int maxSize, int childDimen) {
- final int mode;
- final int size;
- switch (childDimen) {
- case LayoutParams.WRAP_CONTENT:
- mode = MeasureSpec.AT_MOST;
- size = maxSize;
- break;
- case LayoutParams.MATCH_PARENT:
- mode = MeasureSpec.EXACTLY;
- size = maxSize;
- break;
- default:
- mode = MeasureSpec.EXACTLY;
- size = Math.min(maxSize, childDimen);
- break;
- }
- return MeasureSpec.makeMeasureSpec(size, mode);
- }
-
- @Override
- protected void onMeasure(int widthSpec, int heightSpec) {
- if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
- MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
- throw new IllegalArgumentException(
- "SlidingChallengeLayout must be measured with an exact size");
- }
-
- final int width = MeasureSpec.getSize(widthSpec);
- final int height = MeasureSpec.getSize(heightSpec);
- setMeasuredDimension(width, height);
-
- // Find one and only one challenge view.
- final View oldChallengeView = mChallengeView;
- final View oldExpandChallengeView = mChallengeView;
- mChallengeView = null;
- mExpandChallengeView = null;
- final int count = getChildCount();
-
- // First iteration through the children finds special children and sets any associated
- // state.
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
- if (mChallengeView != null) {
- throw new IllegalStateException(
- "There may only be one child with layout_isChallenge=\"true\"");
- }
- if (!(child instanceof KeyguardSecurityContainer)) {
- throw new IllegalArgumentException(
- "Challenge must be a KeyguardSecurityContainer");
- }
- mChallengeView = (KeyguardSecurityContainer) child;
- if (mChallengeView != oldChallengeView) {
- mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
- }
- // We're going to play silly games with the frame's background drawable later.
- if (!mHasLayout) {
- // Set up the margin correctly based on our content for the first run.
- mHasGlowpad = child.findViewById(R.id.keyguard_selector_view) != null;
- lp.leftMargin = lp.rightMargin = getChallengeMargin(true);
- }
- } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) {
- if (mExpandChallengeView != null) {
- throw new IllegalStateException(
- "There may only be one child with layout_childType"
- + "=\"expandChallengeHandle\"");
- }
- mExpandChallengeView = child;
- if (mExpandChallengeView != oldExpandChallengeView) {
- mExpandChallengeView.setVisibility(mChallengeShowing ? INVISIBLE : VISIBLE);
- mExpandChallengeView.setOnClickListener(mExpandChallengeClickListener);
- }
- } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
- setScrimView(child);
- } else if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) {
- mWidgetsView = child;
- }
- }
-
- // We want to measure the challenge view first, since the KeyguardWidgetPager
- // needs to do things its measure pass that are dependent on the challenge view
- // having been measured.
- if (mChallengeView != null && mChallengeView.getVisibility() != View.GONE) {
- // This one's a little funny. If the IME is present - reported in the form
- // of insets on the root view - we only give the challenge the space it would
- // have had if the IME wasn't there in order to keep the rest of the layout stable.
- // We base this on the layout_maxHeight on the challenge view. If it comes out
- // negative or zero, either we didn't have a maxHeight or we're totally out of space,
- // so give up and measure as if this rule weren't there.
- int challengeHeightSpec = heightSpec;
- final View root = getRootView();
- if (root != null) {
- final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
- final int specSize = MeasureSpec.getSize(heightSpec);
- final int windowHeight = mDisplayMetrics.heightPixels - root.getPaddingTop();
- final int diff = windowHeight - specSize;
- final int maxChallengeHeight = lp.maxHeight - diff;
- if (maxChallengeHeight > 0) {
- challengeHeightSpec = makeChildMeasureSpec(maxChallengeHeight, lp.height);
- }
- }
- measureChildWithMargins(mChallengeView, widthSpec, 0, challengeHeightSpec, 0);
- }
-
- // Measure the rest of the children
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
- // Don't measure the challenge view twice!
- if (child == mChallengeView) continue;
-
- // Measure children. Widget frame measures special, so that we can ignore
- // insets for the IME.
- int parentWidthSpec = widthSpec, parentHeightSpec = heightSpec;
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) {
- final View root = getRootView();
- if (root != null) {
- // This calculation is super dodgy and relies on several assumptions.
- // Specifically that the root of the window will be padded in for insets
- // and that the window is LAYOUT_IN_SCREEN.
- final int windowWidth = mDisplayMetrics.widthPixels;
- final int windowHeight = mDisplayMetrics.heightPixels - root.getPaddingTop();
- parentWidthSpec = MeasureSpec.makeMeasureSpec(
- windowWidth, MeasureSpec.EXACTLY);
- parentHeightSpec = MeasureSpec.makeMeasureSpec(
- windowHeight, MeasureSpec.EXACTLY);
- }
- }
- measureChildWithMargins(child, parentWidthSpec, 0, parentHeightSpec, 0);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- final int paddingLeft = getPaddingLeft();
- final int paddingTop = getPaddingTop();
- final int paddingRight = getPaddingRight();
- final int paddingBottom = getPaddingBottom();
- final int width = r - l;
- final int height = b - t;
-
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
-
- if (child.getVisibility() == GONE) continue;
-
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
- // Challenge views pin to the bottom, offset by a portion of their height,
- // and center horizontally.
- final int center = (paddingLeft + width - paddingRight) / 2;
- final int childWidth = child.getMeasuredWidth();
- final int childHeight = child.getMeasuredHeight();
- final int left = center - childWidth / 2;
- final int layoutBottom = height - paddingBottom - lp.bottomMargin;
- // We use the top of the challenge view to position the handle, so
- // we never want less than the handle size showing at the bottom.
- final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound)
- * (1 - mChallengeOffset));
- child.setAlpha(getChallengeAlpha());
- child.layout(left, bottom - childHeight, left + childWidth, bottom);
- } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) {
- final int center = (paddingLeft + width - paddingRight) / 2;
- final int left = center - child.getMeasuredWidth() / 2;
- final int right = left + child.getMeasuredWidth();
- final int bottom = height - paddingBottom - lp.bottomMargin;
- final int top = bottom - child.getMeasuredHeight();
- child.layout(left, top, right, bottom);
- } else {
- // Non-challenge views lay out from the upper left, layered.
- child.layout(paddingLeft + lp.leftMargin,
- paddingTop + lp.topMargin,
- paddingLeft + child.getMeasuredWidth(),
- paddingTop + child.getMeasuredHeight());
- }
- }
-
- if (!mHasLayout) {
- mHasLayout = true;
- }
- }
-
- @Override
- public void draw(Canvas c) {
- super.draw(c);
- if (DEBUG) {
- final Paint debugPaint = new Paint();
- debugPaint.setColor(0x40FF00CC);
- // show the isInDragHandle() rect
- c.drawRect(mDragHandleEdgeSlop,
- mChallengeView.getTop() - getDragHandleSizeAbove(),
- getWidth() - mDragHandleEdgeSlop,
- mChallengeView.getTop() + getDragHandleSizeBelow(),
- debugPaint);
- }
- }
-
- public void computeScroll() {
- super.computeScroll();
-
- if (!mScroller.isFinished()) {
- if (mChallengeView == null) {
- // Can't scroll if the view is missing.
- Log.e(TAG, "Challenge view missing in computeScroll");
- mScroller.abortAnimation();
- return;
- }
-
- mScroller.computeScrollOffset();
- moveChallengeTo(mScroller.getCurrY());
-
- if (mScroller.isFinished()) {
- post(mEndScrollRunnable);
- }
- }
- }
-
- private void cancelTransitionsInProgress() {
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- completeChallengeScroll();
- }
- if (mFader != null) {
- mFader.cancel();
- }
- }
-
- public void fadeInChallenge() {
- fadeChallenge(true);
- }
-
- public void fadeOutChallenge() {
- fadeChallenge(false);
- }
-
- public void fadeChallenge(final boolean show) {
- if (mChallengeView != null) {
-
- cancelTransitionsInProgress();
- float alpha = show ? 1f : 0f;
- int duration = show ? CHALLENGE_FADE_IN_DURATION : CHALLENGE_FADE_OUT_DURATION;
- mFader = ObjectAnimator.ofFloat(mChallengeView, "alpha", alpha);
- mFader.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- onFadeStart(show);
- }
- @Override
- public void onAnimationEnd(Animator animation) {
- onFadeEnd(show);
- }
- });
- mFader.setDuration(duration);
- mFader.start();
- }
- }
-
- private int getMaxChallengeBottom() {
- if (mChallengeView == null) return 0;
- final int layoutBottom = getLayoutBottom();
- final int challengeHeight = mChallengeView.getMeasuredHeight();
-
- return (layoutBottom + challengeHeight - mChallengeBottomBound);
- }
-
- private int getMinChallengeBottom() {
- return getLayoutBottom();
- }
-
-
- private void onFadeStart(boolean show) {
- mChallengeInteractiveInternal = false;
- mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
-
- if (show) {
- moveChallengeTo(getMinChallengeBottom());
- }
-
- setScrollState(SCROLL_STATE_FADING);
- }
-
- private void onFadeEnd(boolean show) {
- mChallengeInteractiveInternal = true;
- setChallengeShowing(show);
-
- if (!show) {
- moveChallengeTo(getMaxChallengeBottom());
- }
-
- mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
- mFader = null;
- setScrollState(SCROLL_STATE_IDLE);
- }
-
- public int getMaxChallengeTop() {
- if (mChallengeView == null) return 0;
-
- final int layoutBottom = getLayoutBottom();
- final int challengeHeight = mChallengeView.getMeasuredHeight();
- return layoutBottom - challengeHeight;
- }
-
- /**
- * Move the bottom edge of mChallengeView to a new position and notify the listener
- * if it represents a change in position. Changes made through this method will
- * be stable across layout passes. If this method is called before first layout of
- * this SlidingChallengeLayout it will have no effect.
- *
- * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system.
- * @return true if the challenge view was moved
- */
- private boolean moveChallengeTo(int bottom) {
- if (mChallengeView == null || !mHasLayout) {
- return false;
- }
-
- final int layoutBottom = getLayoutBottom();
- final int challengeHeight = mChallengeView.getHeight();
-
- bottom = Math.max(getMinChallengeBottom(),
- Math.min(bottom, getMaxChallengeBottom()));
-
- float offset = 1.f - (float) (bottom - layoutBottom) /
- (challengeHeight - mChallengeBottomBound);
- mChallengeOffset = offset;
- if (offset > 0 && !mChallengeShowing) {
- setChallengeShowing(true);
- }
-
- mChallengeView.layout(mChallengeView.getLeft(),
- bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
-
- mChallengeView.setAlpha(getChallengeAlpha());
- if (mScrollListener != null) {
- mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop());
- }
- postInvalidateOnAnimation();
- return true;
- }
-
- /**
- * The bottom edge of this SlidingChallengeLayout's coordinate system; will coincide with
- * the bottom edge of mChallengeView when the challenge is fully opened.
- */
- private int getLayoutBottom() {
- final int bottomMargin = (mChallengeView == null)
- ? 0
- : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
- final int layoutBottom = getMeasuredHeight() - getPaddingBottom() - bottomMargin;
- return layoutBottom;
- }
-
- /**
- * The bottom edge of mChallengeView; essentially, where the sliding challenge 'is'.
- */
- private int getChallengeBottom() {
- if (mChallengeView == null) return 0;
-
- return mChallengeView.getBottom();
- }
-
- /**
- * Show or hide the challenge view, animating it if necessary.
- * @param show true to show, false to hide
- */
- public void showChallenge(boolean show) {
- showChallenge(show, 0);
- if (!show) {
- // Block any drags in progress so that callers can use this to disable dragging
- // for other touch interactions.
- mBlockDrag = true;
- }
- }
-
- private void showChallenge(int velocity) {
- boolean show = false;
- if (Math.abs(velocity) > mMinVelocity) {
- show = velocity < 0;
- } else {
- show = mChallengeOffset >= 0.5f;
- }
- showChallenge(show, velocity);
- }
-
- private void showChallenge(boolean show, int velocity) {
- if (mChallengeView == null) {
- setChallengeShowing(false);
- return;
- }
-
- if (mHasLayout) {
- mChallengeShowingTargetState = show;
- final int layoutBottom = getLayoutBottom();
- animateChallengeTo(show ? layoutBottom :
- layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity);
- }
- }
-
- @Override
- public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
-
- @Override
- protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
- p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
- new LayoutParams(p);
- }
-
- @Override
- protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams();
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams;
- }
-
- public static class LayoutParams extends MarginLayoutParams {
- public int childType = CHILD_TYPE_NONE;
- public static final int CHILD_TYPE_NONE = 0;
- public static final int CHILD_TYPE_CHALLENGE = 2;
- public static final int CHILD_TYPE_SCRIM = 4;
- public static final int CHILD_TYPE_WIDGETS = 5;
- public static final int CHILD_TYPE_EXPAND_CHALLENGE_HANDLE = 6;
-
- public int maxHeight;
-
- public LayoutParams() {
- this(MATCH_PARENT, WRAP_CONTENT);
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(android.view.ViewGroup.LayoutParams source) {
- super(source);
- }
-
- public LayoutParams(MarginLayoutParams source) {
- super(source);
- }
-
- public LayoutParams(LayoutParams source) {
- super(source);
-
- childType = source.childType;
- }
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
-
- final TypedArray a = c.obtainStyledAttributes(attrs,
- R.styleable.SlidingChallengeLayout_Layout);
- childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType,
- CHILD_TYPE_NONE);
- maxHeight = a.getDimensionPixelSize(
- R.styleable.SlidingChallengeLayout_Layout_layout_maxHeight, 0);
- a.recycle();
- }
- }
-}
diff --git a/preloaded-classes b/preloaded-classes
index 2aa610a..45d27ee 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1220,7 +1220,6 @@
android.webkit.HTML5VideoViewProxy
android.webkit.JWebCoreJavaBridge
android.webkit.JavascriptInterface
-android.webkit.JniUtil
android.webkit.L10nUtils
android.webkit.MockGeolocation
android.webkit.OverScrollGlow
@@ -1269,7 +1268,6 @@
android.webkit.WebViewClassic$TrustStorageListener
android.webkit.WebViewClassic$ViewSizeData
android.webkit.WebViewClient
-android.webkit.WebViewCore
android.webkit.WebViewCore$AutoFillData
android.webkit.WebViewCore$DrawData
android.webkit.WebViewCore$EventHub
@@ -1283,6 +1281,7 @@
android.webkit.WebViewDatabaseClassic
android.webkit.WebViewDatabaseClassic$1
android.webkit.WebViewFactory
+android.webkit.WebViewFactory$Preloader
android.webkit.WebViewFactoryProvider
android.webkit.WebViewFactoryProvider$Statics
android.webkit.WebViewInputDispatcher
diff --git a/services/input/Android.mk b/services/input/Android.mk
index 5d913f3..6e944ef 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -36,12 +36,13 @@
libhardware_legacy \
libskia \
libgui \
- libui
+ libui \
+ libinput
LOCAL_C_INCLUDES := \
external/skia/include/core
-LOCAL_MODULE:= libinput
+LOCAL_MODULE:= libinputservice
LOCAL_MODULE_TAGS := optional
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index f4e1cec..fab091f 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -36,9 +36,9 @@
#include <errno.h>
#include <assert.h>
-#include <androidfw/KeyLayoutMap.h>
-#include <androidfw/KeyCharacterMap.h>
-#include <androidfw/VirtualKeyMap.h>
+#include <input/KeyLayoutMap.h>
+#include <input/KeyCharacterMap.h>
+#include <input/VirtualKeyMap.h>
#include <string.h>
#include <stdint.h>
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index c93fc7a..daa1bea 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -18,12 +18,12 @@
#ifndef _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
-#include <androidfw/Input.h>
-#include <androidfw/InputDevice.h>
-#include <androidfw/Keyboard.h>
-#include <androidfw/KeyLayoutMap.h>
-#include <androidfw/KeyCharacterMap.h>
-#include <androidfw/VirtualKeyMap.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/Keyboard.h>
+#include <input/KeyLayoutMap.h>
+#include <input/KeyCharacterMap.h>
+#include <input/VirtualKeyMap.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Log.h>
diff --git a/services/input/InputApplication.h b/services/input/InputApplication.h
index c04a935..1f5504c 100644
--- a/services/input/InputApplication.h
+++ b/services/input/InputApplication.h
@@ -17,7 +17,7 @@
#ifndef _UI_INPUT_APPLICATION_H
#define _UI_INPUT_APPLICATION_H
-#include <androidfw/Input.h>
+#include <input/Input.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 430721e..63ea781 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -17,8 +17,8 @@
#ifndef _UI_INPUT_DISPATCHER_H
#define _UI_INPUT_DISPATCHER_H
-#include <androidfw/Input.h>
-#include <androidfw/InputTransport.h>
+#include <input/Input.h>
+#include <input/InputTransport.h>
#include <utils/KeyedVector.h>
#include <utils/Vector.h>
#include <utils/threads.h>
diff --git a/services/input/InputListener.h b/services/input/InputListener.h
index cd7c25a..78ae10f 100644
--- a/services/input/InputListener.h
+++ b/services/input/InputListener.h
@@ -17,7 +17,7 @@
#ifndef _UI_INPUT_LISTENER_H
#define _UI_INPUT_LISTENER_H
-#include <androidfw/Input.h>
+#include <input/Input.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
diff --git a/services/input/InputManager.h b/services/input/InputManager.h
index 29584c9..a213b2d 100644
--- a/services/input/InputManager.h
+++ b/services/input/InputManager.h
@@ -25,8 +25,8 @@
#include "InputReader.h"
#include "InputDispatcher.h"
-#include <androidfw/Input.h>
-#include <androidfw/InputTransport.h>
+#include <input/Input.h>
+#include <input/InputTransport.h>
#include <utils/Errors.h>
#include <utils/Vector.h>
#include <utils/Timers.h>
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index e229755..5b64d8a 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -42,8 +42,8 @@
#include "InputReader.h"
#include <cutils/log.h>
-#include <androidfw/Keyboard.h>
-#include <androidfw/VirtualKeyMap.h>
+#include <input/Keyboard.h>
+#include <input/VirtualKeyMap.h>
#include <stddef.h>
#include <stdlib.h>
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 0189ba7..7e303e4 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -21,9 +21,9 @@
#include "PointerController.h"
#include "InputListener.h"
-#include <androidfw/Input.h>
-#include <androidfw/VelocityControl.h>
-#include <androidfw/VelocityTracker.h>
+#include <input/Input.h>
+#include <input/VelocityControl.h>
+#include <input/VelocityTracker.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
#include <utils/Timers.h>
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index 7bd3af7..136870a 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -17,8 +17,8 @@
#ifndef _UI_INPUT_WINDOW_H
#define _UI_INPUT_WINDOW_H
-#include <androidfw/Input.h>
-#include <androidfw/InputTransport.h>
+#include <input/Input.h>
+#include <input/InputTransport.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/String8.h>
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index fd68b61..790c0bb 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -20,7 +20,7 @@
#include "SpriteController.h"
#include <ui/DisplayInfo.h>
-#include <androidfw/Input.h>
+#include <input/Input.h>
#include <utils/BitSet.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk
index 211e64b..9278f41 100644
--- a/services/input/tests/Android.mk
+++ b/services/input/tests/Android.mk
@@ -17,7 +17,8 @@
libui \
libskia \
libstlport \
- libinput
+ libinput \
+ libinputservice
static_libraries := \
libgtest \
@@ -40,7 +41,7 @@
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
$(eval LOCAL_MODULE_TAGS := $(module_tags)) \
- $(eval include $(BUILD_EXECUTABLE)) \
+ $(eval include $(BUILD_NATIVE_TEST)) \
)
# Build the manual test programs.
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index fa758a8..0be9ca5 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -53,7 +53,7 @@
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
-import java.util.Iterator;
+import java.util.LinkedList;
import java.util.Map;
import java.util.TimeZone;
@@ -69,6 +69,7 @@
private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP;
private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
private static final int TIME_CHANGED_MASK = 1 << 16;
+ private static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK;
// Alignment quantum for inexact repeating alarms
private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
@@ -82,6 +83,8 @@
private static final Intent mBackgroundIntent
= new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
+ private static final boolean WAKEUP_STATS = true;
+
private final Context mContext;
private final LocalLog mLog = new LocalLog(TAG);
@@ -106,6 +109,21 @@
private final PendingIntent mTimeTickSender;
private final PendingIntent mDateChangeSender;
+ class WakeupEvent {
+ public long when;
+ public int uid;
+ public String action;
+
+ public WakeupEvent(long theTime, int theUid, String theAction) {
+ when = theTime;
+ uid = theUid;
+ action = theAction;
+ }
+ }
+
+ private final LinkedList<WakeupEvent> mRecentWakeups = new LinkedList<WakeupEvent>();
+ private final long RECENT_WAKEUP_PERIOD = 1000L * 60 * 60 * 24; // one day
+
private static final class InFlight extends Intent {
final PendingIntent mPendingIntent;
final Pair<String, ComponentName> mTarget;
@@ -351,12 +369,11 @@
}
// iterator over the list removing any it where the intent match
- Iterator<Alarm> it = alarmList.iterator();
-
- while (it.hasNext()) {
- Alarm alarm = it.next();
+ for (int i=0; i<alarmList.size(); i++) {
+ Alarm alarm = alarmList.get(i);
if (alarm.operation.equals(operation)) {
- it.remove();
+ alarmList.remove(i);
+ i--;
}
}
}
@@ -375,12 +392,11 @@
}
// iterator over the list removing any it where the intent match
- Iterator<Alarm> it = alarmList.iterator();
-
- while (it.hasNext()) {
- Alarm alarm = it.next();
+ for (int i=0; i<alarmList.size(); i++) {
+ Alarm alarm = alarmList.get(i);
if (alarm.operation.getTargetPackage().equals(packageName)) {
- it.remove();
+ alarmList.remove(i);
+ i--;
}
}
}
@@ -398,12 +414,11 @@
}
// iterator over the list removing any it where the intent match
- Iterator<Alarm> it = alarmList.iterator();
-
- while (it.hasNext()) {
- Alarm alarm = it.next();
+ for (int i=0; i<alarmList.size(); i++) {
+ Alarm alarm = alarmList.get(i);
if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) {
- it.remove();
+ alarmList.remove(i);
+ i--;
}
}
}
@@ -643,6 +658,27 @@
pw.println();
}
}
+
+ if (WAKEUP_STATS) {
+ pw.println();
+ pw.println(" Recent Wakeup History:");
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss.SSS");
+ long last = -1;
+ for (WakeupEvent event : mRecentWakeups) {
+ pw.print(" "); pw.print(sdf.format(new Date(event.when)));
+ pw.print('|');
+ if (last < 0) {
+ pw.print('0');
+ } else {
+ pw.print(event.when - last);
+ }
+ last = event.when;
+ pw.print('|'); pw.print(event.uid);
+ pw.print('|'); pw.print(event.action);
+ pw.println();
+ }
+ pw.println();
+ }
}
}
@@ -666,12 +702,10 @@
ArrayList<Alarm> triggerList,
long now)
{
- Iterator<Alarm> it = alarmList.iterator();
- ArrayList<Alarm> repeats = new ArrayList<Alarm>();
-
- while (it.hasNext())
- {
- Alarm alarm = it.next();
+ ArrayList<Alarm> repeats = null;
+
+ for (int i=0; i<alarmList.size(); i++) {
+ Alarm alarm = alarmList.get(i);
if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
@@ -702,20 +736,25 @@
triggerList.add(alarm);
// remove the alarm from the list
- it.remove();
-
+ alarmList.remove(i);
+ i--;
+
// if it repeats queue it up to be read-added to the list
if (alarm.repeatInterval > 0) {
+ if (repeats == null) {
+ repeats = new ArrayList<Alarm>();
+ }
repeats.add(alarm);
}
}
// reset any repeating alarms.
- it = repeats.iterator();
- while (it.hasNext()) {
- Alarm alarm = it.next();
- alarm.when += alarm.count * alarm.repeatInterval;
- addAlarmLocked(alarm);
+ if (repeats != null) {
+ for (int i=0; i<repeats.size(); i++) {
+ Alarm alarm = repeats.get(i);
+ alarm.when += alarm.count * alarm.repeatInterval;
+ addAlarmLocked(alarm);
+ }
}
if (alarmList.size() > 0) {
@@ -775,7 +814,20 @@
pw.print(prefix); pw.print("operation="); pw.println(operation);
}
}
-
+
+ void recordWakeupAlarms(ArrayList<Alarm> alarms, long now, long skewToRTC) {
+ for (Alarm a : alarms) {
+ if (a.when > now) {
+ break;
+ }
+
+ WakeupEvent e = new WakeupEvent(now + skewToRTC,
+ a.operation.getCreatorUid(),
+ a.operation.getIntent().getAction());
+ mRecentWakeups.add(e);
+ }
+ }
+
private class AlarmThread extends Thread
{
public AlarmThread()
@@ -785,12 +837,14 @@
public void run()
{
+ ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
+
while (true)
{
int result = waitForAlarm(mDescriptor);
-
- ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
-
+
+ triggerList.clear();
+
if ((result & TIME_CHANGED_MASK) != 0) {
remove(mTimeTickSender);
mClockReceiver.scheduleTimeTickEvent();
@@ -807,6 +861,27 @@
TAG, "Checking for alarms... rtc=" + nowRTC
+ ", elapsed=" + nowELAPSED);
+ if (WAKEUP_STATS) {
+ if ((result & IS_WAKEUP_MASK) != 0) {
+ long newEarliest = nowRTC - RECENT_WAKEUP_PERIOD;
+ int n = 0;
+ for (WakeupEvent event : mRecentWakeups) {
+ if (event.when > newEarliest) break;
+ n++; // number of now-stale entries at the list head
+ }
+ for (int i = 0; i < n; i++) {
+ mRecentWakeups.remove();
+ }
+
+ recordWakeupAlarms(mRtcWakeupAlarms,
+ nowRTC,
+ 0);
+ recordWakeupAlarms(mElapsedRealtimeWakeupAlarms,
+ nowELAPSED,
+ nowRTC - nowELAPSED);
+ }
+ }
+
if ((result & RTC_WAKEUP_MASK) != 0)
triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
@@ -820,9 +895,8 @@
triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
// now trigger the alarms
- Iterator<Alarm> it = triggerList.iterator();
- while (it.hasNext()) {
- Alarm alarm = it.next();
+ for (int i=0; i<triggerList.size(); i++) {
+ Alarm alarm = triggerList.get(i);
try {
if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
alarm.operation.send(mContext, 0,
@@ -913,10 +987,8 @@
}
// now trigger the alarms without the lock held
- Iterator<Alarm> it = triggerList.iterator();
- while (it.hasNext())
- {
- Alarm alarm = it.next();
+ for (int i=0; i<triggerList.size(); i++) {
+ Alarm alarm = triggerList.get(i);
try {
alarm.operation.send();
} catch (PendingIntent.CanceledException e) {
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index a402642..7a107e7 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -551,6 +551,9 @@
pkgUid = mContext.getPackageManager().getPackageUid(packageName,
UserHandle.getUserId(uid));
} catch (NameNotFoundException e) {
+ if ("media".equals(packageName)) {
+ pkgUid = Process.MEDIA_UID;
+ }
}
if (pkgUid != uid) {
// Oops! The package name is not valid for the uid they are calling
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index d5715a5..5b76f39 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -27,7 +27,6 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -37,6 +36,7 @@
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
@@ -63,9 +63,7 @@
AppWidgetService(Context context) {
mContext = context;
- HandlerThread handlerThread = new HandlerThread("AppWidgetService -- Save state");
- handlerThread.start();
- mSaveStateHandler = new Handler(handlerThread.getLooper());
+ mSaveStateHandler = BackgroundThread.getHandler();
mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0, mSaveStateHandler);
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index fb2828b..69ae846 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -86,9 +86,12 @@
class AppWidgetServiceImpl {
+ private static final String KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
+ private static final int KEYGUARD_HOST_ID = 0x4b455947;
private static final String TAG = "AppWidgetServiceImpl";
private static final String SETTINGS_FILENAME = "appwidgets.xml";
private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
+ private static final int CURRENT_VERSION = 1; // Bump if the stored widgets need to be upgraded.
private static boolean DBG = false;
@@ -1654,7 +1657,7 @@
out.setOutput(stream, "utf-8");
out.startDocument(null, true);
out.startTag(null, "gs");
-
+ out.attribute(null, "version", String.valueOf(CURRENT_VERSION));
int providerIndex = 0;
N = mInstalledProviders.size();
for (int i = 0; i < N; i++) {
@@ -1723,6 +1726,7 @@
@SuppressWarnings("unused")
void readStateFromFileLocked(FileInputStream stream) {
boolean success = false;
+ int version = 0;
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(stream, null);
@@ -1734,7 +1738,14 @@
type = parser.next();
if (type == XmlPullParser.START_TAG) {
String tag = parser.getName();
- if ("p".equals(tag)) {
+ if ("gs".equals(tag)) {
+ String attributeValue = parser.getAttributeValue(null, "version");
+ try {
+ version = Integer.parseInt(attributeValue);
+ } catch (NumberFormatException e) {
+ version = 0;
+ }
+ } else if ("p".equals(tag)) {
// TODO: do we need to check that this package has the same signature
// as before?
String pkg = parser.getAttributeValue(null, "pkg");
@@ -1873,6 +1884,8 @@
for (int i = mHosts.size() - 1; i >= 0; i--) {
pruneHostLocked(mHosts.get(i));
}
+ // upgrade the database if needed
+ performUpgrade(version);
} else {
// failed reading, clean up
Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
@@ -1886,6 +1899,31 @@
}
}
+ private void performUpgrade(int fromVersion) {
+ if (fromVersion < CURRENT_VERSION) {
+ Slog.v(TAG, "Upgrading widget database from " + fromVersion + " to " + CURRENT_VERSION
+ + " for user " + mUserId);
+ }
+
+ int version = fromVersion;
+
+ // Update 1: keyguard moved from package "android" to "com.android.keyguard"
+ if (version == 0) {
+ for (int i = 0; i < mHosts.size(); i++) {
+ Host host = mHosts.get(i);
+ if (host != null && "android".equals(host.packageName)
+ && host.hostId == KEYGUARD_HOST_ID) {
+ host.packageName = KEYGUARD_HOST_PACKAGE;
+ }
+ }
+ version = 1;
+ }
+
+ if (version != CURRENT_VERSION) {
+ throw new IllegalStateException("Failed to upgrade widget database");
+ }
+ }
+
static File getSettingsFile(int userId) {
return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
}
diff --git a/services/java/com/android/server/AssetAtlasService.java b/services/java/com/android/server/AssetAtlasService.java
new file mode 100644
index 0000000..33f082c
--- /dev/null
+++ b/services/java/com/android/server/AssetAtlasService.java
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Atlas;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.Drawable;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.view.GraphicBuffer;
+import android.view.IAssetAtlas;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This service is responsible for packing preloaded bitmaps into a single
+ * atlas texture. The resulting texture can be shared across processes to
+ * reduce overall memory usage.
+ *
+ * @hide
+ */
+public class AssetAtlasService extends IAssetAtlas.Stub {
+ /**
+ * Name of the <code>AssetAtlasService</code>.
+ */
+ public static final String ASSET_ATLAS_SERVICE = "assetatlas";
+
+ private static final String LOG_TAG = "Atlas";
+
+ // Turns debug logs on/off. Debug logs are kept to a minimum and should
+ // remain on to diagnose issues
+ private static final boolean DEBUG_ATLAS = true;
+
+ // When set to true the content of the atlas will be saved to disk
+ // in /data/system/atlas.png. The shared GraphicBuffer may be empty
+ private static final boolean DEBUG_ATLAS_TEXTURE = false;
+
+ // Minimum size in pixels to consider for the resulting texture
+ private static final int MIN_SIZE = 768;
+ // Maximum size in pixels to consider for the resulting texture
+ private static final int MAX_SIZE = 2048;
+ // Increment in number of pixels between size variants when looking
+ // for the best texture dimensions
+ private static final int STEP = 64;
+
+ // This percentage of the total number of pixels represents the minimum
+ // number of pixels we want to be able to pack in the atlas
+ private static final float PACKING_THRESHOLD = 0.8f;
+
+ // Defines the number of int fields used to represent a single entry
+ // in the atlas map. This number defines the size of the array returned
+ // by the getMap(). See the mAtlasMap field for more information
+ private static final int ATLAS_MAP_ENTRY_FIELD_COUNT = 4;
+
+ // Specifies how our GraphicBuffer will be used. To get proper swizzling
+ // the buffer will be written to using OpenGL (from JNI) so we can leave
+ // the software flag set to "never"
+ private static final int GRAPHIC_BUFFER_USAGE = GraphicBuffer.USAGE_SW_READ_NEVER |
+ GraphicBuffer.USAGE_SW_WRITE_NEVER | GraphicBuffer.USAGE_HW_TEXTURE;
+
+ // This boolean is set to true if an atlas was successfully
+ // computed and rendered
+ private final AtomicBoolean mAtlasReady = new AtomicBoolean(false);
+
+ private final Context mContext;
+
+ // Version name of the current build, used to identify changes to assets list
+ private final String mVersionName;
+
+ // Holds the atlas' data. This buffer can be mapped to
+ // OpenGL using an EGLImage
+ private GraphicBuffer mBuffer;
+
+ // Describes how bitmaps are placed in the atlas. Each bitmap is
+ // represented by several entries in the array:
+ // int0: SkBitmap*, the native bitmap object
+ // int1: x position
+ // int2: y position
+ // int3: rotated, 1 if the bitmap must be rotated, 0 otherwise
+ // NOTE: This will need to be handled differently to support 64 bit pointers
+ private int[] mAtlasMap;
+
+ /**
+ * Creates a new service. Upon creating, the service will gather the list of
+ * assets to consider for packing into the atlas and spawn a new thread to
+ * start the packing work.
+ *
+ * @param context The context giving access to preloaded resources
+ */
+ public AssetAtlasService(Context context) {
+ mContext = context;
+ mVersionName = queryVersionName(context);
+
+ ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>(300);
+ int totalPixelCount = 0;
+
+ // We only care about drawables that hold bitmaps
+ final Resources resources = context.getResources();
+ final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
+
+ final int count = drawables.size();
+ for (int i = 0; i < count; i++) {
+ final Bitmap bitmap = drawables.valueAt(i).getBitmap();
+ if (bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888) {
+ bitmaps.add(bitmap);
+ totalPixelCount += bitmap.getWidth() * bitmap.getHeight();
+ }
+ }
+
+ // Our algorithms perform better when the bitmaps are first sorted
+ // The comparator will sort the bitmap by width first, then by height
+ Collections.sort(bitmaps, new Comparator<Bitmap>() {
+ @Override
+ public int compare(Bitmap b1, Bitmap b2) {
+ if (b1.getWidth() == b2.getWidth()) {
+ return b2.getHeight() - b1.getHeight();
+ }
+ return b2.getWidth() - b1.getWidth();
+ }
+ });
+
+ // Kick off the packing work on a worker thread
+ new Thread(new Renderer(bitmaps, totalPixelCount)).start();
+ }
+
+ /**
+ * Queries the version name stored in framework's AndroidManifest.
+ * The version name can be used to identify possible changes to
+ * framework resources.
+ *
+ * @see #getBuildIdentifier(String)
+ */
+ private static String queryVersionName(Context context) {
+ try {
+ String packageName = context.getPackageName();
+ PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
+ return info.versionName;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(LOG_TAG, "Could not get package info", e);
+ }
+ return null;
+ }
+
+ /**
+ * Callback invoked by the server thread to indicate we can now run
+ * 3rd party code.
+ */
+ public void systemReady() {
+ }
+
+ /**
+ * The renderer does all the work:
+ */
+ private class Renderer implements Runnable {
+ private final ArrayList<Bitmap> mBitmaps;
+ private final int mPixelCount;
+
+ private int mNativeBitmap;
+
+ // Used for debugging only
+ private Bitmap mAtlasBitmap;
+
+ Renderer(ArrayList<Bitmap> bitmaps, int pixelCount) {
+ mBitmaps = bitmaps;
+ mPixelCount = pixelCount;
+ }
+
+ /**
+ * 1. On first boot or after every update, brute-force through all the
+ * possible atlas configurations and look for the best one (maximimize
+ * number of packed assets and minimize texture size)
+ * a. If a best configuration was computed, write it out to disk for
+ * future use
+ * 2. Read best configuration from disk
+ * 3. Compute the packing using the best configuration
+ * 4. Allocate a GraphicBuffer
+ * 5. Render assets in the buffer
+ */
+ @Override
+ public void run() {
+ Configuration config = chooseConfiguration(mBitmaps, mPixelCount, mVersionName);
+ if (DEBUG_ATLAS) Log.d(LOG_TAG, "Loaded configuration: " + config);
+
+ if (config != null) {
+ mBuffer = GraphicBuffer.create(config.width, config.height,
+ PixelFormat.RGBA_8888, GRAPHIC_BUFFER_USAGE);
+
+ if (mBuffer != null) {
+ Atlas atlas = new Atlas(config.type, config.width, config.height, config.flags);
+ if (renderAtlas(mBuffer, atlas, config.count)) {
+ mAtlasReady.set(true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Renders a list of bitmaps into the atlas. The position of each bitmap
+ * was decided by the packing algorithm and will be honored by this
+ * method. If need be this method will also rotate bitmaps.
+ *
+ * @param buffer The buffer to render the atlas entries into
+ * @param atlas The atlas to pack the bitmaps into
+ * @param packCount The number of bitmaps that will be packed in the atlas
+ *
+ * @return true if the atlas was rendered, false otherwise
+ */
+ @SuppressWarnings("MismatchedReadAndWriteOfArray")
+ private boolean renderAtlas(GraphicBuffer buffer, Atlas atlas, int packCount) {
+ // Use a Source blend mode to improve performance, the target bitmap
+ // will be zero'd out so there's no need to waste time applying blending
+ final Paint paint = new Paint();
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+
+ // We always render the atlas into a bitmap. This bitmap is then
+ // uploaded into the GraphicBuffer using OpenGL to swizzle the content
+ final Canvas canvas = acquireCanvas(buffer.getWidth(), buffer.getHeight());
+ if (canvas == null) return false;
+
+ final Atlas.Entry entry = new Atlas.Entry();
+
+ mAtlasMap = new int[packCount * ATLAS_MAP_ENTRY_FIELD_COUNT];
+ int[] atlasMap = mAtlasMap;
+ int mapIndex = 0;
+
+ boolean result = false;
+ try {
+ final long startRender = System.nanoTime();
+ final int count = mBitmaps.size();
+
+ for (int i = 0; i < count; i++) {
+ final Bitmap bitmap = mBitmaps.get(i);
+ if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) {
+ // We have more bitmaps to pack than the current configuration
+ // says, we were most likely not able to detect a change in the
+ // list of preloaded drawables, abort and delete the configuration
+ if (mapIndex >= mAtlasMap.length) {
+ deleteDataFile();
+ break;
+ }
+
+ canvas.save();
+ canvas.translate(entry.x, entry.y);
+ if (entry.rotated) {
+ canvas.translate(bitmap.getHeight(), 0.0f);
+ canvas.rotate(90.0f);
+ }
+ canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
+ canvas.restore();
+
+ atlasMap[mapIndex++] = bitmap.mNativeBitmap;
+ atlasMap[mapIndex++] = entry.x;
+ atlasMap[mapIndex++] = entry.y;
+ atlasMap[mapIndex++] = entry.rotated ? 1 : 0;
+ }
+ }
+
+ final long endRender = System.nanoTime();
+ if (mNativeBitmap != 0) {
+ result = nUploadAtlas(buffer, mNativeBitmap);
+ }
+
+ final long endUpload = System.nanoTime();
+ if (DEBUG_ATLAS) {
+ float renderDuration = (endRender - startRender) / 1000.0f / 1000.0f;
+ float uploadDuration = (endUpload - endRender) / 1000.0f / 1000.0f;
+ Log.d(LOG_TAG, String.format("Rendered atlas in %.2fms (%.2f+%.2fms)",
+ renderDuration + uploadDuration, renderDuration, uploadDuration));
+ }
+
+ } finally {
+ releaseCanvas(canvas);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a Canvas for the specified buffer. If {@link #DEBUG_ATLAS_TEXTURE}
+ * is turned on, the returned Canvas will render into a local bitmap that
+ * will then be saved out to disk for debugging purposes.
+ * @param width
+ * @param height
+ */
+ private Canvas acquireCanvas(int width, int height) {
+ if (DEBUG_ATLAS_TEXTURE) {
+ mAtlasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ return new Canvas(mAtlasBitmap);
+ } else {
+ Canvas canvas = new Canvas();
+ mNativeBitmap = nAcquireAtlasCanvas(canvas, width, height);
+ return canvas;
+ }
+ }
+
+ /**
+ * Releases the canvas used to render into the buffer. Calling this method
+ * will release any resource previously acquired. If {@link #DEBUG_ATLAS_TEXTURE}
+ * is turend on, calling this method will write the content of the atlas
+ * to disk in /data/system/atlas.png for debugging.
+ */
+ private void releaseCanvas(Canvas canvas) {
+ if (DEBUG_ATLAS_TEXTURE) {
+ canvas.setBitmap(null);
+
+ File systemDirectory = new File(Environment.getDataDirectory(), "system");
+ File dataFile = new File(systemDirectory, "atlas.png");
+
+ try {
+ FileOutputStream out = new FileOutputStream(dataFile);
+ mAtlasBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ out.close();
+ } catch (FileNotFoundException e) {
+ // Ignore
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ mAtlasBitmap.recycle();
+ mAtlasBitmap = null;
+ } else {
+ nReleaseAtlasCanvas(canvas, mNativeBitmap);
+ }
+ }
+ }
+
+ private static native int nAcquireAtlasCanvas(Canvas canvas, int width, int height);
+ private static native void nReleaseAtlasCanvas(Canvas canvas, int bitmap);
+ private static native boolean nUploadAtlas(GraphicBuffer buffer, int bitmap);
+
+ @Override
+ public boolean isCompatible(int ppid) {
+ return ppid == android.os.Process.myPpid();
+ }
+
+ @Override
+ public GraphicBuffer getBuffer() throws RemoteException {
+ return mAtlasReady.get() ? mBuffer : null;
+ }
+
+ @Override
+ public int[] getMap() throws RemoteException {
+ return mAtlasReady.get() ? mAtlasMap : null;
+ }
+
+ /**
+ * Finds the best atlas configuration to pack the list of supplied bitmaps.
+ * This method takes advantage of multi-core systems by spawning a number
+ * of threads equal to the number of available cores.
+ */
+ private static Configuration computeBestConfiguration(
+ ArrayList<Bitmap> bitmaps, int pixelCount) {
+ if (DEBUG_ATLAS) Log.d(LOG_TAG, "Computing best atlas configuration...");
+
+ long begin = System.nanoTime();
+ List<WorkerResult> results = Collections.synchronizedList(new ArrayList<WorkerResult>());
+
+ // Don't bother with an extra thread if there's only one processor
+ int cpuCount = Runtime.getRuntime().availableProcessors();
+ if (cpuCount == 1) {
+ new ComputeWorker(MIN_SIZE, MAX_SIZE, STEP, bitmaps, pixelCount, results, null).run();
+ } else {
+ int start = MIN_SIZE;
+ int end = MAX_SIZE - (cpuCount - 1) * STEP;
+ int step = STEP * cpuCount;
+
+ final CountDownLatch signal = new CountDownLatch(cpuCount);
+
+ for (int i = 0; i < cpuCount; i++, start += STEP, end += STEP) {
+ ComputeWorker worker = new ComputeWorker(start, end, step,
+ bitmaps, pixelCount, results, signal);
+ new Thread(worker, "Atlas Worker #" + (i + 1)).start();
+ }
+
+ try {
+ signal.await(10, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.w(LOG_TAG, "Could not complete configuration computation");
+ return null;
+ }
+ }
+
+ // Maximize the number of packed bitmaps, minimize the texture size
+ Collections.sort(results, new Comparator<WorkerResult>() {
+ @Override
+ public int compare(WorkerResult r1, WorkerResult r2) {
+ int delta = r2.count - r1.count;
+ if (delta != 0) return delta;
+ return r1.width * r1.height - r2.width * r2.height;
+ }
+ });
+
+ if (DEBUG_ATLAS) {
+ float delay = (System.nanoTime() - begin) / 1000.0f / 1000.0f / 1000.0f;
+ Log.d(LOG_TAG, String.format("Found best atlas configuration in %.2fs", delay));
+ }
+
+ WorkerResult result = results.get(0);
+ return new Configuration(result.type, result.width, result.height, result.count);
+ }
+
+ /**
+ * Returns the path to the file containing the best computed
+ * atlas configuration.
+ */
+ private static File getDataFile() {
+ File systemDirectory = new File(Environment.getDataDirectory(), "system");
+ return new File(systemDirectory, "framework_atlas.config");
+ }
+
+ private static void deleteDataFile() {
+ Log.w(LOG_TAG, "Current configuration inconsistent with assets list");
+ if (!getDataFile().delete()) {
+ Log.w(LOG_TAG, "Could not delete the current configuration");
+ }
+ }
+
+ private File getFrameworkResourcesFile() {
+ return new File(mContext.getApplicationInfo().sourceDir);
+ }
+
+ /**
+ * Returns the best known atlas configuration. This method will either
+ * read the configuration from disk or start a brute-force search
+ * and save the result out to disk.
+ */
+ private Configuration chooseConfiguration(ArrayList<Bitmap> bitmaps, int pixelCount,
+ String versionName) {
+ Configuration config = null;
+
+ final File dataFile = getDataFile();
+ if (dataFile.exists()) {
+ config = readConfiguration(dataFile, versionName);
+ }
+
+ if (config == null) {
+ config = computeBestConfiguration(bitmaps, pixelCount);
+ if (config != null) writeConfiguration(config, dataFile, versionName);
+ }
+
+ return config;
+ }
+
+ /**
+ * Writes the specified atlas configuration to the specified file.
+ */
+ private void writeConfiguration(Configuration config, File file, String versionName) {
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
+ writer.write(getBuildIdentifier(versionName));
+ writer.newLine();
+ writer.write(config.type.toString());
+ writer.newLine();
+ writer.write(String.valueOf(config.width));
+ writer.newLine();
+ writer.write(String.valueOf(config.height));
+ writer.newLine();
+ writer.write(String.valueOf(config.count));
+ writer.newLine();
+ writer.write(String.valueOf(config.flags));
+ writer.newLine();
+ } catch (FileNotFoundException e) {
+ Log.w(LOG_TAG, "Could not write " + file, e);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Could not write " + file, e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Reads an atlas configuration from the specified file. This method
+ * returns null if an error occurs or if the configuration is invalid.
+ */
+ private Configuration readConfiguration(File file, String versionName) {
+ BufferedReader reader = null;
+ Configuration config = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+
+ if (checkBuildIdentifier(reader, versionName)) {
+ Atlas.Type type = Atlas.Type.valueOf(reader.readLine());
+ int width = readInt(reader, MIN_SIZE, MAX_SIZE);
+ int height = readInt(reader, MIN_SIZE, MAX_SIZE);
+ int count = readInt(reader, 0, Integer.MAX_VALUE);
+ int flags = readInt(reader, Integer.MIN_VALUE, Integer.MAX_VALUE);
+
+ config = new Configuration(type, width, height, count, flags);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(LOG_TAG, "Invalid parameter value in " + file, e);
+ } catch (FileNotFoundException e) {
+ Log.w(LOG_TAG, "Could not read " + file, e);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Could not read " + file, e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ return config;
+ }
+
+ private static int readInt(BufferedReader reader, int min, int max) throws IOException {
+ return Math.max(min, Math.min(max, Integer.parseInt(reader.readLine())));
+ }
+
+ /**
+ * Compares the next line in the specified buffered reader to the current
+ * build identifier. Returns whether the two values are equal.
+ *
+ * @see #getBuildIdentifier(String)
+ */
+ private boolean checkBuildIdentifier(BufferedReader reader, String versionName)
+ throws IOException {
+ String deviceBuildId = getBuildIdentifier(versionName);
+ String buildId = reader.readLine();
+ return deviceBuildId.equals(buildId);
+ }
+
+ /**
+ * Returns an identifier for the current build that can be used to detect
+ * likely changes to framework resources. The build identifier is made of
+ * several distinct values:
+ *
+ * build fingerprint/framework version name/file size of framework resources apk
+ *
+ * Only the build fingerprint should be necessary on user builds but
+ * the other values are useful to detect changes on eng builds during
+ * development.
+ *
+ * This identifier does not attempt to be exact: a new identifier does not
+ * necessarily mean the preloaded drawables have changed. It is important
+ * however that whenever the list of preloaded drawables changes, this
+ * identifier changes as well.
+ *
+ * @see #checkBuildIdentifier(java.io.BufferedReader, String)
+ */
+ private String getBuildIdentifier(String versionName) {
+ return SystemProperties.get("ro.build.fingerprint", "") + '/' + versionName + '/' +
+ String.valueOf(getFrameworkResourcesFile().length());
+ }
+
+ /**
+ * Atlas configuration. Specifies the algorithm, dimensions and flags to use.
+ */
+ private static class Configuration {
+ final Atlas.Type type;
+ final int width;
+ final int height;
+ final int count;
+ final int flags;
+
+ Configuration(Atlas.Type type, int width, int height, int count) {
+ this(type, width, height, count, Atlas.FLAG_DEFAULTS);
+ }
+
+ Configuration(Atlas.Type type, int width, int height, int count, int flags) {
+ this.type = type;
+ this.width = width;
+ this.height = height;
+ this.count = count;
+ this.flags = flags;
+ }
+
+ @Override
+ public String toString() {
+ return type.toString() + " (" + width + "x" + height + ") flags=0x" +
+ Integer.toHexString(flags) + " count=" + count;
+ }
+ }
+
+ /**
+ * Used during the brute-force search to gather information about each
+ * variant of the packing algorithm.
+ */
+ private static class WorkerResult {
+ Atlas.Type type;
+ int width;
+ int height;
+ int count;
+
+ WorkerResult(Atlas.Type type, int width, int height, int count) {
+ this.type = type;
+ this.width = width;
+ this.height = height;
+ this.count = count;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s %dx%d", type.toString(), width, height);
+ }
+ }
+
+ /**
+ * A compute worker will try a finite number of variations of the packing
+ * algorithms and save the results in a supplied list.
+ */
+ private static class ComputeWorker implements Runnable {
+ private final int mStart;
+ private final int mEnd;
+ private final int mStep;
+ private final List<Bitmap> mBitmaps;
+ private final List<WorkerResult> mResults;
+ private final CountDownLatch mSignal;
+ private final int mThreshold;
+
+ /**
+ * Creates a new compute worker to brute-force through a range of
+ * packing algorithms variants.
+ *
+ * @param start The minimum texture width to try
+ * @param end The maximum texture width to try
+ * @param step The number of pixels to increment the texture width by at each step
+ * @param bitmaps The list of bitmaps to pack in the atlas
+ * @param pixelCount The total number of pixels occupied by the list of bitmaps
+ * @param results The list of results in which to save the brute-force search results
+ * @param signal Latch to decrement when this worker is done, may be null
+ */
+ ComputeWorker(int start, int end, int step, List<Bitmap> bitmaps, int pixelCount,
+ List<WorkerResult> results, CountDownLatch signal) {
+ mStart = start;
+ mEnd = end;
+ mStep = step;
+ mBitmaps = bitmaps;
+ mResults = results;
+ mSignal = signal;
+
+ // Minimum number of pixels we want to be able to pack
+ int threshold = (int) (pixelCount * PACKING_THRESHOLD);
+ // Make sure we can find at least one configuration
+ while (threshold > MAX_SIZE * MAX_SIZE) {
+ threshold >>= 1;
+ }
+ mThreshold = threshold;
+ }
+
+ @Override
+ public void run() {
+ if (DEBUG_ATLAS) Log.d(LOG_TAG, "Running " + Thread.currentThread().getName());
+
+ Atlas.Entry entry = new Atlas.Entry();
+ for (Atlas.Type type : Atlas.Type.values()) {
+ for (int width = mStart; width < mEnd; width += mStep) {
+ for (int height = MIN_SIZE; height < MAX_SIZE; height += STEP) {
+ // If the atlas is not big enough, skip it
+ if (width * height <= mThreshold) continue;
+
+ final int count = packBitmaps(type, width, height, entry);
+ if (count > 0) {
+ mResults.add(new WorkerResult(type, width, height, count));
+ // If we were able to pack everything let's stop here
+ // Increasing the height further won't make things better
+ if (count == mBitmaps.size()) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (mSignal != null) {
+ mSignal.countDown();
+ }
+ }
+
+ private int packBitmaps(Atlas.Type type, int width, int height, Atlas.Entry entry) {
+ int total = 0;
+ Atlas atlas = new Atlas(type, width, height);
+
+ final int count = mBitmaps.size();
+ for (int i = 0; i < count; i++) {
+ final Bitmap bitmap = mBitmaps.get(i);
+ if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) {
+ total++;
+ }
+ }
+
+ return total;
+ }
+ }
+}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 1f2947d..de58496 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.os.BatteryStats;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
@@ -88,8 +89,7 @@
private int mCriticalBatteryLevel;
private static final int DUMP_MAX_LENGTH = 24 * 1024;
- private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-u" };
- private static final String BATTERY_STATS_SERVICE_NAME = "batteryinfo";
+ private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
private static final String DUMPSYS_DATA_PATH = "/data/system/";
@@ -509,7 +509,7 @@
}
private void logBatteryStatsLocked() {
- IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME);
+ IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);
if (batteryInfoService == null) return;
DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
@@ -519,7 +519,7 @@
FileOutputStream dumpStream = null;
try {
// dump the service to a file
- dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump");
+ dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");
dumpStream = new FileOutputStream(dumpFile);
batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
FileUtils.sync(dumpStream);
@@ -642,15 +642,25 @@
update = false;
}
if (update) {
- mUpdatesStopped = true;
- processValuesLocked();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mUpdatesStopped = true;
+ processValuesLocked();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
} catch (NumberFormatException ex) {
pw.println("Bad value: " + value);
}
} else if (args.length == 1 && "reset".equals(args[0])) {
- mUpdatesStopped = false;
- updateLocked();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mUpdatesStopped = false;
+ updateLocked();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
pw.println("Dump current battery state, or:");
pw.println(" set ac|usb|wireless|status|level|invalid <value>");
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index bea2cca..f0164cb 100644
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -34,7 +34,6 @@
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -120,7 +119,6 @@
// used inside handler thread
private boolean mEnable;
private int mState;
- private HandlerThread mThread;
private final BluetoothHandler mHandler;
private int mErrorRecoveryRetryCounter;
@@ -194,9 +192,7 @@
};
BluetoothManagerService(Context context) {
- mThread = new HandlerThread("BluetoothManager");
- mThread.start();
- mHandler = new BluetoothHandler(mThread.getLooper());
+ mHandler = new BluetoothHandler(IoThread.get().getLooper());
mContext = context;
mBluetooth = null;
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index e7dd3b7..f2e0f29 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -107,6 +107,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.am.BatteryStatsService;
+import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
@@ -371,6 +372,7 @@
// the set of network types that can only be enabled by system/sig apps
List mProtectedNetworks;
+ private DataConnectionStats mDataConnectionStats;
private AtomicInteger mEnableFailFastMobileDataTag = new AtomicInteger(0);
TelephonyManager mTelephonyManager;
@@ -604,6 +606,9 @@
mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
loadGlobalProxy();
+
+ mDataConnectionStats = new DataConnectionStats(mContext);
+ mDataConnectionStats.startMonitoring();
}
/**
@@ -3184,6 +3189,10 @@
Settings.Global.HTTP_PROXY);
if (!TextUtils.isEmpty(proxy)) {
String data[] = proxy.split(":");
+ if (data.length == 0) {
+ return;
+ }
+
String proxyHost = data[0];
int proxyPort = 8080;
if (data.length > 1) {
@@ -3195,6 +3204,8 @@
}
ProxyProperties p = new ProxyProperties(data[0], proxyPort, "");
setGlobalProxy(p);
+ } else {
+ setGlobalProxy(null);
}
}
diff --git a/services/java/com/android/server/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java
index fc76277..8407fa4 100644
--- a/services/java/com/android/server/CountryDetectorService.java
+++ b/services/java/com/android/server/CountryDetectorService.java
@@ -20,6 +20,7 @@
import java.io.PrintWriter;
import java.util.HashMap;
+import com.android.internal.os.BackgroundThread;
import com.android.server.location.ComprehensiveCountryDetector;
import android.content.Context;
@@ -29,8 +30,6 @@
import android.location.ICountryListener;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Process;
import android.os.RemoteException;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -169,8 +168,7 @@
void systemReady() {
// Shall we wait for the initialization finish.
- Thread thread = new Thread(this, "CountryDetectorService");
- thread.start();
+ BackgroundThread.getHandler().post(this);
}
private void initialize() {
@@ -187,12 +185,9 @@
}
public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- Looper.prepare();
mHandler = new Handler();
initialize();
mSystemReady = true;
- Looper.loop();
}
protected void setCountryListener(final CountryListener listener) {
diff --git a/services/java/com/android/server/FgThread.java b/services/java/com/android/server/FgThread.java
new file mode 100644
index 0000000..3b655f2
--- /dev/null
+++ b/services/java/com/android/server/FgThread.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+/**
+ * Shared singleton foreground thread for the system. This is a thread for regular
+ * foreground service operations, which shouldn't be blocked by anything running in
+ * the background. In particular, the shared background thread could be doing
+ * relatively long-running operations like saving state to disk (in addition to
+ * simply being a background priority), which can cause operations scheduled on it
+ * to be delayed for a user-noticeable amount of time.
+ */
+public final class FgThread extends HandlerThread {
+ private static FgThread sInstance;
+ private static Handler sHandler;
+
+ private FgThread() {
+ super("android.fg", android.os.Process.THREAD_PRIORITY_DEFAULT);
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new FgThread();
+ sInstance.start();
+ sHandler = new Handler(sInstance.getLooper());
+ sHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ android.os.Process.setCanSelfBackground(false);
+ }
+ });
+ }
+ }
+
+ public static FgThread get() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index c916857..1c1b002 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -791,6 +791,11 @@
// InputMethodFileManager should be reset when the user is changed
mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
final String defaultImiId = mSettings.getSelectedInputMethod();
+ // For secondary users, the list of enabled IMEs may not have been updated since the
+ // callbacks to PackageMonitor are ignored for the secondary user. Here, defaultImiId may
+ // not be empty even if the IME has been uninstalled by the primary user.
+ // Even in such cases, IMMS works fine because it will find the most applicable
+ // IME for that user.
final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId);
if (DEBUG) {
Slog.d(TAG, "Switch user: " + newUserId + " current ime = " + defaultImiId);
@@ -1506,23 +1511,16 @@
final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) {
// Used to load label
- final PackageManager pm = mContext.getPackageManager();
final CharSequence title = mRes.getText(
com.android.internal.R.string.select_input_method);
- final CharSequence imiLabel = imi.loadLabel(pm);
- final CharSequence summary = mCurrentSubtype != null
- ? TextUtils.concat(mCurrentSubtype.getDisplayName(mContext,
- imi.getPackageName(), imi.getServiceInfo().applicationInfo),
- (TextUtils.isEmpty(imiLabel) ?
- "" : " - " + imiLabel))
- : imiLabel;
+ final CharSequence summary = InputMethodUtils.getImeAndSubtypeDisplayName(
+ mContext, imi, mCurrentSubtype);
mImeSwitcherNotification.setLatestEventInfo(
mContext, title, summary, mImeSwitchPendingIntent);
if (mNotificationManager != null) {
if (DEBUG) {
- Slog.d(TAG, "--- show notification: label = " + imiLabel
- + ", summary = " + summary);
+ Slog.d(TAG, "--- show notification: label = " + summary);
}
mNotificationManager.notifyAsUser(null,
com.android.internal.R.string.select_input_method,
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 35345f5..3a35f3f 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -20,7 +20,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -29,12 +28,12 @@
import android.net.Uri;
import android.util.FastImmutableArraySet;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.LogPrinter;
import android.util.Printer;
-import android.util.StringBuilderPrinter;
import android.content.Intent;
import android.content.IntentFilter;
@@ -46,7 +45,6 @@
final private static String TAG = "IntentResolver";
final private static boolean DEBUG = false;
final private static boolean localLOGV = DEBUG || false;
- final private static boolean VALIDATE = false;
public void addFilter(F f) {
if (localLOGV) {
@@ -67,21 +65,11 @@
register_intent_filter(f, f.actionsIterator(),
mTypedActionToFilter, " TypedAction: ");
}
-
- if (VALIDATE) {
- mOldResolver.addFilter(f);
- verifyDataStructures(f);
- }
}
public void removeFilter(F f) {
removeFilterInternal(f);
mFilters.remove(f);
-
- if (VALIDATE) {
- mOldResolver.removeFilter(f);
- verifyDataStructures(f);
- }
}
void removeFilterInternal(F f) {
@@ -319,16 +307,6 @@
}
sortResults(finalList);
- if (VALIDATE) {
- List<R> oldList = mOldResolver.queryIntent(intent, resolvedType, defaultOnly, userId);
- if (oldList.size() != finalList.size()) {
- ValidationFailure here = new ValidationFailure();
- here.fillInStackTrace();
- Log.wtf(TAG, "Query result " + intent + " size is " + finalList.size()
- + "; old implementation is " + oldList.size(), here);
- }
- }
-
if (debug) {
Slog.v(TAG, "Final result list:");
for (R r : finalList) {
@@ -379,7 +357,7 @@
out.print(prefix); out.println(filter);
}
- private final void addFilter(HashMap<String, F[]> map, String name, F filter) {
+ private final void addFilter(ArrayMap<String, F[]> map, String name, F filter) {
F[] array = map.get(name);
if (array == null) {
array = newArray(2);
@@ -464,7 +442,7 @@
}
private final int register_intent_filter(F filter, Iterator<String> i,
- HashMap<String, F[]> dest, String prefix) {
+ ArrayMap<String, F[]> dest, String prefix) {
if (i == null) {
return 0;
}
@@ -480,7 +458,7 @@
}
private final int unregister_intent_filter(F filter, Iterator<String> i,
- HashMap<String, F[]> dest, String prefix) {
+ ArrayMap<String, F[]> dest, String prefix) {
if (i == null) {
return 0;
}
@@ -495,7 +473,7 @@
return num;
}
- private final void remove_all_objects(HashMap<String, F[]> map, String name,
+ private final void remove_all_objects(ArrayMap<String, F[]> map, String name,
Object object) {
F[] array = map.get(name);
if (array != null) {
@@ -613,120 +591,6 @@
}
};
- static class ValidationFailure extends RuntimeException {
- }
-
- private void verifyDataStructures(IntentFilter src) {
- compareMaps(src, "mTypeToFilter", mTypeToFilter, mOldResolver.mTypeToFilter);
- compareMaps(src, "mBaseTypeToFilter", mBaseTypeToFilter, mOldResolver.mBaseTypeToFilter);
- compareMaps(src, "mWildTypeToFilter", mWildTypeToFilter, mOldResolver.mWildTypeToFilter);
- compareMaps(src, "mSchemeToFilter", mSchemeToFilter, mOldResolver.mSchemeToFilter);
- compareMaps(src, "mActionToFilter", mActionToFilter, mOldResolver.mActionToFilter);
- compareMaps(src, "mTypedActionToFilter", mTypedActionToFilter, mOldResolver.mTypedActionToFilter);
- }
-
- private void compareMaps(IntentFilter src, String name, HashMap<String, F[]> cur,
- HashMap<String, ArrayList<F>> old) {
- if (cur.size() != old.size()) {
- StringBuilder missing = new StringBuilder(128);
- for (Map.Entry<String, ArrayList<F>> e : old.entrySet()) {
- final F[] curArray = cur.get(e.getKey());
- if (curArray == null) {
- if (missing.length() > 0) {
- missing.append(' ');
- }
- missing.append(e.getKey());
- }
- }
- StringBuilder extra = new StringBuilder(128);
- for (Map.Entry<String, F[]> e : cur.entrySet()) {
- if (old.get(e.getKey()) == null) {
- if (extra.length() > 0) {
- extra.append(' ');
- }
- extra.append(e.getKey());
- }
- }
- StringBuilder srcStr = new StringBuilder(1024);
- StringBuilderPrinter printer = new StringBuilderPrinter(srcStr);
- src.dump(printer, "");
- ValidationFailure here = new ValidationFailure();
- here.fillInStackTrace();
- Log.wtf(TAG, "New map " + name + " size is " + cur.size()
- + "; old implementation is " + old.size()
- + "; missing: " + missing.toString()
- + "; extra: " + extra.toString()
- + "; src: " + srcStr.toString(), here);
- return;
- }
- for (Map.Entry<String, ArrayList<F>> e : old.entrySet()) {
- final F[] curArray = cur.get(e.getKey());
- int curLen = curArray != null ? curArray.length : 0;
- if (curLen == 0) {
- ValidationFailure here = new ValidationFailure();
- here.fillInStackTrace();
- Log.wtf(TAG, "New map " + name + " doesn't contain expected key "
- + e.getKey() + " (array=" + curArray + ")");
- return;
- }
- while (curLen > 0 && curArray[curLen-1] == null) {
- curLen--;
- }
- final ArrayList<F> oldArray = e.getValue();
- final int oldLen = oldArray.size();
- if (curLen != oldLen) {
- ValidationFailure here = new ValidationFailure();
- here.fillInStackTrace();
- Log.wtf(TAG, "New map " + name + " entry " + e.getKey() + " size is "
- + curLen + "; old implementation is " + oldLen, here);
- return;
- }
- for (int i=0; i<oldLen; i++) {
- F f = oldArray.get(i);
- boolean found = false;
- for (int j=0; j<curLen; j++) {
- if (curArray[j] == f) {
- found = true;
- break;
- }
- }
- if (!found) {
- ValidationFailure here = new ValidationFailure();
- here.fillInStackTrace();
- Log.wtf(TAG, "New map " + name + " entry + " + e.getKey()
- + " doesn't contain expected filter " + f, here);
- }
- }
- for (int i=0; i<curLen; i++) {
- if (curArray[i] == null) {
- ValidationFailure here = new ValidationFailure();
- here.fillInStackTrace();
- Log.wtf(TAG, "New map " + name + " entry + " + e.getKey()
- + " has unexpected null at " + i + "; array: " + curArray, here);
- break;
- }
- }
- }
- }
-
- private final IntentResolverOld<F, R> mOldResolver = new IntentResolverOld<F, R>() {
- @Override protected boolean isPackageForFilter(String packageName, F filter) {
- return IntentResolver.this.isPackageForFilter(packageName, filter);
- }
- @Override protected boolean allowFilterResult(F filter, List<R> dest) {
- return IntentResolver.this.allowFilterResult(filter, dest);
- }
- @Override protected boolean isFilterStopped(F filter, int userId) {
- return IntentResolver.this.isFilterStopped(filter, userId);
- }
- @Override protected R newResult(F filter, int match, int userId) {
- return IntentResolver.this.newResult(filter, match, userId);
- }
- @Override protected void sortResults(List<R> results) {
- IntentResolver.this.sortResults(results);
- }
- };
-
/**
* All filters that have been registered.
*/
@@ -736,14 +600,14 @@
* All of the MIME types that have been registered, such as "image/jpeg",
* "image/*", or "{@literal *}/*".
*/
- private final HashMap<String, F[]> mTypeToFilter = new HashMap<String, F[]>();
+ private final ArrayMap<String, F[]> mTypeToFilter = new ArrayMap<String, F[]>();
/**
* The base names of all of all fully qualified MIME types that have been
* registered, such as "image" or "*". Wild card MIME types such as
* "image/*" will not be here.
*/
- private final HashMap<String, F[]> mBaseTypeToFilter = new HashMap<String, F[]>();
+ private final ArrayMap<String, F[]> mBaseTypeToFilter = new ArrayMap<String, F[]>();
/**
* The base names of all of the MIME types with a sub-type wildcard that
@@ -752,21 +616,21 @@
* included here. This also includes the "*" for the "{@literal *}/*"
* MIME type.
*/
- private final HashMap<String, F[]> mWildTypeToFilter = new HashMap<String, F[]>();
+ private final ArrayMap<String, F[]> mWildTypeToFilter = new ArrayMap<String, F[]>();
/**
* All of the URI schemes (such as http) that have been registered.
*/
- private final HashMap<String, F[]> mSchemeToFilter = new HashMap<String, F[]>();
+ private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>();
/**
* All of the actions that have been registered, but only those that did
* not specify data.
*/
- private final HashMap<String, F[]> mActionToFilter = new HashMap<String, F[]>();
+ private final ArrayMap<String, F[]> mActionToFilter = new ArrayMap<String, F[]>();
/**
* All of the actions that have been registered and specified a MIME type.
*/
- private final HashMap<String, F[]> mTypedActionToFilter = new HashMap<String, F[]>();
+ private final ArrayMap<String, F[]> mTypedActionToFilter = new ArrayMap<String, F[]>();
}
diff --git a/services/java/com/android/server/IntentResolverOld.java b/services/java/com/android/server/IntentResolverOld.java
deleted file mode 100644
index 94a2379..0000000
--- a/services/java/com/android/server/IntentResolverOld.java
+++ /dev/null
@@ -1,638 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import android.net.Uri;
-import android.util.FastImmutableArraySet;
-import android.util.Log;
-import android.util.PrintWriterPrinter;
-import android.util.Slog;
-import android.util.LogPrinter;
-import android.util.Printer;
-
-import android.content.Intent;
-import android.content.IntentFilter;
-
-/**
- * Temporary for verification of new implementation.
- * {@hide}
- */
-public abstract class IntentResolverOld<F extends IntentFilter, R extends Object> {
- final private static String TAG = "IntentResolver";
- final private static boolean DEBUG = false;
- final private static boolean localLOGV = DEBUG || false;
-
- public void addFilter(F f) {
- if (localLOGV) {
- Slog.v(TAG, "Adding filter: " + f);
- f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
- Slog.v(TAG, " Building Lookup Maps:");
- }
-
- mFilters.add(f);
- int numS = register_intent_filter(f, f.schemesIterator(),
- mSchemeToFilter, " Scheme: ");
- int numT = register_mime_types(f, " Type: ");
- if (numS == 0 && numT == 0) {
- register_intent_filter(f, f.actionsIterator(),
- mActionToFilter, " Action: ");
- }
- if (numT != 0) {
- register_intent_filter(f, f.actionsIterator(),
- mTypedActionToFilter, " TypedAction: ");
- }
- }
-
- public void removeFilter(F f) {
- removeFilterInternal(f);
- mFilters.remove(f);
- }
-
- void removeFilterInternal(F f) {
- if (localLOGV) {
- Slog.v(TAG, "Removing filter: " + f);
- f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
- Slog.v(TAG, " Cleaning Lookup Maps:");
- }
-
- int numS = unregister_intent_filter(f, f.schemesIterator(),
- mSchemeToFilter, " Scheme: ");
- int numT = unregister_mime_types(f, " Type: ");
- if (numS == 0 && numT == 0) {
- unregister_intent_filter(f, f.actionsIterator(),
- mActionToFilter, " Action: ");
- }
- if (numT != 0) {
- unregister_intent_filter(f, f.actionsIterator(),
- mTypedActionToFilter, " TypedAction: ");
- }
- }
-
- boolean dumpMap(PrintWriter out, String titlePrefix, String title,
- String prefix, Map<String, ArrayList<F>> map, String packageName,
- boolean printFilter) {
- String eprefix = prefix + " ";
- String fprefix = prefix + " ";
- boolean printedSomething = false;
- Printer printer = null;
- for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
- ArrayList<F> a = e.getValue();
- final int N = a.size();
- boolean printedHeader = false;
- for (int i=0; i<N; i++) {
- F filter = a.get(i);
- if (packageName != null && !isPackageForFilter(packageName, filter)) {
- continue;
- }
- if (title != null) {
- out.print(titlePrefix); out.println(title);
- title = null;
- }
- if (!printedHeader) {
- out.print(eprefix); out.print(e.getKey()); out.println(":");
- printedHeader = true;
- }
- printedSomething = true;
- dumpFilter(out, fprefix, filter);
- if (printFilter) {
- if (printer == null) {
- printer = new PrintWriterPrinter(out);
- }
- filter.dump(printer, fprefix + " ");
- }
- }
- }
- return printedSomething;
- }
-
- public boolean dump(PrintWriter out, String title, String prefix, String packageName,
- boolean printFilter) {
- String innerPrefix = prefix + " ";
- String sepPrefix = "\n" + prefix;
- String curPrefix = title + "\n" + prefix;
- if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
- mTypeToFilter, packageName, printFilter)) {
- curPrefix = sepPrefix;
- }
- if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
- mBaseTypeToFilter, packageName, printFilter)) {
- curPrefix = sepPrefix;
- }
- if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
- mWildTypeToFilter, packageName, printFilter)) {
- curPrefix = sepPrefix;
- }
- if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
- mSchemeToFilter, packageName, printFilter)) {
- curPrefix = sepPrefix;
- }
- if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
- mActionToFilter, packageName, printFilter)) {
- curPrefix = sepPrefix;
- }
- if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
- mTypedActionToFilter, packageName, printFilter)) {
- curPrefix = sepPrefix;
- }
- return curPrefix == sepPrefix;
- }
-
- private class IteratorWrapper implements Iterator<F> {
- private final Iterator<F> mI;
- private F mCur;
-
- IteratorWrapper(Iterator<F> it) {
- mI = it;
- }
-
- public boolean hasNext() {
- return mI.hasNext();
- }
-
- public F next() {
- return (mCur = mI.next());
- }
-
- public void remove() {
- if (mCur != null) {
- removeFilterInternal(mCur);
- }
- mI.remove();
- }
-
- }
-
- /**
- * Returns an iterator allowing filters to be removed.
- */
- public Iterator<F> filterIterator() {
- return new IteratorWrapper(mFilters.iterator());
- }
-
- /**
- * Returns a read-only set of the filters.
- */
- public Set<F> filterSet() {
- return Collections.unmodifiableSet(mFilters);
- }
-
- public List<R> queryIntentFromList(Intent intent, String resolvedType,
- boolean defaultOnly, ArrayList<ArrayList<F>> listCut, int userId) {
- ArrayList<R> resultList = new ArrayList<R>();
-
- final boolean debug = localLOGV ||
- ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
-
- FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
- final String scheme = intent.getScheme();
- int N = listCut.size();
- for (int i = 0; i < N; ++i) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, listCut.get(i), resultList, userId);
- }
- sortResults(resultList);
- return resultList;
- }
-
- public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
- int userId) {
- String scheme = intent.getScheme();
-
- ArrayList<R> finalList = new ArrayList<R>();
-
- final boolean debug = localLOGV ||
- ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
-
- if (debug) Slog.v(
- TAG, "Resolving type " + resolvedType + " scheme " + scheme
- + " of intent " + intent);
-
- ArrayList<F> firstTypeCut = null;
- ArrayList<F> secondTypeCut = null;
- ArrayList<F> thirdTypeCut = null;
- ArrayList<F> schemeCut = null;
-
- // If the intent includes a MIME type, then we want to collect all of
- // the filters that match that MIME type.
- if (resolvedType != null) {
- int slashpos = resolvedType.indexOf('/');
- if (slashpos > 0) {
- final String baseType = resolvedType.substring(0, slashpos);
- if (!baseType.equals("*")) {
- if (resolvedType.length() != slashpos+2
- || resolvedType.charAt(slashpos+1) != '*') {
- // Not a wild card, so we can just look for all filters that
- // completely match or wildcards whose base type matches.
- firstTypeCut = mTypeToFilter.get(resolvedType);
- if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut);
- secondTypeCut = mWildTypeToFilter.get(baseType);
- if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut);
- } else {
- // We can match anything with our base type.
- firstTypeCut = mBaseTypeToFilter.get(baseType);
- if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut);
- secondTypeCut = mWildTypeToFilter.get(baseType);
- if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut);
- }
- // Any */* types always apply, but we only need to do this
- // if the intent type was not already */*.
- thirdTypeCut = mWildTypeToFilter.get("*");
- if (debug) Slog.v(TAG, "Third type cut: " + thirdTypeCut);
- } else if (intent.getAction() != null) {
- // The intent specified any type ({@literal *}/*). This
- // can be a whole heck of a lot of things, so as a first
- // cut let's use the action instead.
- firstTypeCut = mTypedActionToFilter.get(intent.getAction());
- if (debug) Slog.v(TAG, "Typed Action list: " + firstTypeCut);
- }
- }
- }
-
- // If the intent includes a data URI, then we want to collect all of
- // the filters that match its scheme (we will further refine matches
- // on the authority and path by directly matching each resulting filter).
- if (scheme != null) {
- schemeCut = mSchemeToFilter.get(scheme);
- if (debug) Slog.v(TAG, "Scheme list: " + schemeCut);
- }
-
- // If the intent does not specify any data -- either a MIME type or
- // a URI -- then we will only be looking for matches against empty
- // data.
- if (resolvedType == null && scheme == null && intent.getAction() != null) {
- firstTypeCut = mActionToFilter.get(intent.getAction());
- if (debug) Slog.v(TAG, "Action list: " + firstTypeCut);
- }
-
- FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
- if (firstTypeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, firstTypeCut, finalList, userId);
- }
- if (secondTypeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, secondTypeCut, finalList, userId);
- }
- if (thirdTypeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, thirdTypeCut, finalList, userId);
- }
- if (schemeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, schemeCut, finalList, userId);
- }
- sortResults(finalList);
-
- if (debug) {
- Slog.v(TAG, "Final result list:");
- for (R r : finalList) {
- Slog.v(TAG, " " + r);
- }
- }
- return finalList;
- }
-
- /**
- * Control whether the given filter is allowed to go into the result
- * list. Mainly intended to prevent adding multiple filters for the
- * same target object.
- */
- protected boolean allowFilterResult(F filter, List<R> dest) {
- return true;
- }
-
- /**
- * Returns whether the object associated with the given filter is
- * "stopped," that is whether it should not be included in the result
- * if the intent requests to excluded stopped objects.
- */
- protected boolean isFilterStopped(F filter, int userId) {
- return false;
- }
-
- /**
- * Returns whether this filter is owned by this package. This must be
- * implemented to provide correct filtering of Intents that have
- * specified a package name they are to be delivered to.
- */
- protected abstract boolean isPackageForFilter(String packageName, F filter);
-
- @SuppressWarnings("unchecked")
- protected R newResult(F filter, int match, int userId) {
- return (R)filter;
- }
-
- @SuppressWarnings("unchecked")
- protected void sortResults(List<R> results) {
- Collections.sort(results, mResolvePrioritySorter);
- }
-
- protected void dumpFilter(PrintWriter out, String prefix, F filter) {
- out.print(prefix); out.println(filter);
- }
-
- private final int register_mime_types(F filter, String prefix) {
- final Iterator<String> i = filter.typesIterator();
- if (i == null) {
- return 0;
- }
-
- int num = 0;
- while (i.hasNext()) {
- String name = i.next();
- num++;
- if (localLOGV) Slog.v(TAG, prefix + name);
- String baseName = name;
- final int slashpos = name.indexOf('/');
- if (slashpos > 0) {
- baseName = name.substring(0, slashpos).intern();
- } else {
- name = name + "/*";
- }
-
- ArrayList<F> array = mTypeToFilter.get(name);
- if (array == null) {
- //Slog.v(TAG, "Creating new array for " + name);
- array = new ArrayList<F>();
- mTypeToFilter.put(name, array);
- }
- array.add(filter);
-
- if (slashpos > 0) {
- array = mBaseTypeToFilter.get(baseName);
- if (array == null) {
- //Slog.v(TAG, "Creating new array for " + name);
- array = new ArrayList<F>();
- mBaseTypeToFilter.put(baseName, array);
- }
- array.add(filter);
- } else {
- array = mWildTypeToFilter.get(baseName);
- if (array == null) {
- //Slog.v(TAG, "Creating new array for " + name);
- array = new ArrayList<F>();
- mWildTypeToFilter.put(baseName, array);
- }
- array.add(filter);
- }
- }
-
- return num;
- }
-
- private final int unregister_mime_types(F filter, String prefix) {
- final Iterator<String> i = filter.typesIterator();
- if (i == null) {
- return 0;
- }
-
- int num = 0;
- while (i.hasNext()) {
- String name = i.next();
- num++;
- if (localLOGV) Slog.v(TAG, prefix + name);
- String baseName = name;
- final int slashpos = name.indexOf('/');
- if (slashpos > 0) {
- baseName = name.substring(0, slashpos).intern();
- } else {
- name = name + "/*";
- }
-
- if (!remove_all_objects(mTypeToFilter.get(name), filter)) {
- mTypeToFilter.remove(name);
- }
-
- if (slashpos > 0) {
- if (!remove_all_objects(mBaseTypeToFilter.get(baseName), filter)) {
- mBaseTypeToFilter.remove(baseName);
- }
- } else {
- if (!remove_all_objects(mWildTypeToFilter.get(baseName), filter)) {
- mWildTypeToFilter.remove(baseName);
- }
- }
- }
- return num;
- }
-
- private final int register_intent_filter(F filter, Iterator<String> i,
- HashMap<String, ArrayList<F>> dest, String prefix) {
- if (i == null) {
- return 0;
- }
-
- int num = 0;
- while (i.hasNext()) {
- String name = i.next();
- num++;
- if (localLOGV) Slog.v(TAG, prefix + name);
- ArrayList<F> array = dest.get(name);
- if (array == null) {
- //Slog.v(TAG, "Creating new array for " + name);
- array = new ArrayList<F>();
- dest.put(name, array);
- }
- array.add(filter);
- }
- return num;
- }
-
- private final int unregister_intent_filter(F filter, Iterator<String> i,
- HashMap<String, ArrayList<F>> dest, String prefix) {
- if (i == null) {
- return 0;
- }
-
- int num = 0;
- while (i.hasNext()) {
- String name = i.next();
- num++;
- if (localLOGV) Slog.v(TAG, prefix + name);
- if (!remove_all_objects(dest.get(name), filter)) {
- dest.remove(name);
- }
- }
- return num;
- }
-
- private final boolean remove_all_objects(List<F> list, Object object) {
- if (list != null) {
- int N = list.size();
- for (int idx=0; idx<N; idx++) {
- if (list.get(idx) == object) {
- list.remove(idx);
- idx--;
- N--;
- }
- }
- return N > 0;
- }
- return false;
- }
-
- private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) {
- final Set<String> categories = intent.getCategories();
- if (categories == null) {
- return null;
- }
- return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));
- }
-
- private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
- boolean debug, boolean defaultOnly,
- String resolvedType, String scheme, List<F> src, List<R> dest, int userId) {
- final String action = intent.getAction();
- final Uri data = intent.getData();
- final String packageName = intent.getPackage();
-
- final boolean excludingStopped = intent.isExcludingStopped();
-
- final int N = src != null ? src.size() : 0;
- boolean hasNonDefaults = false;
- int i;
- for (i=0; i<N; i++) {
- F filter = src.get(i);
- int match;
- if (debug) Slog.v(TAG, "Matching against filter " + filter);
-
- if (excludingStopped && isFilterStopped(filter, userId)) {
- if (debug) {
- Slog.v(TAG, " Filter's target is stopped; skipping");
- }
- continue;
- }
-
- // Is delivery being limited to filters owned by a particular package?
- if (packageName != null && !isPackageForFilter(packageName, filter)) {
- if (debug) {
- Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
- }
- continue;
- }
-
- // Do we already have this one?
- if (!allowFilterResult(filter, dest)) {
- if (debug) {
- Slog.v(TAG, " Filter's target already added");
- }
- continue;
- }
-
- match = filter.match(action, resolvedType, scheme, data, categories, TAG);
- if (match >= 0) {
- if (debug) Slog.v(TAG, " Filter matched! match=0x" +
- Integer.toHexString(match));
- if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
- final R oneResult = newResult(filter, match, userId);
- if (oneResult != null) {
- dest.add(oneResult);
- }
- } else {
- hasNonDefaults = true;
- }
- } else {
- if (debug) {
- String reason;
- switch (match) {
- case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
- case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
- case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
- case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
- default: reason = "unknown reason"; break;
- }
- Slog.v(TAG, " Filter did not match: " + reason);
- }
- }
- }
-
- if (dest.size() == 0 && hasNonDefaults) {
- Slog.w(TAG, "resolveIntent failed: found match, but none with Intent.CATEGORY_DEFAULT");
- }
- }
-
- // Sorts a List of IntentFilter objects into descending priority order.
- @SuppressWarnings("rawtypes")
- private static final Comparator mResolvePrioritySorter = new Comparator() {
- public int compare(Object o1, Object o2) {
- final int q1 = ((IntentFilter) o1).getPriority();
- final int q2 = ((IntentFilter) o2).getPriority();
- return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0);
- }
- };
-
- /**
- * All filters that have been registered.
- */
- final HashSet<F> mFilters = new HashSet<F>();
-
- /**
- * All of the MIME types that have been registered, such as "image/jpeg",
- * "image/*", or "{@literal *}/*".
- */
- final HashMap<String, ArrayList<F>> mTypeToFilter
- = new HashMap<String, ArrayList<F>>();
-
- /**
- * The base names of all of all fully qualified MIME types that have been
- * registered, such as "image" or "*". Wild card MIME types such as
- * "image/*" will not be here.
- */
- final HashMap<String, ArrayList<F>> mBaseTypeToFilter
- = new HashMap<String, ArrayList<F>>();
-
- /**
- * The base names of all of the MIME types with a sub-type wildcard that
- * have been registered. For example, a filter with "image/*" will be
- * included here as "image" but one with "image/jpeg" will not be
- * included here. This also includes the "*" for the "{@literal *}/*"
- * MIME type.
- */
- final HashMap<String, ArrayList<F>> mWildTypeToFilter
- = new HashMap<String, ArrayList<F>>();
-
- /**
- * All of the URI schemes (such as http) that have been registered.
- */
- final HashMap<String, ArrayList<F>> mSchemeToFilter
- = new HashMap<String, ArrayList<F>>();
-
- /**
- * All of the actions that have been registered, but only those that did
- * not specify data.
- */
- final HashMap<String, ArrayList<F>> mActionToFilter
- = new HashMap<String, ArrayList<F>>();
-
- /**
- * All of the actions that have been registered and specified a MIME type.
- */
- final HashMap<String, ArrayList<F>> mTypedActionToFilter
- = new HashMap<String, ArrayList<F>>();
-}
-
diff --git a/services/java/com/android/server/IoThread.java b/services/java/com/android/server/IoThread.java
new file mode 100644
index 0000000..09f2af7
--- /dev/null
+++ b/services/java/com/android/server/IoThread.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+/**
+ * Shared singleton I/O thread for the system. This is a thread for non-background
+ * service operations that can potential block briefly on network IO operations
+ * (not waiting for data itself, but communicating with network daemons).
+ */
+public final class IoThread extends HandlerThread {
+ private static IoThread sInstance;
+ private static Handler sHandler;
+
+ private IoThread() {
+ super("android.io", android.os.Process.THREAD_PRIORITY_DEFAULT);
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new IoThread();
+ sInstance.start();
+ sHandler = new Handler(sInstance.getLooper());
+ sHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ android.os.Process.setCanSelfBackground(false);
+ }
+ });
+ }
+ }
+
+ public static IoThread get() {
+ synchronized (IoThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (IoThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index f784030..c6c9845 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -47,7 +47,6 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -63,6 +62,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
+import com.android.internal.os.BackgroundThread;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceProxy;
import com.android.server.location.GeofenceManager;
@@ -93,7 +93,6 @@
public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
private static final String WAKELOCK_KEY = TAG;
- private static final String THREAD_NAME = TAG;
// Location resolution level: no location data whatsoever
private static final int RESOLUTION_LEVEL_NONE = 0;
@@ -143,7 +142,6 @@
private LocationWorkerHandler mLocationHandler;
private PassiveProvider mPassiveProvider; // track passive provider for special cases
private LocationBlacklist mBlacklist;
- private HandlerThread mHandlerThread;
// --- fields below are protected by mLock ---
// Set of providers that are explicitly enabled
@@ -211,9 +209,7 @@
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
// prepare worker thread
- mHandlerThread = new HandlerThread(THREAD_NAME, Process.THREAD_PRIORITY_BACKGROUND);
- mHandlerThread.start();
- mLocationHandler = new LocationWorkerHandler(mHandlerThread.getLooper());
+ mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
// prepare mLocationHandler's dependents
mLocationFudger = new LocationFudger(mContext, mLocationHandler);
@@ -225,6 +221,9 @@
AppOpsManager.Callback callback = new AppOpsManager.Callback() {
public void opChanged(int op, String packageName) {
synchronized (mLock) {
+ for (Receiver receiver : mReceivers.values()) {
+ receiver.updateMonitoring(true);
+ }
applyAllProviderRequirementsLocked();
}
}
@@ -463,6 +462,7 @@
final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
+ boolean mOpMonitoring;
int mPendingBroadcasts;
PowerManager.WakeLock mWakeLock;
@@ -480,6 +480,8 @@
mPid = pid;
mPackageName = packageName;
+ updateMonitoring(true);
+
// construct/configure wakelock
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
mWakeLock.setWorkSource(new WorkSource(mUid, mPackageName));
@@ -515,6 +517,21 @@
return s.toString();
}
+ public void updateMonitoring(boolean allow) {
+ if (!mOpMonitoring) {
+ if (allow) {
+ mOpMonitoring = mAppOps.startOpNoThrow(AppOpsManager.OP_MONITOR_LOCATION,
+ mUid, mPackageName) == AppOpsManager.MODE_ALLOWED;
+ }
+ } else {
+ if (!allow || mAppOps.checkOpNoThrow(AppOpsManager.OP_MONITOR_LOCATION,
+ mUid, mPackageName) != AppOpsManager.MODE_ALLOWED) {
+ mAppOps.finishOp(AppOpsManager.OP_MONITOR_LOCATION, mUid, mPackageName);
+ mOpMonitoring = false;
+ }
+ }
+ }
+
public boolean isListener() {
return mListener != null;
}
@@ -1369,6 +1386,8 @@
}
}
+ receiver.updateMonitoring(false);
+
// Record which providers were associated with this listener
HashSet<String> providers = new HashSet<String>();
HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
diff --git a/services/java/com/android/server/LockSettingsService.java b/services/java/com/android/server/LockSettingsService.java
index d52a8e2..e28a258 100644
--- a/services/java/com/android/server/LockSettingsService.java
+++ b/services/java/com/android/server/LockSettingsService.java
@@ -57,6 +57,7 @@
*/
public class LockSettingsService extends ILockSettings.Stub {
+ private static final String PERMISSION = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE";
private final DatabaseHelper mOpenHelper;
private static final String TAG = "LockSettingsService";
@@ -143,20 +144,12 @@
}
}
- private static final void checkWritePermission(int userId) {
- final int callingUid = Binder.getCallingUid();
- if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
- throw new SecurityException("uid=" + callingUid
- + " not authorized to write lock settings");
- }
+ private final void checkWritePermission(int userId) {
+ mContext.checkCallingOrSelfPermission(PERMISSION);
}
- private static final void checkPasswordReadPermission(int userId) {
- final int callingUid = Binder.getCallingUid();
- if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
- throw new SecurityException("uid=" + callingUid
- + " not authorized to read lock password");
- }
+ private final void checkPasswordReadPermission(int userId) {
+ mContext.checkCallingOrSelfPermission(PERMISSION);
}
private final void checkReadPermission(String requestedKey, int userId) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index e670ef9..83739f5 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -492,7 +492,6 @@
}
};
- private final HandlerThread mHandlerThread;
private final Handler mHandler;
void waitForAsecScan() {
@@ -828,7 +827,7 @@
}
if (code == VoldResponseCode.VolumeDiskInserted) {
- new Thread() {
+ new Thread("MountService#VolumeDiskInserted") {
@Override
public void run() {
try {
@@ -1115,7 +1114,7 @@
/*
* USB mass storage disconnected while enabled
*/
- new Thread() {
+ new Thread("MountService#AvailabilityChange") {
@Override
public void run() {
try {
@@ -1314,9 +1313,9 @@
// XXX: This will go away soon in favor of IMountServiceObserver
mPms = (PackageManagerService) ServiceManager.getService("package");
- mHandlerThread = new HandlerThread("MountService");
- mHandlerThread.start();
- mHandler = new MountServiceHandler(mHandlerThread.getLooper());
+ HandlerThread hthread = new HandlerThread(TAG);
+ hthread.start();
+ mHandler = new MountServiceHandler(hthread.getLooper());
// Watch for user changes
final IntentFilter userFilter = new IntentFilter();
@@ -1338,7 +1337,7 @@
idleMaintenanceFilter, null, mHandler);
// Add OBB Action Handler to MountService thread.
- mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
+ mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
/*
* Create the connection to vold with a maximum queue of twice the
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 0a91919..a9942e3 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -19,7 +19,6 @@
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
import android.util.LocalLog;
@@ -81,9 +80,7 @@
@Override
public void run() {
- HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
- thread.start();
- mCallbackHandler = new Handler(thread.getLooper(), this);
+ mCallbackHandler = new Handler(FgThread.get().getLooper(), this);
while (true) {
try {
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 3b84c732..6f740cd0 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -43,18 +43,21 @@
import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import com.android.internal.app.IBatteryStats;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnector.Command;
@@ -343,6 +346,14 @@
SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
+ if (mBandwidthControlEnabled) {
+ try {
+ IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME))
+ .noteNetworkStatsEnabled();
+ } catch (RemoteException e) {
+ }
+ }
+
// push any existing quota or UID rules
synchronized (mQuotaLock) {
int size = mActiveQuotas.size();
@@ -990,7 +1001,8 @@
mConnector.execute("softap", "set", wlanIface);
} else {
mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
- getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey));
+ "broadcast", "6", getSecurityType(wifiConfig),
+ new SensitiveArg(wifiConfig.preSharedKey));
}
mConnector.execute("softap", "startap");
} catch (NativeDaemonConnectorException e) {
@@ -1039,7 +1051,8 @@
mConnector.execute("softap", "set", wlanIface);
} else {
mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
- getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey));
+ "broadcast", "6", getSecurityType(wifiConfig),
+ new SensitiveArg(wifiConfig.preSharedKey));
}
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
@@ -1366,6 +1379,79 @@
}
@Override
+ public void setUidRangeRoute(String iface, int uid_start, int uid_end) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("interface", "route",
+ "uid", "add", iface, uid_start, uid_end);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void clearUidRangeRoute(String iface, int uid_start, int uid_end) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("interface", "route",
+ "uid", "remove", iface, uid_start, uid_end);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void setMarkedForwarding(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("interface", "route", "fwmark", "add", iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void clearMarkedForwarding(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("interface", "route", "fwmark", "remove", iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void setDnsInterfaceForUidRange(String iface, int uid_start, int uid_end) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("resolver", "setifaceforuidrange", iface, uid_start, uid_end);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void clearDnsInterfaceForUidRange(int uid_start, int uid_end) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("resolver", "clearifaceforuidrange", uid_start, uid_end);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void clearDnsInterfaceMaps() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("resolver", "clearifacemapping");
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+
+ @Override
public void flushDefaultDnsCache() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index 3bfd190..02b42b8 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -27,7 +27,6 @@
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
@@ -36,6 +35,7 @@
import android.util.NtpTrustedTime;
import android.util.TrustedTime;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.TelephonyIntents;
/**
@@ -71,7 +71,6 @@
// NTP lookup is done on this thread and handler
private Handler mHandler;
- private HandlerThread mThread;
private AlarmManager mAlarmManager;
private PendingIntent mPendingPollIntent;
private SettingsObserver mSettingsObserver;
@@ -114,9 +113,7 @@
registerForAlarms();
registerForConnectivityIntents();
- mThread = new HandlerThread(TAG);
- mThread.start();
- mHandler = new MyHandler(mThread.getLooper());
+ mHandler = new MyHandler(BackgroundThread.get().getLooper());
// Check the network time on the new thread
mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 29aaeaf..c98a85a 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -1476,7 +1476,7 @@
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try {
record.callback.show();
- scheduleTimeoutLocked(record, false);
+ scheduleTimeoutLocked(record);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Object died trying to show notification " + record.callback
@@ -1516,12 +1516,14 @@
}
}
- private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
+ private void scheduleTimeoutLocked(ToastRecord r)
{
- Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
- long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
mHandler.removeCallbacksAndMessages(r);
- mHandler.sendMessageDelayed(m, delay);
+ if (r.duration != Toast.LENGTH_INFINITE) {
+ Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
+ long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
+ mHandler.sendMessageDelayed(m, delay);
+ }
}
private void handleTimeout(ToastRecord record)
@@ -1599,8 +1601,10 @@
// Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
// uid/pid of another application)
- public void enqueueNotificationInternal(String pkg, String basePkg, int callingUid,
- int callingPid, String tag, int id, Notification notification, int[] idOut, int userId)
+
+ public void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid,
+ final int callingPid, final String tag, final int id, final Notification notification,
+ int[] idOut, int incomingUserId)
{
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
@@ -1608,8 +1612,8 @@
checkCallerIsSystemOrSameApp(pkg);
final boolean isSystemNotification = isCallerSystem() || ("android".equals(pkg));
- userId = ActivityManager.handleIncomingUser(callingPid,
- callingUid, userId, true, false, "enqueueNotification", pkg);
+ final int userId = ActivityManager.handleIncomingUser(callingPid,
+ callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
final UserHandle user = new UserHandle(userId);
// Limit the number of notifications that any given package except the android
@@ -1651,244 +1655,267 @@
}
}
- // === Scoring ===
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
- // 0. Sanitize inputs
- notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
- // Migrate notification flags to scores
- if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
- if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
- } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
- if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
- }
+ // === Scoring ===
- // 1. initial score: buckets of 10, around the app
- int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
-
- // 2. Consult external heuristics (TBD)
-
- // 3. Apply local rules
-
- // blocked apps
- if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
- if (!isSystemNotification) {
- score = JUNK_SCORE;
- Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
- }
- }
-
- if (DBG) {
- Slog.v(TAG, "Assigned score=" + score + " to " + notification);
- }
-
- if (score < SCORE_DISPLAY_THRESHOLD) {
- // Notification will be blocked because the score is too low.
- return;
- }
-
- // Should this notification make noise, vibe, or use the LED?
- final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
-
- synchronized (mNotificationList) {
- final StatusBarNotification n = new StatusBarNotification(
- pkg, id, tag, callingUid, callingPid, score, notification, user);
- NotificationRecord r = new NotificationRecord(n);
- NotificationRecord old = null;
-
- int index = indexOfNotificationLocked(pkg, tag, id, userId);
- if (index < 0) {
- mNotificationList.add(r);
- } else {
- old = mNotificationList.remove(index);
- mNotificationList.add(index, r);
- // Make sure we don't lose the foreground service state.
- if (old != null) {
- notification.flags |=
- old.getNotification().flags&Notification.FLAG_FOREGROUND_SERVICE;
- }
- }
-
- // Ensure if this is a foreground service that the proper additional
- // flags are set.
- if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- notification.flags |= Notification.FLAG_ONGOING_EVENT
- | Notification.FLAG_NO_CLEAR;
- }
-
- final int currentUser;
- final long token = Binder.clearCallingIdentity();
- try {
- currentUser = ActivityManager.getCurrentUser();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- if (notification.icon != 0) {
- if (old != null && old.statusBarKey != null) {
- r.statusBarKey = old.statusBarKey;
- long identity = Binder.clearCallingIdentity();
- try {
- mStatusBar.updateNotification(r.statusBarKey, n);
+ // 0. Sanitize inputs
+ notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
+ Notification.PRIORITY_MAX);
+ // Migrate notification flags to scores
+ if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
+ if (notification.priority < Notification.PRIORITY_MAX) {
+ notification.priority = Notification.PRIORITY_MAX;
}
- finally {
- Binder.restoreCallingIdentity(identity);
- }
- } else {
- long identity = Binder.clearCallingIdentity();
- try {
- r.statusBarKey = mStatusBar.addNotification(n);
- if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
- && canInterrupt) {
- mAttentionLight.pulse();
- }
- }
- finally {
- Binder.restoreCallingIdentity(identity);
+ } else if (SCORE_ONGOING_HIGHER &&
+ 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
+ if (notification.priority < Notification.PRIORITY_HIGH) {
+ notification.priority = Notification.PRIORITY_HIGH;
}
}
- // Send accessibility events only for the current user.
- if (currentUser == userId) {
- sendAccessibilityEvent(notification, pkg);
- }
- notifyPostedLocked(r);
- } else {
- Slog.e(TAG, "Not posting notification with icon==0: " + notification);
- if (old != null && old.statusBarKey != null) {
- long identity = Binder.clearCallingIdentity();
- try {
- mStatusBar.removeNotification(old.statusBarKey);
+ // 1. initial score: buckets of 10, around the app
+ int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
+
+ // 2. Consult external heuristics (TBD)
+
+ // 3. Apply local rules
+
+ // blocked apps
+ if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
+ if (!isSystemNotification) {
+ score = JUNK_SCORE;
+ Slog.e(TAG, "Suppressing notification from package " + pkg
+ + " by user request.");
}
- finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- notifyRemovedLocked(r);
- }
- // ATTENTION: in a future release we will bail out here
- // so that we do not play sounds, show lights, etc. for invalid notifications
- Slog.e(TAG, "WARNING: In a future release this will crash the app: " + n.getPackageName());
- }
-
- // If we're not supposed to beep, vibrate, etc. then don't.
- if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
- && (!(old != null
- && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
- && (r.getUserId() == UserHandle.USER_ALL ||
- (r.getUserId() == userId && r.getUserId() == currentUser))
- && canInterrupt
- && mSystemReady) {
-
- final AudioManager audioManager = (AudioManager) mContext
- .getSystemService(Context.AUDIO_SERVICE);
-
- // sound
-
- // should we use the default notification sound? (indicated either by DEFAULT_SOUND
- // or because notification.sound is pointing at Settings.System.NOTIFICATION_SOUND)
- final boolean useDefaultSound =
- (notification.defaults & Notification.DEFAULT_SOUND) != 0
- || Settings.System.DEFAULT_NOTIFICATION_URI.equals(notification.sound);
-
- Uri soundUri = null;
- boolean hasValidSound = false;
-
- if (useDefaultSound) {
- soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
-
- // check to see if the default notification sound is silent
- ContentResolver resolver = mContext.getContentResolver();
- hasValidSound = Settings.System.getString(resolver,
- Settings.System.NOTIFICATION_SOUND) != null;
- } else if (notification.sound != null) {
- soundUri = notification.sound;
- hasValidSound = (soundUri != null);
}
- if (hasValidSound) {
- boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
- int audioStreamType;
- if (notification.audioStreamType >= 0) {
- audioStreamType = notification.audioStreamType;
+ if (DBG) {
+ Slog.v(TAG, "Assigned score=" + score + " to " + notification);
+ }
+
+ if (score < SCORE_DISPLAY_THRESHOLD) {
+ // Notification will be blocked because the score is too low.
+ return;
+ }
+
+ // Should this notification make noise, vibe, or use the LED?
+ final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
+
+ synchronized (mNotificationList) {
+ final StatusBarNotification n = new StatusBarNotification(
+ pkg, id, tag, callingUid, callingPid, score, notification, user);
+ NotificationRecord r = new NotificationRecord(n);
+ NotificationRecord old = null;
+
+ int index = indexOfNotificationLocked(pkg, tag, id, userId);
+ if (index < 0) {
+ mNotificationList.add(r);
} else {
- audioStreamType = DEFAULT_STREAM_TYPE;
+ old = mNotificationList.remove(index);
+ mNotificationList.add(index, r);
+ // Make sure we don't lose the foreground service state.
+ if (old != null) {
+ notification.flags |=
+ old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+ }
}
- mSoundNotification = r;
- // do not play notifications if stream volume is 0
- // (typically because ringer mode is silent) or if speech recognition is active.
- if ((audioManager.getStreamVolume(audioStreamType) != 0)
- && !audioManager.isSpeechRecognitionActive()) {
- final long identity = Binder.clearCallingIdentity();
- try {
- final IRingtonePlayer player = mAudioService.getRingtonePlayer();
- if (player != null) {
- player.playAsync(soundUri, user, looping, audioStreamType);
+
+ // Ensure if this is a foreground service that the proper additional
+ // flags are set.
+ if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ notification.flags |= Notification.FLAG_ONGOING_EVENT
+ | Notification.FLAG_NO_CLEAR;
+ }
+
+ final int currentUser;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ currentUser = ActivityManager.getCurrentUser();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ if (notification.icon != 0) {
+ if (old != null && old.statusBarKey != null) {
+ r.statusBarKey = old.statusBarKey;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mStatusBar.updateNotification(r.statusBarKey, n);
}
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(identity);
+ finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ r.statusBarKey = mStatusBar.addNotification(n);
+ if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
+ && canInterrupt) {
+ mAttentionLight.pulse();
+ }
+ }
+ finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ // Send accessibility events only for the current user.
+ if (currentUser == userId) {
+ sendAccessibilityEvent(notification, pkg);
+ }
+
+ notifyPostedLocked(r);
+ } else {
+ Slog.e(TAG, "Not posting notification with icon==0: " + notification);
+ if (old != null && old.statusBarKey != null) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mStatusBar.removeNotification(old.statusBarKey);
+ }
+ finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ notifyRemovedLocked(r);
+ }
+ // ATTENTION: in a future release we will bail out here
+ // so that we do not play sounds, show lights, etc. for invalid notifications
+ Slog.e(TAG, "WARNING: In a future release this will crash the app: "
+ + n.getPackageName());
+ }
+
+ // If we're not supposed to beep, vibrate, etc. then don't.
+ if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
+ && (!(old != null
+ && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
+ && (r.getUserId() == UserHandle.USER_ALL ||
+ (r.getUserId() == userId && r.getUserId() == currentUser))
+ && canInterrupt
+ && mSystemReady) {
+
+ final AudioManager audioManager = (AudioManager) mContext
+ .getSystemService(Context.AUDIO_SERVICE);
+
+ // sound
+
+ // should we use the default notification sound? (indicated either by
+ // DEFAULT_SOUND or because notification.sound is pointing at
+ // Settings.System.NOTIFICATION_SOUND)
+ final boolean useDefaultSound =
+ (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
+ Settings.System.DEFAULT_NOTIFICATION_URI
+ .equals(notification.sound);
+
+ Uri soundUri = null;
+ boolean hasValidSound = false;
+
+ if (useDefaultSound) {
+ soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+
+ // check to see if the default notification sound is silent
+ ContentResolver resolver = mContext.getContentResolver();
+ hasValidSound = Settings.System.getString(resolver,
+ Settings.System.NOTIFICATION_SOUND) != null;
+ } else if (notification.sound != null) {
+ soundUri = notification.sound;
+ hasValidSound = (soundUri != null);
+ }
+
+ if (hasValidSound) {
+ boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
+ int audioStreamType;
+ if (notification.audioStreamType >= 0) {
+ audioStreamType = notification.audioStreamType;
+ } else {
+ audioStreamType = DEFAULT_STREAM_TYPE;
+ }
+ mSoundNotification = r;
+ // do not play notifications if stream volume is 0 (typically because
+ // ringer mode is silent) or if speech recognition is active.
+ if ((audioManager.getStreamVolume(audioStreamType) != 0)
+ && !audioManager.isSpeechRecognitionActive()) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player = mAudioService.getRingtonePlayer();
+ if (player != null) {
+ player.playAsync(soundUri, user, looping, audioStreamType);
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ // vibrate
+ // Does the notification want to specify its own vibration?
+ final boolean hasCustomVibrate = notification.vibrate != null;
+
+ // new in 4.2: if there was supposed to be a sound and we're in vibrate
+ // mode, and no other vibration is specified, we fall back to vibration
+ final boolean convertSoundToVibration =
+ !hasCustomVibrate
+ && hasValidSound
+ && (audioManager.getRingerMode()
+ == AudioManager.RINGER_MODE_VIBRATE);
+
+ // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
+ final boolean useDefaultVibrate =
+ (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
+
+ if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
+ && !(audioManager.getRingerMode()
+ == AudioManager.RINGER_MODE_SILENT)) {
+ mVibrateNotification = r;
+
+ if (useDefaultVibrate || convertSoundToVibration) {
+ // Escalate privileges so we can use the vibrator even if the
+ // notifying app does not have the VIBRATE permission.
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
+ useDefaultVibrate ? mDefaultVibrationPattern
+ : mFallbackVibrationPattern,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else if (notification.vibrate.length > 1) {
+ // If you want your own vibration pattern, you need the VIBRATE
+ // permission
+ mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
+ notification.vibrate,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1);
+ }
+ }
+ }
+
+ // light
+ // the most recent thing gets the light
+ mLights.remove(old);
+ if (mLedNotification == old) {
+ mLedNotification = null;
+ }
+ //Slog.i(TAG, "notification.lights="
+ // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
+ // != 0));
+ if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
+ && canInterrupt) {
+ mLights.add(r);
+ updateLightsLocked();
+ } else {
+ if (old != null
+ && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
+ updateLightsLocked();
}
}
}
-
- // vibrate
- // Does the notification want to specify its own vibration?
- final boolean hasCustomVibrate = notification.vibrate != null;
-
- // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,
- // and no other vibration is specified, we fall back to vibration
- final boolean convertSoundToVibration =
- !hasCustomVibrate
- && hasValidSound
- && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
-
- // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
- final boolean useDefaultVibrate =
- (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
-
- if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
- && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {
- mVibrateNotification = r;
-
- if (useDefaultVibrate || convertSoundToVibration) {
- // Escalate privileges so we can use the vibrator even if the notifying app
- // does not have the VIBRATE permission.
- long identity = Binder.clearCallingIdentity();
- try {
- mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
- useDefaultVibrate ? mDefaultVibrationPattern
- : mFallbackVibrationPattern,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } else if (notification.vibrate.length > 1) {
- // If you want your own vibration pattern, you need the VIBRATE permission
- mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(), notification.vibrate,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
- }
- }
}
-
- // light
- // the most recent thing gets the light
- mLights.remove(old);
- if (mLedNotification == old) {
- mLedNotification = null;
- }
- //Slog.i(TAG, "notification.lights="
- // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
- if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
- && canInterrupt) {
- mLights.add(r);
- updateLightsLocked();
- } else {
- if (old != null
- && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
- updateLightsLocked();
- }
- }
- }
+ });
idOut[0] = id;
}
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java
index faa72a2..e0f415b 100644
--- a/services/java/com/android/server/NsdService.java
+++ b/services/java/com/android/server/NsdService.java
@@ -417,7 +417,15 @@
int keyId = clientInfo.mClientIds.indexOfValue(id);
if (keyId != -1) {
clientId = clientInfo.mClientIds.keyAt(keyId);
+ } else {
+ // This can happen because of race conditions. For example,
+ // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
+ // and we may get in this situation.
+ Slog.d(TAG, "Notification for a listener that is no longer active: " + id);
+ handled = false;
+ return handled;
}
+
switch (code) {
case NativeResponseCode.SERVICE_FOUND:
/* NNN uniqueId serviceName regType domain */
diff --git a/services/java/com/android/server/ProcessMap.java b/services/java/com/android/server/ProcessMap.java
index 6b26403..20c97ba 100644
--- a/services/java/com/android/server/ProcessMap.java
+++ b/services/java/com/android/server/ProcessMap.java
@@ -16,13 +16,12 @@
package com.android.server;
+import android.util.ArrayMap;
import android.util.SparseArray;
-import java.util.HashMap;
-
public class ProcessMap<E> {
- final HashMap<String, SparseArray<E>> mMap
- = new HashMap<String, SparseArray<E>>();
+ final ArrayMap<String, SparseArray<E>> mMap
+ = new ArrayMap<String, SparseArray<E>>();
public E get(String name, int uid) {
SparseArray<E> uids = mMap.get(name);
@@ -50,7 +49,7 @@
}
}
- public HashMap<String, SparseArray<E>> getMap() {
+ public ArrayMap<String, SparseArray<E>> getMap() {
return mMap;
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9455017..acfc096 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -44,6 +44,7 @@
import android.util.Slog;
import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
import com.android.server.accessibility.AccessibilityManagerService;
@@ -62,6 +63,7 @@
import com.android.server.pm.UserManagerService;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
+import com.android.server.print.PrintManagerService;
import com.android.server.search.SearchManagerService;
import com.android.server.usb.UsbService;
import com.android.server.wifi.WifiService;
@@ -74,7 +76,7 @@
import java.util.Timer;
import java.util.TimerTask;
-class ServerThread extends Thread {
+class ServerThread {
private static final String TAG = "SystemServer";
private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
private static final String ENCRYPTED_STATE = "1";
@@ -86,8 +88,7 @@
Log.wtf(TAG, "BOOT FAILURE " + msg, e);
}
- @Override
- public void run() {
+ public void initAndLoop() {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,
SystemClock.uptimeMillis());
@@ -154,30 +155,6 @@
InputManagerService inputManager = null;
TelephonyRegistry telephonyRegistry = null;
- // Create a shared handler thread for UI within the system server.
- // This thread is used by at least the following components:
- // - WindowManagerPolicy
- // - KeyguardViewManager
- // - DisplayManagerService
- HandlerThread uiHandlerThread = new HandlerThread("UI");
- uiHandlerThread.start();
- Handler uiHandler = new Handler(uiHandlerThread.getLooper());
- uiHandler.post(new Runnable() {
- @Override
- public void run() {
- //Looper.myLooper().setMessageLogging(new LogPrinter(
- // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_FOREGROUND);
- android.os.Process.setCanSelfBackground(false);
-
- // For debug builds, log event loop stalls to dropbox for analysis.
- if (StrictMode.conditionallyEnableDebugLogging()) {
- Slog.i(TAG, "Enabled StrictMode logging for UI Looper");
- }
- }
- });
-
// Create a handler thread just for the window manager to enjoy.
HandlerThread wmHandlerThread = new HandlerThread("WindowManager");
wmHandlerThread.start();
@@ -198,8 +175,9 @@
}
});
- // Critical services...
+ // bootstrap services
boolean onlyCore = false;
+ boolean firstBoot = false;
try {
// Wait for installd to finished starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
@@ -214,9 +192,23 @@
Slog.i(TAG, "Activity Manager");
context = ActivityManagerService.main(factoryTest);
+ } catch (RuntimeException e) {
+ Slog.e("System", "******************************************");
+ Slog.e("System", "************ Failure starting bootstrap service", e);
+ }
+ boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
+ boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
+ boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
+ boolean disableTelephony = SystemProperties.getBoolean("config.disable_telephony", false);
+ boolean disableLocation = SystemProperties.getBoolean("config.disable_location", false);
+ boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
+ boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
+ boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
+
+ try {
Slog.i(TAG, "Display Manager");
- display = new DisplayManagerService(context, wmHandler, uiHandler);
+ display = new DisplayManagerService(context, wmHandler);
ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
Slog.i(TAG, "Telephony Registry");
@@ -248,7 +240,6 @@
pm = PackageManagerService.main(context, installer,
factoryTest != SystemServer.FACTORY_TEST_OFF,
onlyCore);
- boolean firstBoot = false;
try {
firstBoot = pm.isFirstBoot();
} catch (RemoteException e) {
@@ -267,6 +258,7 @@
// The AccountManager must come before the ContentService
try {
+ // TODO: seems like this should be disable-able, but req'd by ContentService
Slog.i(TAG, "Account Manager");
accountManager = new AccountManagerService(context);
ServiceManager.addService(Context.ACCOUNT_SERVICE, accountManager);
@@ -295,7 +287,8 @@
// only initialize the power service after we have started the
// lights service, content providers and the battery service.
power.init(context, lights, ActivityManagerService.self(), battery,
- BatteryStatsService.getService(), display);
+ BatteryStatsService.getService(),
+ ActivityManagerService.self().getAppOpsService(), display);
Slog.i(TAG, "Alarm Manager");
alarm = new AlarmManagerService(context);
@@ -304,14 +297,14 @@
Slog.i(TAG, "Init Watchdog");
Watchdog.getInstance().init(context, battery, power, alarm,
ActivityManagerService.self());
+ Watchdog.getInstance().addThread(wmHandler, "WindowManager thread");
Slog.i(TAG, "Input Manager");
inputManager = new InputManagerService(context, wmHandler);
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power, display, inputManager,
- uiHandler, wmHandler,
- factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
+ wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
@@ -334,12 +327,13 @@
} else if (!context.getPackageManager().hasSystemFeature
(PackageManager.FEATURE_BLUETOOTH)) {
Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
+ } else if (disableBluetooth) {
+ Slog.i(TAG, "Bluetooth Service disabled by config");
} else {
Slog.i(TAG, "Bluetooth Manager Service");
bluetooth = new BluetoothManagerService(context);
ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);
}
-
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
@@ -356,23 +350,27 @@
TextServicesManagerService tsms = null;
LockSettingsService lockSettings = null;
DreamManagerService dreamy = null;
+ AssetAtlasService atlas = null;
// Bring up services needed for UI.
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- try {
- Slog.i(TAG, "Input Method Service");
- imm = new InputMethodManagerService(context, wm);
- ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
- } catch (Throwable e) {
- reportWtf("starting Input Manager Service", e);
- }
+ //if (!disableNonCoreServices) { // TODO: View depends on these; mock them?
+ if (true) {
+ try {
+ Slog.i(TAG, "Input Method Service");
+ imm = new InputMethodManagerService(context, wm);
+ ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
+ } catch (Throwable e) {
+ reportWtf("starting Input Manager Service", e);
+ }
- try {
- Slog.i(TAG, "Accessibility Manager");
- ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
- new AccessibilityManagerService(context));
- } catch (Throwable e) {
- reportWtf("starting Accessibility Manager", e);
+ try {
+ Slog.i(TAG, "Accessibility Manager");
+ ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
+ new AccessibilityManagerService(context));
+ } catch (Throwable e) {
+ reportWtf("starting Accessibility Manager", e);
+ }
}
}
@@ -397,7 +395,8 @@
}
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+ if (!disableStorage &&
+ !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
try {
/*
* NotificationManagerService is dependant on MountService,
@@ -411,116 +410,130 @@
}
}
- try {
- Slog.i(TAG, "LockSettingsService");
- lockSettings = new LockSettingsService(context);
- ServiceManager.addService("lock_settings", lockSettings);
- } catch (Throwable e) {
- reportWtf("starting LockSettingsService service", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "LockSettingsService");
+ lockSettings = new LockSettingsService(context);
+ ServiceManager.addService("lock_settings", lockSettings);
+ } catch (Throwable e) {
+ reportWtf("starting LockSettingsService service", e);
+ }
+
+ try {
+ Slog.i(TAG, "Device Policy");
+ devicePolicy = new DevicePolicyManagerService(context);
+ ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
+ } catch (Throwable e) {
+ reportWtf("starting DevicePolicyService", e);
+ }
}
- try {
- Slog.i(TAG, "Device Policy");
- devicePolicy = new DevicePolicyManagerService(context);
- ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
- } catch (Throwable e) {
- reportWtf("starting DevicePolicyService", e);
+ if (!disableSystemUI) {
+ try {
+ Slog.i(TAG, "Status Bar");
+ statusBar = new StatusBarManagerService(context, wm);
+ ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
+ } catch (Throwable e) {
+ reportWtf("starting StatusBarManagerService", e);
+ }
}
- try {
- Slog.i(TAG, "Status Bar");
- statusBar = new StatusBarManagerService(context, wm);
- ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
- } catch (Throwable e) {
- reportWtf("starting StatusBarManagerService", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Clipboard Service");
+ ServiceManager.addService(Context.CLIPBOARD_SERVICE,
+ new ClipboardService(context));
+ } catch (Throwable e) {
+ reportWtf("starting Clipboard Service", e);
+ }
}
- try {
- Slog.i(TAG, "Clipboard Service");
- ServiceManager.addService(Context.CLIPBOARD_SERVICE,
- new ClipboardService(context));
- } catch (Throwable e) {
- reportWtf("starting Clipboard Service", e);
+ if (!disableNetwork) {
+ try {
+ Slog.i(TAG, "NetworkManagement Service");
+ networkManagement = NetworkManagementService.create(context);
+ ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkManagement Service", e);
+ }
}
- try {
- Slog.i(TAG, "NetworkManagement Service");
- networkManagement = NetworkManagementService.create(context);
- ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
- } catch (Throwable e) {
- reportWtf("starting NetworkManagement Service", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Text Service Manager Service");
+ tsms = new TextServicesManagerService(context);
+ ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
+ } catch (Throwable e) {
+ reportWtf("starting Text Service Manager Service", e);
+ }
}
- try {
- Slog.i(TAG, "Text Service Manager Service");
- tsms = new TextServicesManagerService(context);
- ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
- } catch (Throwable e) {
- reportWtf("starting Text Service Manager Service", e);
+ if (!disableNetwork) {
+ try {
+ Slog.i(TAG, "NetworkStats Service");
+ networkStats = new NetworkStatsService(context, networkManagement, alarm);
+ ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkStats Service", e);
+ }
+
+ try {
+ Slog.i(TAG, "NetworkPolicy Service");
+ networkPolicy = new NetworkPolicyManagerService(
+ context, ActivityManagerService.self(), power,
+ networkStats, networkManagement);
+ ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkPolicy Service", e);
+ }
+
+ try {
+ Slog.i(TAG, "Wi-Fi P2pService");
+ wifiP2p = new WifiP2pService(context);
+ ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);
+ } catch (Throwable e) {
+ reportWtf("starting Wi-Fi P2pService", e);
+ }
+
+ try {
+ Slog.i(TAG, "Wi-Fi Service");
+ wifi = new WifiService(context);
+ ServiceManager.addService(Context.WIFI_SERVICE, wifi);
+ } catch (Throwable e) {
+ reportWtf("starting Wi-Fi Service", e);
+ }
+
+ try {
+ Slog.i(TAG, "Connectivity Service");
+ connectivity = new ConnectivityService(
+ context, networkManagement, networkStats, networkPolicy);
+ ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
+ networkStats.bindConnectivityManager(connectivity);
+ networkPolicy.bindConnectivityManager(connectivity);
+ wifi.checkAndStartWifi();
+ wifiP2p.connectivityServiceReady();
+ } catch (Throwable e) {
+ reportWtf("starting Connectivity Service", e);
+ }
+
+ try {
+ Slog.i(TAG, "Network Service Discovery Service");
+ serviceDiscovery = NsdService.create(context);
+ ServiceManager.addService(
+ Context.NSD_SERVICE, serviceDiscovery);
+ } catch (Throwable e) {
+ reportWtf("starting Service Discovery Service", e);
+ }
}
- try {
- Slog.i(TAG, "NetworkStats Service");
- networkStats = new NetworkStatsService(context, networkManagement, alarm);
- ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
- } catch (Throwable e) {
- reportWtf("starting NetworkStats Service", e);
- }
-
- try {
- Slog.i(TAG, "NetworkPolicy Service");
- networkPolicy = new NetworkPolicyManagerService(
- context, ActivityManagerService.self(), power,
- networkStats, networkManagement);
- ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
- } catch (Throwable e) {
- reportWtf("starting NetworkPolicy Service", e);
- }
-
- try {
- Slog.i(TAG, "Wi-Fi P2pService");
- wifiP2p = new WifiP2pService(context);
- ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi P2pService", e);
- }
-
- try {
- Slog.i(TAG, "Wi-Fi Service");
- wifi = new WifiService(context);
- ServiceManager.addService(Context.WIFI_SERVICE, wifi);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi Service", e);
- }
-
- try {
- Slog.i(TAG, "Connectivity Service");
- connectivity = new ConnectivityService(
- context, networkManagement, networkStats, networkPolicy);
- ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
- networkStats.bindConnectivityManager(connectivity);
- networkPolicy.bindConnectivityManager(connectivity);
- wifi.checkAndStartWifi();
- wifiP2p.connectivityServiceReady();
- } catch (Throwable e) {
- reportWtf("starting Connectivity Service", e);
- }
-
- try {
- Slog.i(TAG, "Network Service Discovery Service");
- serviceDiscovery = NsdService.create(context);
- ServiceManager.addService(
- Context.NSD_SERVICE, serviceDiscovery);
- } catch (Throwable e) {
- reportWtf("starting Service Discovery Service", e);
- }
-
- try {
- Slog.i(TAG, "UpdateLock Service");
- ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
- new UpdateLockService(context));
- } catch (Throwable e) {
- reportWtf("starting UpdateLockService", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "UpdateLock Service");
+ ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
+ new UpdateLockService(context));
+ } catch (Throwable e) {
+ reportWtf("starting UpdateLockService", e);
+ }
}
/*
@@ -528,7 +541,7 @@
* AppWidget Provider. Make sure MountService is completely started
* first before continuing.
*/
- if (mountService != null) {
+ if (mountService != null && !onlyCore) {
mountService.waitForAsecScan();
}
@@ -563,28 +576,32 @@
reportWtf("starting DeviceStorageMonitor service", e);
}
- try {
- Slog.i(TAG, "Location Manager");
- location = new LocationManagerService(context);
- ServiceManager.addService(Context.LOCATION_SERVICE, location);
- } catch (Throwable e) {
- reportWtf("starting Location Manager", e);
+ if (!disableLocation) {
+ try {
+ Slog.i(TAG, "Location Manager");
+ location = new LocationManagerService(context);
+ ServiceManager.addService(Context.LOCATION_SERVICE, location);
+ } catch (Throwable e) {
+ reportWtf("starting Location Manager", e);
+ }
+
+ try {
+ Slog.i(TAG, "Country Detector");
+ countryDetector = new CountryDetectorService(context);
+ ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
+ } catch (Throwable e) {
+ reportWtf("starting Country Detector", e);
+ }
}
- try {
- Slog.i(TAG, "Country Detector");
- countryDetector = new CountryDetectorService(context);
- ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
- } catch (Throwable e) {
- reportWtf("starting Country Detector", e);
- }
-
- try {
- Slog.i(TAG, "Search Service");
- ServiceManager.addService(Context.SEARCH_SERVICE,
- new SearchManagerService(context));
- } catch (Throwable e) {
- reportWtf("starting Search Service", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Search Service");
+ ServiceManager.addService(Context.SEARCH_SERVICE,
+ new SearchManagerService(context));
+ } catch (Throwable e) {
+ reportWtf("starting Search Service", e);
+ }
}
try {
@@ -595,8 +612,8 @@
reportWtf("starting DropBoxManagerService", e);
}
- if (context.getResources().getBoolean(
- com.android.internal.R.bool.config_enableWallpaperService)) {
+ if (!disableNonCoreServices && context.getResources().getBoolean(
+ R.bool.config_enableWallpaperService)) {
try {
Slog.i(TAG, "Wallpaper Service");
if (!headless) {
@@ -608,7 +625,7 @@
}
}
- if (!"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
+ if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
try {
Slog.i(TAG, "Audio Service");
ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context));
@@ -617,39 +634,45 @@
}
}
- try {
- Slog.i(TAG, "Dock Observer");
- // Listen for dock station changes
- dock = new DockObserver(context);
- } catch (Throwable e) {
- reportWtf("starting DockObserver", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Dock Observer");
+ // Listen for dock station changes
+ dock = new DockObserver(context);
+ } catch (Throwable e) {
+ reportWtf("starting DockObserver", e);
+ }
}
- try {
- Slog.i(TAG, "Wired Accessory Manager");
- // Listen for wired headset changes
- inputManager.setWiredAccessoryCallbacks(
- new WiredAccessoryManager(context, inputManager));
- } catch (Throwable e) {
- reportWtf("starting WiredAccessoryManager", e);
+ if (!disableMedia) {
+ try {
+ Slog.i(TAG, "Wired Accessory Manager");
+ // Listen for wired headset changes
+ inputManager.setWiredAccessoryCallbacks(
+ new WiredAccessoryManager(context, inputManager));
+ } catch (Throwable e) {
+ reportWtf("starting WiredAccessoryManager", e);
+ }
}
- try {
- Slog.i(TAG, "USB Service");
- // Manage USB host and device support
- usb = new UsbService(context);
- ServiceManager.addService(Context.USB_SERVICE, usb);
- } catch (Throwable e) {
- reportWtf("starting UsbService", e);
- }
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "USB Service");
+ // Manage USB host and device support
+ usb = new UsbService(context);
+ ServiceManager.addService(Context.USB_SERVICE, usb);
+ } catch (Throwable e) {
+ reportWtf("starting UsbService", e);
+ }
- try {
- Slog.i(TAG, "Serial Service");
- // Serial port support
- serial = new SerialService(context);
- ServiceManager.addService(Context.SERIAL_SERVICE, serial);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting SerialService", e);
+ try {
+ Slog.i(TAG, "Serial Service");
+ // Serial port support
+ serial = new SerialService(context);
+ ServiceManager.addService(Context.SERIAL_SERVICE, serial);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting SerialService", e);
+ }
}
try {
@@ -667,27 +690,29 @@
reportWtf("starting UiModeManagerService", e);
}
- try {
- Slog.i(TAG, "Backup Service");
- ServiceManager.addService(Context.BACKUP_SERVICE,
- new BackupManagerService(context));
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting Backup Service", e);
- }
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Backup Service");
+ ServiceManager.addService(Context.BACKUP_SERVICE,
+ new BackupManagerService(context));
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Backup Service", e);
+ }
- try {
- Slog.i(TAG, "AppWidget Service");
- appWidget = new AppWidgetService(context);
- ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
- } catch (Throwable e) {
- reportWtf("starting AppWidget Service", e);
- }
+ try {
+ Slog.i(TAG, "AppWidget Service");
+ appWidget = new AppWidgetService(context);
+ ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
+ } catch (Throwable e) {
+ reportWtf("starting AppWidget Service", e);
+ }
- try {
- Slog.i(TAG, "Recognition Service");
- recognition = new RecognitionManagerService(context);
- } catch (Throwable e) {
- reportWtf("starting Recognition Service", e);
+ try {
+ Slog.i(TAG, "Recognition Service");
+ recognition = new RecognitionManagerService(context);
+ } catch (Throwable e) {
+ reportWtf("starting Recognition Service", e);
+ }
}
try {
@@ -709,30 +734,36 @@
reportWtf("starting SamplingProfiler Service", e);
}
- try {
- Slog.i(TAG, "NetworkTimeUpdateService");
- networkTimeUpdater = new NetworkTimeUpdateService(context);
- } catch (Throwable e) {
- reportWtf("starting NetworkTimeUpdate service", e);
+ if (!disableNetwork) {
+ try {
+ Slog.i(TAG, "NetworkTimeUpdateService");
+ networkTimeUpdater = new NetworkTimeUpdateService(context);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkTimeUpdate service", e);
+ }
}
- try {
- Slog.i(TAG, "CommonTimeManagementService");
- commonTimeMgmtService = new CommonTimeManagementService(context);
- ServiceManager.addService("commontime_management", commonTimeMgmtService);
- } catch (Throwable e) {
- reportWtf("starting CommonTimeManagementService service", e);
+ if (!disableMedia) {
+ try {
+ Slog.i(TAG, "CommonTimeManagementService");
+ commonTimeMgmtService = new CommonTimeManagementService(context);
+ ServiceManager.addService("commontime_management", commonTimeMgmtService);
+ } catch (Throwable e) {
+ reportWtf("starting CommonTimeManagementService service", e);
+ }
}
- try {
- Slog.i(TAG, "CertBlacklister");
- CertBlacklister blacklister = new CertBlacklister(context);
- } catch (Throwable e) {
- reportWtf("starting CertBlacklister", e);
+ if (!disableNetwork) {
+ try {
+ Slog.i(TAG, "CertBlacklister");
+ CertBlacklister blacklister = new CertBlacklister(context);
+ } catch (Throwable e) {
+ reportWtf("starting CertBlacklister", e);
+ }
}
-
- if (context.getResources().getBoolean(
- com.android.internal.R.bool.config_dreamsSupported)) {
+
+ if (!disableNonCoreServices &&
+ context.getResources().getBoolean(R.bool.config_dreamsSupported)) {
try {
Slog.i(TAG, "Dreams Service");
// Dreams (interactive idle-time views, a/k/a screen savers)
@@ -743,12 +774,30 @@
}
}
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Assets Atlas Service");
+ atlas = new AssetAtlasService(context);
+ ServiceManager.addService(AssetAtlasService.ASSET_ATLAS_SERVICE, atlas);
+ } catch (Throwable e) {
+ reportWtf("starting AssetAtlasService", e);
+ }
+ }
+
try {
Slog.i(TAG, "IdleMaintenanceService");
new IdleMaintenanceService(context, battery);
} catch (Throwable e) {
reportWtf("starting IdleMaintenanceService", e);
}
+
+ try {
+ Slog.i(TAG, "Print Service");
+ ServiceManager.addService(Context.PRINT_SERVICE,
+ new PrintManagerService(context));
+ } catch (Throwable e) {
+ reportWtf("starting Print Service", e);
+ }
}
// Before things start rolling, be sure we have decided whether
@@ -773,10 +822,12 @@
reportWtf("making Vibrator Service ready", e);
}
- try {
- lockSettings.systemReady();
- } catch (Throwable e) {
- reportWtf("making Lock Settings Service ready", e);
+ if (lockSettings != null) {
+ try {
+ lockSettings.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Lock Settings Service ready", e);
+ }
}
if (devicePolicy != null) {
@@ -855,6 +906,7 @@
final TextServicesManagerService textServiceManagerServiceF = tsms;
final StatusBarManagerService statusBarF = statusBar;
final DreamManagerService dreamyF = dreamy;
+ final AssetAtlasService atlasF = atlas;
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
@@ -872,7 +924,9 @@
} catch (Throwable e) {
reportWtf("observing native crashes", e);
}
- if (!headless) startSystemUi(contextF);
+ if (!headless) {
+ startSystemUi(contextF);
+ }
try {
if (mountServiceF != null) mountServiceF.systemReady();
} catch (Throwable e) {
@@ -979,11 +1033,17 @@
reportWtf("making DreamManagerService ready", e);
}
try {
+ if (atlasF != null) atlasF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making AssetAtlasService ready", e);
+ }
+ try {
// TODO(BT) Pass parameter to input manager
if (inputManagerF != null) inputManagerF.systemReady();
} catch (Throwable e) {
reportWtf("making InputManagerService ready", e);
}
+
try {
if (telephonyRegistryF != null) telephonyRegistryF.systemReady();
} catch (Throwable e) {
@@ -1068,8 +1128,10 @@
public static final void init2() {
Slog.i(TAG, "Entered the Android system server!");
- Thread thr = new ServerThread();
- thr.setName("android.server.ServerThread");
- thr.start();
+
+ // This used to be its own separate thread, but now it is
+ // just the loop we run on the main thread.
+ ServerThread thr = new ServerThread();
+ thr.initAndLoop();
}
}
diff --git a/services/java/com/android/server/UiThread.java b/services/java/com/android/server/UiThread.java
new file mode 100644
index 0000000..60d73aa
--- /dev/null
+++ b/services/java/com/android/server/UiThread.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.StrictMode;
+import android.util.Slog;
+
+/**
+ * Shared singleton thread for showing UI. This is a foreground thread, and in
+ * additional should not have operations that can take more than a few ms scheduled
+ * on it to avoid UI jank.
+ */
+public final class UiThread extends HandlerThread {
+ private static UiThread sInstance;
+ private static Handler sHandler;
+
+ private UiThread() {
+ super("android.ui", android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new UiThread();
+ sInstance.start();
+ sHandler = new Handler(sInstance.getLooper());
+ sHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //Looper.myLooper().setMessageLogging(new LogPrinter(
+ // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
+ android.os.Process.setCanSelfBackground(false);
+
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i("UiThread", "Enabled StrictMode logging for UI thread");
+ }
+ }
+ });
+ }
+ }
+
+ public static UiThread get() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index 21d3111..9b5f8f6 100644
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.input.InputManager;
+import android.os.BatteryStats;
import android.os.Handler;
import android.os.IVibratorService;
import android.os.PowerManager;
@@ -143,7 +144,8 @@
mWakeLock.setReferenceCounted(true);
mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
- mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+ mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME));
mVibrations = new LinkedList<Vibration>();
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 3aec4ea..ec573f9 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -33,7 +33,6 @@
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.Process;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -59,20 +58,7 @@
// Set this to true to have the watchdog record kernel thread stacks when it fires
static final boolean RECORD_KERNEL_THREADS = true;
- static final int MONITOR = 2718;
-
- static final int TIME_TO_RESTART = DB ? 15*1000 : 60*1000;
- static final int TIME_TO_WAIT = TIME_TO_RESTART / 2;
-
- static final int MEMCHECK_DEFAULT_MIN_SCREEN_OFF = DB ? 1*60 : 5*60; // 5 minutes
- static final int MEMCHECK_DEFAULT_MIN_ALARM = DB ? 1*60 : 3*60; // 3 minutes
- static final int MEMCHECK_DEFAULT_RECHECK_INTERVAL = DB ? 1*60 : 5*60; // 5 minutes
-
- static final int REBOOT_DEFAULT_INTERVAL = DB ? 1 : 0; // never force reboot
- static final int REBOOT_DEFAULT_START_TIME = 3*60*60; // 3:00am
- static final int REBOOT_DEFAULT_WINDOW = 60*60; // within 1 hour
-
- static final String REBOOT_ACTION = "com.android.service.Watchdog.REBOOT";
+ static final int TIME_TO_WAIT = DB ? 5*1000 : 30*1000;
static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
"/system/bin/mediaserver",
@@ -83,100 +69,94 @@
static Watchdog sWatchdog;
/* This handler will be used to post message back onto the main thread */
- final Handler mHandler;
- final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
+ final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<HandlerChecker>();
+ final HandlerChecker mMonitorChecker;
ContentResolver mResolver;
BatteryService mBattery;
PowerManagerService mPower;
AlarmManagerService mAlarm;
ActivityManagerService mActivity;
- boolean mCompleted;
- Monitor mCurrentMonitor;
int mPhonePid;
IActivityController mController;
boolean mAllowRestart = true;
- final Calendar mCalendar = Calendar.getInstance();
- int mMinScreenOff = MEMCHECK_DEFAULT_MIN_SCREEN_OFF;
- int mMinAlarm = MEMCHECK_DEFAULT_MIN_ALARM;
- boolean mNeedScheduledCheck;
- PendingIntent mCheckupIntent;
- PendingIntent mRebootIntent;
-
- long mBootTime;
- int mRebootInterval;
-
- boolean mReqRebootNoWait; // should wait for one interval before reboot?
- int mReqRebootInterval = -1; // >= 0 if a reboot has been requested
- int mReqRebootStartTime = -1; // >= 0 if a specific start time has been requested
- int mReqRebootWindow = -1; // >= 0 if a specific window has been requested
- int mReqMinScreenOff = -1; // >= 0 if a specific screen off time has been requested
- int mReqMinNextAlarm = -1; // >= 0 if specific time to next alarm has been requested
- int mReqRecheckInterval= -1; // >= 0 if a specific recheck interval has been requested
-
/**
- * Used for scheduling monitor callbacks and checking memory usage.
+ * Used for checking status of handle threads and scheduling monitor callbacks.
*/
- final class HeartbeatHandler extends Handler {
- HeartbeatHandler(Looper looper) {
- super(looper);
+ public final class HandlerChecker implements Runnable {
+ private final Handler mHandler;
+ private final String mName;
+ private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
+ private boolean mCompleted;
+ private Monitor mCurrentMonitor;
+
+ HandlerChecker(Handler handler, String name) {
+ mHandler = handler;
+ mName = name;
}
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MONITOR: {
- // See if we should force a reboot.
- int rebootInterval = mReqRebootInterval >= 0
- ? mReqRebootInterval : REBOOT_DEFAULT_INTERVAL;
- if (mRebootInterval != rebootInterval) {
- mRebootInterval = rebootInterval;
- // We have been running long enough that a reboot can
- // be considered...
- checkReboot(false);
- }
+ public void addMonitor(Monitor monitor) {
+ mMonitors.add(monitor);
+ }
- final int size = mMonitors.size();
- for (int i = 0 ; i < size ; i++) {
- synchronized (Watchdog.this) {
- mCurrentMonitor = mMonitors.get(i);
- }
- mCurrentMonitor.monitor();
- }
-
- synchronized (Watchdog.this) {
- mCompleted = true;
- mCurrentMonitor = null;
- }
- } break;
+ public void scheduleCheckLocked() {
+ if (mMonitors.size() == 0 && mHandler.getLooper().isIdling()) {
+ // If the target looper is or just recently was idling, then
+ // there is no reason to enqueue our checker on it since that
+ // is as good as it not being deadlocked. This avoid having
+ // to do a context switch to check the thread. Note that we
+ // only do this if mCheckReboot is false and we have no
+ // monitors, since those would need to be executed at this point.
+ mCompleted = true;
+ return;
}
+ mCompleted = false;
+ mCurrentMonitor = null;
+ mHandler.postAtFrontOfQueue(this);
}
- }
- final class RebootReceiver extends BroadcastReceiver {
+ public boolean isCompletedLocked() {
+ return mCompleted;
+ }
+
+ public Thread getThread() {
+ return mHandler.getLooper().getThread();
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String describeBlockedStateLocked() {
+ return mCurrentMonitor == null ? mName : mCurrentMonitor.getClass().getName();
+ }
+
@Override
- public void onReceive(Context c, Intent intent) {
- if (localLOGV) Slog.v(TAG, "Alarm went off, checking reboot.");
- checkReboot(true);
+ public void run() {
+ final int size = mMonitors.size();
+ for (int i = 0 ; i < size ; i++) {
+ synchronized (Watchdog.this) {
+ mCurrentMonitor = mMonitors.get(i);
+ }
+ mCurrentMonitor.monitor();
+ }
+
+ synchronized (Watchdog.this) {
+ mCompleted = true;
+ mCurrentMonitor = null;
+ }
}
}
final class RebootRequestReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context c, Intent intent) {
- mReqRebootNoWait = intent.getIntExtra("nowait", 0) != 0;
- mReqRebootInterval = intent.getIntExtra("interval", -1);
- mReqRebootStartTime = intent.getIntExtra("startTime", -1);
- mReqRebootWindow = intent.getIntExtra("window", -1);
- mReqMinScreenOff = intent.getIntExtra("minScreenOff", -1);
- mReqMinNextAlarm = intent.getIntExtra("minNextAlarm", -1);
- mReqRecheckInterval = intent.getIntExtra("recheckInterval", -1);
- EventLog.writeEvent(EventLogTags.WATCHDOG_REQUESTED_REBOOT,
- mReqRebootNoWait ? 1 : 0, mReqRebootInterval,
- mReqRecheckInterval, mReqRebootStartTime,
- mReqRebootWindow, mReqMinScreenOff, mReqMinNextAlarm);
- checkReboot(true);
+ if (intent.getIntExtra("nowait", 0) != 0) {
+ rebootSystem("Received ACTION_REBOOT broadcast");
+ return;
+ }
+ Slog.w(TAG, "Unsupported ACTION_REBOOT broadcast: " + intent);
}
}
@@ -194,9 +174,23 @@
private Watchdog() {
super("watchdog");
- // Explicitly bind the HeartbeatHandler to run on the ServerThread, so
- // that it can't get accidentally bound to another thread.
- mHandler = new HeartbeatHandler(Looper.getMainLooper());
+ // Initialize handler checkers for each common thread we want to check. Note
+ // that we are not currently checking the background thread, since it can
+ // potentially hold longer running operations with no guarantees about the timeliness
+ // of operations there.
+
+ // The shared foreground thread is the main checker. It is where we
+ // will also dispatch monitor checks and do other work.
+ mMonitorChecker = new HandlerChecker(FgThread.getHandler(), "foreground thread");
+ mHandlerCheckers.add(mMonitorChecker);
+ // Add checker for main thread. We only do a quick check since there
+ // can be UI running on the thread.
+ mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
+ "main thread"));
+ // Add checker for shared UI thread.
+ mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), "ui thread"));
+ // And also check IO thread.
+ mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), "i/o thread"));
}
public void init(Context context, BatteryService battery,
@@ -208,16 +202,9 @@
mAlarm = alarm;
mActivity = activity;
- context.registerReceiver(new RebootReceiver(),
- new IntentFilter(REBOOT_ACTION));
- mRebootIntent = PendingIntent.getBroadcast(context,
- 0, new Intent(REBOOT_ACTION), 0);
-
context.registerReceiver(new RebootRequestReceiver(),
new IntentFilter(Intent.ACTION_REBOOT),
android.Manifest.permission.REBOOT, null);
-
- mBootTime = System.currentTimeMillis();
}
public void processStarted(String name, int pid) {
@@ -243,87 +230,19 @@
public void addMonitor(Monitor monitor) {
synchronized (this) {
if (isAlive()) {
- throw new RuntimeException("Monitors can't be added while the Watchdog is running");
+ throw new RuntimeException("Monitors can't be added once the Watchdog is running");
}
- mMonitors.add(monitor);
+ mMonitorChecker.addMonitor(monitor);
}
}
- void checkReboot(boolean fromAlarm) {
- int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval
- : REBOOT_DEFAULT_INTERVAL;
- mRebootInterval = rebootInterval;
- if (rebootInterval <= 0) {
- // No reboot interval requested.
- if (localLOGV) Slog.v(TAG, "No need to schedule a reboot alarm!");
- mAlarm.remove(mRebootIntent);
- return;
- }
-
- long rebootStartTime = mReqRebootStartTime >= 0 ? mReqRebootStartTime
- : REBOOT_DEFAULT_START_TIME;
- long rebootWindowMillis = (mReqRebootWindow >= 0 ? mReqRebootWindow
- : REBOOT_DEFAULT_WINDOW) * 1000;
- long recheckInterval = (mReqRecheckInterval >= 0 ? mReqRecheckInterval
- : MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000;
-
- retrieveBrutalityAmount();
-
- long realStartTime;
- long now;
-
+ public void addThread(Handler thread, String name) {
synchronized (this) {
- now = System.currentTimeMillis();
- realStartTime = computeCalendarTime(mCalendar, now,
- rebootStartTime);
-
- long rebootIntervalMillis = rebootInterval*24*60*60*1000;
- if (DB || mReqRebootNoWait ||
- (now-mBootTime) >= (rebootIntervalMillis-rebootWindowMillis)) {
- if (fromAlarm && rebootWindowMillis <= 0) {
- // No reboot window -- just immediately reboot.
- EventLog.writeEvent(EventLogTags.WATCHDOG_SCHEDULED_REBOOT, now,
- (int)rebootIntervalMillis, (int)rebootStartTime*1000,
- (int)rebootWindowMillis, "");
- rebootSystem("Checkin scheduled forced");
- return;
- }
-
- // Are we within the reboot window?
- if (now < realStartTime) {
- // Schedule alarm for next check interval.
- realStartTime = computeCalendarTime(mCalendar,
- now, rebootStartTime);
- } else if (now < (realStartTime+rebootWindowMillis)) {
- String doit = shouldWeBeBrutalLocked(now);
- EventLog.writeEvent(EventLogTags.WATCHDOG_SCHEDULED_REBOOT, now,
- (int)rebootInterval, (int)rebootStartTime*1000,
- (int)rebootWindowMillis, doit != null ? doit : "");
- if (doit == null) {
- rebootSystem("Checked scheduled range");
- return;
- }
-
- // Schedule next alarm either within the window or in the
- // next interval.
- if ((now+recheckInterval) >= (realStartTime+rebootWindowMillis)) {
- realStartTime = computeCalendarTime(mCalendar,
- now + rebootIntervalMillis, rebootStartTime);
- } else {
- realStartTime = now + recheckInterval;
- }
- } else {
- // Schedule alarm for next check interval.
- realStartTime = computeCalendarTime(mCalendar,
- now + rebootIntervalMillis, rebootStartTime);
- }
+ if (isAlive()) {
+ throw new RuntimeException("Threads can't be added once the Watchdog is running");
}
+ mHandlerCheckers.add(new HandlerChecker(thread, name));
}
-
- if (localLOGV) Slog.v(TAG, "Scheduling next reboot alarm for "
- + ((realStartTime-now)/1000/60) + "m from now");
- mAlarm.remove(mRebootIntent);
- mAlarm.set(AlarmManager.RTC_WAKEUP, realStartTime, mRebootIntent);
}
/**
@@ -335,82 +254,55 @@
pms.reboot(false, reason, false);
}
- /**
- * Load the current Gservices settings for when
- * {@link #shouldWeBeBrutalLocked} will allow the brutality to happen.
- * Must not be called with the lock held.
- */
- void retrieveBrutalityAmount() {
- mMinScreenOff = (mReqMinScreenOff >= 0 ? mReqMinScreenOff
- : MEMCHECK_DEFAULT_MIN_SCREEN_OFF) * 1000;
- mMinAlarm = (mReqMinNextAlarm >= 0 ? mReqMinNextAlarm
- : MEMCHECK_DEFAULT_MIN_ALARM) * 1000;
+ private boolean haveAllCheckersCompletedLocked() {
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ if (!hc.isCompletedLocked()) {
+ return false;
+ }
+ }
+ return true;
}
- /**
- * Determine whether it is a good time to kill, crash, or otherwise
- * plunder the current situation for the overall long-term benefit of
- * the world.
- *
- * @param curTime The current system time.
- * @return Returns null if this is a good time, else a String with the
- * text of why it is not a good time.
- */
- String shouldWeBeBrutalLocked(long curTime) {
- if (mBattery == null || !mBattery.isPowered(BatteryManager.BATTERY_PLUGGED_ANY)) {
- return "battery";
+ private ArrayList<HandlerChecker> getBlockedCheckersLocked() {
+ ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>();
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ if (!hc.isCompletedLocked()) {
+ checkers.add(hc);
+ }
}
-
- if (mMinScreenOff >= 0 && (mPower == null ||
- mPower.timeSinceScreenWasLastOn() < mMinScreenOff)) {
- return "screen";
- }
-
- if (mMinAlarm >= 0 && (mAlarm == null ||
- mAlarm.timeToNextAlarm() < mMinAlarm)) {
- return "alarm";
- }
-
- return null;
+ return checkers;
}
- static long computeCalendarTime(Calendar c, long curTime,
- long secondsSinceMidnight) {
-
- // start with now
- c.setTimeInMillis(curTime);
-
- int val = (int)secondsSinceMidnight / (60*60);
- c.set(Calendar.HOUR_OF_DAY, val);
- secondsSinceMidnight -= val * (60*60);
- val = (int)secondsSinceMidnight / 60;
- c.set(Calendar.MINUTE, val);
- c.set(Calendar.SECOND, (int)secondsSinceMidnight - (val*60));
- c.set(Calendar.MILLISECOND, 0);
-
- long newTime = c.getTimeInMillis();
- if (newTime < curTime) {
- // The given time (in seconds since midnight) has already passed for today, so advance
- // by one day (due to daylight savings, etc., the delta may differ from 24 hours).
- c.add(Calendar.DAY_OF_MONTH, 1);
- newTime = c.getTimeInMillis();
+ private String describeCheckersLocked(ArrayList<HandlerChecker> checkers) {
+ StringBuilder builder = new StringBuilder(128);
+ for (int i=0; i<checkers.size(); i++) {
+ if (builder.length() > 0) {
+ builder.append(", ");
+ }
+ builder.append(checkers.get(i).describeBlockedStateLocked());
}
-
- return newTime;
+ return builder.toString();
}
@Override
public void run() {
boolean waitedHalf = false;
while (true) {
- mCompleted = false;
- mHandler.sendEmptyMessage(MONITOR);
-
-
+ final ArrayList<HandlerChecker> blockedCheckers;
final String name;
final boolean allowRestart;
synchronized (this) {
long timeout = TIME_TO_WAIT;
+ if (!waitedHalf) {
+ // If we are not at the half-point of waiting, perform a
+ // new set of checks. Otherwise we are still waiting for a previous set.
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ hc.scheduleCheckLocked();
+ }
+ }
// NOTE: We use uptimeMillis() here because we do not want to increment the time we
// wait while asleep. If the device is asleep then the thing that we are waiting
@@ -426,7 +318,7 @@
timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);
}
- if (mCompleted) {
+ if (haveAllCheckersCompletedLocked()) {
// The monitors have returned.
waitedHalf = false;
continue;
@@ -443,8 +335,8 @@
continue;
}
- name = (mCurrentMonitor != null) ?
- mCurrentMonitor.getClass().getName() : "null";
+ blockedCheckers = getBlockedCheckersLocked();
+ name = describeCheckersLocked(blockedCheckers);
allowRestart = mAllowRestart;
}
@@ -521,6 +413,15 @@
Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
} else {
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name);
+ for (int i=0; i<blockedCheckers.size(); i++) {
+ Slog.w(TAG, blockedCheckers.get(i).getName() + " stack trace:");
+ StackTraceElement[] stackTrace
+ = blockedCheckers.get(i).getThread().getStackTrace();
+ for (StackTraceElement element: stackTrace) {
+ Slog.w(TAG, " at " + element);
+ }
+ }
+ Slog.w(TAG, "*** GOODBYE!");
Process.killProcess(Process.myPid());
System.exit(10);
}
diff --git a/services/java/com/android/server/WiredAccessoryManager.java b/services/java/com/android/server/WiredAccessoryManager.java
index d5c9c8f..415fcc1 100644
--- a/services/java/com/android/server/WiredAccessoryManager.java
+++ b/services/java/com/android/server/WiredAccessoryManager.java
@@ -44,6 +44,7 @@
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* <p>WiredAccessoryManager monitors for a wired headset on the main board or dock using
@@ -408,11 +409,11 @@
public String getDevName() { return mDevName; }
public String getDevPath() {
- return String.format("/devices/virtual/switch/%s", mDevName);
+ return String.format(Locale.US, "/devices/virtual/switch/%s", mDevName);
}
public String getSwitchStatePath() {
- return String.format("/sys/class/switch/%s/state", mDevName);
+ return String.format(Locale.US, "/sys/class/switch/%s/state", mDevName);
}
public boolean checkSwitchExists() {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index f1e4b0c..fd6b467 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2694,7 +2694,10 @@
| AccessibilityNodeInfo.ACTION_COPY
| AccessibilityNodeInfo.ACTION_PASTE
| AccessibilityNodeInfo.ACTION_CUT
- | AccessibilityNodeInfo.ACTION_SET_SELECTION;
+ | AccessibilityNodeInfo.ACTION_SET_SELECTION
+ | AccessibilityNodeInfo.ACTION_EXPAND
+ | AccessibilityNodeInfo.ACTION_COLLAPSE
+ | AccessibilityNodeInfo.ACTION_DISMISS;
private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
AccessibilityEvent.TYPE_VIEW_CLICKED
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 0fbde37..2145b76 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -56,7 +56,6 @@
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -74,6 +73,7 @@
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.FgThread;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
@@ -113,7 +113,6 @@
private final PackageManager mPackageManager;
private UserManager mUserManager;
- private HandlerThread mMessageThread;
private final MessageHandler mMessageHandler;
// Messages that can be sent on mHandler
@@ -234,9 +233,7 @@
mContext = context;
mPackageManager = packageManager;
- mMessageThread = new HandlerThread("AccountManagerService");
- mMessageThread.start();
- mMessageHandler = new MessageHandler(mMessageThread.getLooper());
+ mMessageHandler = new MessageHandler(FgThread.get().getLooper());
mAuthenticatorCache = authenticatorCache;
mAuthenticatorCache.setListener(this, null /* Handler */);
@@ -1510,7 +1507,10 @@
int userId) {
// Only allow the system process to read accounts of other users
if (userId != UserHandle.getCallingUserId()
- && Binder.getCallingUid() != Process.myUid()) {
+ && Binder.getCallingUid() != Process.myUid()
+ && mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("User " + UserHandle.getCallingUserId()
+ " trying to confirm account credentials for " + userId);
}
@@ -1779,7 +1779,10 @@
int callingUid = Binder.getCallingUid();
// Only allow the system process to read accounts of other users
if (userId != UserHandle.getCallingUserId()
- && callingUid != Process.myUid()) {
+ && callingUid != Process.myUid()
+ && mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("User " + UserHandle.getCallingUserId()
+ " trying to get account for " + userId);
}
@@ -2537,7 +2540,7 @@
return userId;
}
- private boolean inSystemImage(int callingUid) {
+ private boolean isPrivileged(int callingUid) {
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageManager userPackageManager;
@@ -2553,7 +2556,7 @@
try {
PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
if (packageInfo != null
- && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
return true;
}
} catch (PackageManager.NameNotFoundException e) {
@@ -2564,7 +2567,7 @@
}
private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
- final boolean inSystemImage = inSystemImage(callerUid);
+ final boolean isPrivileged = isPrivileged(callerUid);
final boolean fromAuthenticator = account != null
&& hasAuthenticatorUid(account.type, callerUid);
final boolean hasExplicitGrants = account != null
@@ -2575,7 +2578,7 @@
+ ": is authenticator? " + fromAuthenticator
+ ", has explicit permission? " + hasExplicitGrants);
}
- return fromAuthenticator || hasExplicitGrants || inSystemImage;
+ return fromAuthenticator || hasExplicitGrants || isPrivileged;
}
private boolean hasAuthenticatorUid(String accountType, int callingUid) {
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 5c24e67..3c10480 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -27,6 +27,7 @@
import java.util.List;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.TransferPipe;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.am.ActivityManagerService.NeededUriGrants;
@@ -57,7 +58,7 @@
import android.util.SparseArray;
import android.util.TimeUtils;
-public class ActiveServices {
+public final class ActiveServices {
static final boolean DEBUG_SERVICE = ActivityManagerService.DEBUG_SERVICE;
static final boolean DEBUG_SERVICE_EXECUTING = ActivityManagerService.DEBUG_SERVICE_EXECUTING;
static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
@@ -240,11 +241,14 @@
if (unscheduleServiceRestartLocked(r)) {
if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
}
+ r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
+ if (r.tracker != null) {
+ r.tracker.setStarted(true, mAm.mProcessTracker.getMemFactorLocked(), r.lastActivity);
+ }
r.callStart = false;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
- r.lastActivity = SystemClock.uptimeMillis();
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
}
@@ -260,8 +264,12 @@
service.stats.stopRunningLocked();
}
service.startRequested = false;
+ if (service.tracker != null) {
+ service.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
+ SystemClock.uptimeMillis());
+ }
service.callStart = false;
- bringDownServiceLocked(service, false);
+ bringDownServiceIfNeededLocked(service, false, false);
}
int stopServiceLocked(IApplicationThread caller, Intent service,
@@ -354,11 +362,15 @@
synchronized (r.stats.getBatteryStats()) {
r.stats.stopRunningLocked();
- r.startRequested = false;
- r.callStart = false;
}
+ r.startRequested = false;
+ if (r.tracker != null) {
+ r.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
+ SystemClock.uptimeMillis());
+ }
+ r.callStart = false;
final long origId = Binder.clearCallingIdentity();
- bringDownServiceLocked(r, false);
+ bringDownServiceIfNeededLocked(r, false, false);
Binder.restoreCallingIdentity(origId);
return true;
}
@@ -439,7 +451,7 @@
ActivityRecord activity = null;
if (token != null) {
- activity = mAm.mMainStack.isInStackLocked(token);
+ activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
@@ -488,6 +500,17 @@
+ s);
}
+ if ((flags&Context.BIND_AUTO_CREATE) != 0) {
+ s.lastActivity = SystemClock.uptimeMillis();
+ if (!s.hasAutoCreateConnections()) {
+ // This is the first binding, let the tracker know.
+ if (s.tracker != null) {
+ s.tracker.setBound(true, mAm.mProcessTracker.getMemFactorLocked(),
+ s.lastActivity);
+ }
+ }
+ }
+
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
@@ -574,30 +597,26 @@
b.binder = service;
b.requested = true;
b.received = true;
- if (r.connections.size() > 0) {
- Iterator<ArrayList<ConnectionRecord>> it
- = r.connections.values().iterator();
- while (it.hasNext()) {
- ArrayList<ConnectionRecord> clist = it.next();
- for (int i=0; i<clist.size(); i++) {
- ConnectionRecord c = clist.get(i);
- if (!filter.equals(c.binding.intent.intent)) {
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Not publishing to: " + c);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Bound intent: " + c.binding.intent.intent);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Published intent: " + intent);
- continue;
- }
- if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
- try {
- c.conn.connected(r.name, service);
- } catch (Exception e) {
- Slog.w(TAG, "Failure sending service " + r.name +
- " to connection " + c.conn.asBinder() +
- " (in " + c.binding.client.processName + ")", e);
- }
+ for (int conni=r.connections.size()-1; conni>=0; conni--) {
+ ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
+ for (int i=0; i<clist.size(); i++) {
+ ConnectionRecord c = clist.get(i);
+ if (!filter.equals(c.binding.intent.intent)) {
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Not publishing to: " + c);
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Bound intent: " + c.binding.intent.intent);
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Published intent: " + intent);
+ continue;
+ }
+ if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
+ try {
+ c.conn.connected(r.name, service);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failure sending service " + r.name +
+ " to connection " + c.conn.asBinder() +
+ " (in " + c.binding.client.processName + ")", e);
}
}
}
@@ -751,7 +770,12 @@
sInfo.applicationInfo.uid, sInfo.packageName,
sInfo.name);
}
- r = new ServiceRecord(mAm, ss, name, filter, sInfo, res);
+ ProcessTracker.ServiceState tracker = null;
+ if ((sInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ tracker = mAm.mProcessTracker.getServiceStateLocked(sInfo.packageName,
+ sInfo.applicationInfo.uid, sInfo.name);
+ }
+ r = new ServiceRecord(mAm, ss, name, filter, sInfo, res, tracker);
res.setService(r);
mServiceMap.putServiceByName(name, UserHandle.getUserId(r.appInfo.uid), r);
mServiceMap.putServiceByIntent(filter, UserHandle.getUserId(r.appInfo.uid), r);
@@ -801,14 +825,19 @@
else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING "
+ why + " of " + r.shortName);
long now = SystemClock.uptimeMillis();
- if (r.executeNesting == 0 && r.app != null) {
- if (r.app.executingServices.size() == 0) {
- Message msg = mAm.mHandler.obtainMessage(
- ActivityManagerService.SERVICE_TIMEOUT_MSG);
- msg.obj = r.app;
- mAm.mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT);
+ if (r.executeNesting == 0) {
+ if (r.tracker != null) {
+ r.tracker.setExecuting(true, mAm.mProcessTracker.getMemFactorLocked(), now);
}
- r.app.executingServices.add(r);
+ if (r.app != null) {
+ if (r.app.executingServices.size() == 0) {
+ Message msg = mAm.mHandler.obtainMessage(
+ ActivityManagerService.SERVICE_TIMEOUT_MSG);
+ msg.obj = r.app;
+ mAm.mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT);
+ }
+ r.app.executingServices.add(r);
+ }
}
r.executeNesting++;
r.executingStart = now;
@@ -994,7 +1023,7 @@
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": user " + r.userId + " is stopped";
Slog.w(TAG, msg);
- bringDownServiceLocked(r, true);
+ bringDownServiceLocked(r);
return msg;
}
@@ -1018,7 +1047,7 @@
Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
if (app != null && app.thread != null) {
try {
- app.addPackage(r.appInfo.packageName);
+ app.addPackage(r.appInfo.packageName, mAm.mProcessTracker);
realStartServiceLocked(r, app);
return null;
} catch (RemoteException e) {
@@ -1048,7 +1077,7 @@
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": process is bad";
Slog.w(TAG, msg);
- bringDownServiceLocked(r, true);
+ bringDownServiceLocked(r);
return msg;
}
if (isolated) {
@@ -1064,10 +1093,9 @@
}
private final void requestServiceBindingsLocked(ServiceRecord r) {
- Iterator<IntentBindRecord> bindings = r.bindings.values().iterator();
- while (bindings.hasNext()) {
- IntentBindRecord i = bindings.next();
- if (!requestServiceBindingLocked(r, i, false)) {
+ for (int i=r.bindings.size()-1; i>=0; i--) {
+ IntentBindRecord ibr = r.bindings.valueAt(i);
+ if (!requestServiceBindingLocked(r, ibr, false)) {
break;
}
}
@@ -1171,58 +1199,56 @@
}
}
- private final void bringDownServiceLocked(ServiceRecord r, boolean force) {
+ private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
+ boolean hasConn) {
//Slog.i(TAG, "Bring down service:");
//r.dump(" ");
// Does it still need to run?
- if (!force && r.startRequested) {
+ if (r.startRequested) {
return;
}
- if (r.connections.size() > 0) {
- if (!force) {
- // XXX should probably keep a count of the number of auto-create
- // connections directly in the service.
- Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
- while (it.hasNext()) {
- ArrayList<ConnectionRecord> cr = it.next();
- for (int i=0; i<cr.size(); i++) {
- if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
- return;
- }
- }
- }
- }
- // Report to all of the connections that the service is no longer
- // available.
- Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
- while (it.hasNext()) {
- ArrayList<ConnectionRecord> c = it.next();
- for (int i=0; i<c.size(); i++) {
- ConnectionRecord cr = c.get(i);
- // There is still a connection to the service that is
- // being brought down. Mark it as dead.
- cr.serviceDead = true;
- try {
- cr.conn.connected(r.name, null);
- } catch (Exception e) {
- Slog.w(TAG, "Failure disconnecting service " + r.name +
- " to connection " + c.get(i).conn.asBinder() +
- " (in " + c.get(i).binding.client.processName + ")", e);
- }
+ if (!knowConn) {
+ hasConn = r.hasAutoCreateConnections();
+ }
+ if (hasConn) {
+ return;
+ }
+
+ bringDownServiceLocked(r);
+ }
+
+ private final void bringDownServiceLocked(ServiceRecord r) {
+ //Slog.i(TAG, "Bring down service:");
+ //r.dump(" ");
+
+ // Report to all of the connections that the service is no longer
+ // available.
+ for (int conni=r.connections.size()-1; conni>=0; conni--) {
+ ArrayList<ConnectionRecord> c = r.connections.valueAt(conni);
+ for (int i=0; i<c.size(); i++) {
+ ConnectionRecord cr = c.get(i);
+ // There is still a connection to the service that is
+ // being brought down. Mark it as dead.
+ cr.serviceDead = true;
+ try {
+ cr.conn.connected(r.name, null);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failure disconnecting service " + r.name +
+ " to connection " + c.get(i).conn.asBinder() +
+ " (in " + c.get(i).binding.client.processName + ")", e);
}
}
}
// Tell the service that it has been unbound.
- if (r.bindings.size() > 0 && r.app != null && r.app.thread != null) {
- Iterator<IntentBindRecord> it = r.bindings.values().iterator();
- while (it.hasNext()) {
- IntentBindRecord ibr = it.next();
+ if (r.app != null && r.app.thread != null) {
+ for (int i=r.bindings.size()-1; i>=0; i--) {
+ IntentBindRecord ibr = r.bindings.valueAt(i);
if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down binding " + ibr
+ ": hasBound=" + ibr.hasBound);
- if (r.app != null && r.app.thread != null && ibr.hasBound) {
+ if (ibr.hasBound) {
try {
bumpServiceExecutingLocked(r, "bring down unbind");
mAm.updateOomAdjLocked(r.app);
@@ -1300,6 +1326,13 @@
if (r.restarter instanceof ServiceRestarter) {
((ServiceRestarter)r.restarter).setService(null);
}
+
+ int memFactor = mAm.mProcessTracker.getMemFactorLocked();
+ long now = SystemClock.uptimeMillis();
+ if (r.tracker != null) {
+ r.tracker.setStarted(false, memFactor, now);
+ r.tracker.setBound(false, memFactor, now);
+ }
}
void removeConnectionLocked(
@@ -1358,7 +1391,14 @@
}
if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
- bringDownServiceLocked(s, false);
+ boolean hasAutoCreate = s.hasAutoCreateConnections();
+ if (!hasAutoCreate) {
+ if (s.tracker != null) {
+ s.tracker.setBound(false, mAm.mProcessTracker.getMemFactorLocked(),
+ SystemClock.uptimeMillis());
+ }
+ }
+ bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
}
}
}
@@ -1431,22 +1471,28 @@
+ ", inStopping=" + inStopping + ", app=" + r.app);
else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName);
r.executeNesting--;
- if (r.executeNesting <= 0 && r.app != null) {
- if (DEBUG_SERVICE) Slog.v(TAG,
- "Nesting at 0 of " + r.shortName);
- r.app.executingServices.remove(r);
- if (r.app.executingServices.size() == 0) {
- if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
- "No more executingServices of " + r.shortName);
- mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
- }
- if (inStopping) {
+ if (r.executeNesting <= 0) {
+ if (r.app != null) {
if (DEBUG_SERVICE) Slog.v(TAG,
- "doneExecuting remove stopping " + r);
- mStoppingServices.remove(r);
- r.bindings.clear();
+ "Nesting at 0 of " + r.shortName);
+ r.app.executingServices.remove(r);
+ if (r.app.executingServices.size() == 0) {
+ if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
+ "No more executingServices of " + r.shortName);
+ mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
+ }
+ if (inStopping) {
+ if (DEBUG_SERVICE) Slog.v(TAG,
+ "doneExecuting remove stopping " + r);
+ mStoppingServices.remove(r);
+ r.bindings.clear();
+ }
+ mAm.updateOomAdjLocked(r.app);
}
- mAm.updateOomAdjLocked(r.app);
+ if (r.tracker != null) {
+ r.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
+ SystemClock.uptimeMillis());
+ }
}
}
@@ -1465,6 +1511,7 @@
mPendingServices.remove(i);
i--;
+ proc.addPackage(sr.appInfo.packageName, mAm.mProcessTracker);
realStartServiceLocked(sr, proc);
didSomething = true;
}
@@ -1503,7 +1550,7 @@
sr.isolatedProc = null;
mPendingServices.remove(i);
i--;
- bringDownServiceLocked(sr, true);
+ bringDownServiceLocked(sr);
}
}
}
@@ -1554,7 +1601,7 @@
int N = services.size();
for (int i=0; i<N; i++) {
- bringDownServiceLocked(services.get(i), true);
+ bringDownServiceLocked(services.get(i));
}
return didSomething;
}
@@ -1595,22 +1642,18 @@
Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
ServiceRecord r = it.next();
- if (r.connections.size() > 0) {
- Iterator<ArrayList<ConnectionRecord>> jt
- = r.connections.values().iterator();
- while (jt.hasNext()) {
- ArrayList<ConnectionRecord> cl = jt.next();
- for (int i=0; i<cl.size(); i++) {
- ConnectionRecord c = cl.get(i);
- if (c.binding.client != app) {
- try {
- //c.conn.connected(r.className, null);
- } catch (Exception e) {
- // todo: this should be asynchronous!
- Slog.w(TAG, "Exception thrown disconnected servce "
- + r.shortName
- + " from app " + app.processName, e);
- }
+ for (int conni=r.connections.size()-1; conni>=0; conni--) {
+ ArrayList<ConnectionRecord> cl = r.connections.valueAt(conni);
+ for (int i=0; i<cl.size(); i++) {
+ ConnectionRecord c = cl.get(i);
+ if (c.binding.client != app) {
+ try {
+ //c.conn.connected(r.className, null);
+ } catch (Exception e) {
+ // todo: this should be asynchronous!
+ Slog.w(TAG, "Exception thrown disconnected servce "
+ + r.shortName
+ + " from app " + app.processName, e);
}
}
}
@@ -1641,21 +1684,21 @@
sr.app = null;
sr.isolatedProc = null;
sr.executeNesting = 0;
+ if (sr.tracker != null) {
+ sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
+ SystemClock.uptimeMillis());
+ }
if (mStoppingServices.remove(sr)) {
if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
}
- boolean hasClients = sr.bindings.size() > 0;
- if (hasClients) {
- Iterator<IntentBindRecord> bindings
- = sr.bindings.values().iterator();
- while (bindings.hasNext()) {
- IntentBindRecord b = bindings.next();
- if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
- + ": shouldUnbind=" + b.hasBound);
- b.binder = null;
- b.requested = b.received = b.hasBound = false;
- }
+ final int numClients = sr.bindings.size();
+ for (int bindingi=numClients-1; bindingi>=0; bindingi--) {
+ IntentBindRecord b = sr.bindings.valueAt(bindingi);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
+ + ": shouldUnbind=" + b.hasBound);
+ b.binder = null;
+ b.requested = b.received = b.hasBound = false;
}
if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
@@ -1664,9 +1707,9 @@
+ " times, stopping: " + sr);
EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
sr.userId, sr.crashCount, sr.shortName, app.pid);
- bringDownServiceLocked(sr, true);
+ bringDownServiceLocked(sr);
} else if (!allowRestart) {
- bringDownServiceLocked(sr, true);
+ bringDownServiceLocked(sr);
} else {
boolean canceled = scheduleServiceRestartLocked(sr, true);
@@ -1676,9 +1719,13 @@
if (sr.startRequested && (sr.stopIfKilled || canceled)) {
if (sr.pendingStarts.size() == 0) {
sr.startRequested = false;
- if (!hasClients) {
+ if (sr.tracker != null) {
+ sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
+ SystemClock.uptimeMillis());
+ }
+ if (!sr.hasAutoCreateConnections()) {
// Whoops, no reason to restart!
- bringDownServiceLocked(sr, true);
+ bringDownServiceLocked(sr);
}
}
}
@@ -1732,7 +1779,8 @@
info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
}
- for (ArrayList<ConnectionRecord> connl : r.connections.values()) {
+ for (int conni=r.connections.size()-1; conni>=0; conni--) {
+ ArrayList<ConnectionRecord> connl = r.connections.valueAt(conni);
for (int i=0; i<connl.size(); i++) {
ConnectionRecord conn = connl.get(i);
if (conn.clientLabel != 0) {
@@ -1805,7 +1853,8 @@
int userId = UserHandle.getUserId(Binder.getCallingUid());
ServiceRecord r = mServiceMap.getServiceByName(name, userId);
if (r != null) {
- for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
+ for (int conni=r.connections.size()-1; conni>=0; conni--) {
+ ArrayList<ConnectionRecord> conn = r.connections.valueAt(conni);
for (int i=0; i<conn.size(); i++) {
if (conn.get(i).clientIntent != null) {
return conn.get(i).clientIntent;
@@ -1856,9 +1905,10 @@
/**
* Prints a list of ServiceRecords (dumpsys activity services)
*/
- boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ void dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
boolean needSep = false;
+ boolean printedAnything = false;
ItemMatcher matcher = new ItemMatcher();
matcher.build(args, opti);
@@ -1881,6 +1931,7 @@
if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
continue;
}
+ printedAnything = true;
if (!printed) {
if (user != 0) {
pw.println();
@@ -1907,7 +1958,8 @@
pw.println(r.connections.size());
if (r.connections.size() > 0) {
pw.println(" Connections:");
- for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
+ for (int conni=0; conni<r.connections.size(); conni++) {
+ ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
for (int i = 0; i < clist.size(); i++) {
ConnectionRecord conn = clist.get(i);
pw.print(" ");
@@ -1943,11 +1995,11 @@
needSep = true;
}
}
- needSep = printed;
+ needSep |= printed;
}
}
} catch (Exception e) {
- Log.w(TAG, "Exception in dumpServicesLocked: " + e);
+ Log.w(TAG, "Exception in dumpServicesLocked", e);
}
if (mPendingServices.size() > 0) {
@@ -1960,8 +2012,9 @@
if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
continue;
}
+ printedAnything = true;
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Pending services:");
printed = true;
@@ -1982,8 +2035,9 @@
if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
continue;
}
+ printedAnything = true;
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Restarting services:");
printed = true;
@@ -2004,8 +2058,9 @@
if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
continue;
}
+ printedAnything = true;
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Stopping services:");
printed = true;
@@ -2032,8 +2087,9 @@
|| !dumpPackage.equals(cr.binding.client.info.packageName))) {
continue;
}
+ printedAnything = true;
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Connection bindings to services:");
printed = true;
@@ -2042,11 +2098,12 @@
cr.dump(pw, " ");
}
}
- needSep = true;
}
}
- return needSep;
+ if (!printedAnything) {
+ pw.println(" (nothing)");
+ }
}
/**
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index db01655..65006e5 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,12 +17,26 @@
package com.android.server.am;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import android.app.AppOpsManager;
import android.appwidget.AppWidgetManager;
+import android.util.ArrayMap;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessStats;
+import com.android.internal.os.TransferPipe;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.MemInfoReader;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -33,12 +47,24 @@
import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.AppTransition;
+import com.android.server.wm.StackBox;
import com.android.server.wm.WindowManagerService;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
import dalvik.system.Zygote;
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackBoxInfo;
+import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.ActivityThread;
@@ -125,6 +151,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.Time;
+import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
@@ -132,6 +159,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.Xml;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -166,41 +194,43 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
-public final class ActivityManagerService extends ActivityManagerNative
+public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
private static final String USER_DATA_DIR = "/data/user/";
static final String TAG = "ActivityManager";
static final String TAG_MU = "ActivityManagerServiceMU";
static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG;
+ static final boolean DEBUG_BACKUP = localLOGV || false;
+ static final boolean DEBUG_BROADCAST = localLOGV || false;
+ static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
+ static final boolean DEBUG_BACKGROUND_BROADCAST = DEBUG_BROADCAST || false;
+ static final boolean DEBUG_CLEANUP = localLOGV || false;
+ static final boolean DEBUG_CONFIGURATION = localLOGV || false;
+ static final boolean DEBUG_FOCUS = true;
+ static final boolean DEBUG_IMMERSIVE = localLOGV || false;
+ static final boolean DEBUG_MU = localLOGV || false;
+ static final boolean DEBUG_OOM_ADJ = localLOGV || false;
+ static final boolean DEBUG_PAUSE = localLOGV || false;
+ static final boolean DEBUG_POWER = localLOGV || false;
+ static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
+ static final boolean DEBUG_PROCESS_OBSERVERS = localLOGV || false;
+ static final boolean DEBUG_PROCESSES = localLOGV || false;
+ static final boolean DEBUG_PROVIDER = localLOGV || false;
+ static final boolean DEBUG_RESULTS = localLOGV || false;
+ static final boolean DEBUG_SERVICE = localLOGV || false;
+ static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
+ static final boolean DEBUG_STACK = localLOGV || false;
static final boolean DEBUG_SWITCH = localLOGV || false;
static final boolean DEBUG_TASKS = localLOGV || false;
static final boolean DEBUG_THUMBNAILS = localLOGV || false;
- static final boolean DEBUG_PAUSE = localLOGV || false;
- static final boolean DEBUG_OOM_ADJ = localLOGV || false;
static final boolean DEBUG_TRANSITION = localLOGV || false;
- static final boolean DEBUG_BROADCAST = localLOGV || false;
- static final boolean DEBUG_BACKGROUND_BROADCAST = DEBUG_BROADCAST || false;
- static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
- static final boolean DEBUG_SERVICE = localLOGV || false;
- static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
- static final boolean DEBUG_VISBILITY = localLOGV || false;
- static final boolean DEBUG_PROCESSES = localLOGV || false;
- static final boolean DEBUG_PROCESS_OBSERVERS = localLOGV || false;
- static final boolean DEBUG_CLEANUP = localLOGV || false;
- static final boolean DEBUG_PROVIDER = localLOGV || false;
static final boolean DEBUG_URI_PERMISSION = localLOGV || false;
static final boolean DEBUG_USER_LEAVING = localLOGV || false;
- static final boolean DEBUG_RESULTS = localLOGV || false;
- static final boolean DEBUG_BACKUP = localLOGV || false;
- static final boolean DEBUG_CONFIGURATION = localLOGV || false;
- static final boolean DEBUG_POWER = localLOGV || false;
- static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
- static final boolean DEBUG_MU = localLOGV || false;
- static final boolean DEBUG_IMMERSIVE = localLOGV || false;
- static final boolean VALIDATE_TOKENS = false;
+ static final boolean DEBUG_VISBILITY = localLOGV || false;
+ static final boolean VALIDATE_TOKENS = true;
static final boolean SHOW_ACTIVITY_START_TIME = true;
-
+
// Control over CPU and battery monitoring.
static final long BATTERY_STATS_TIME = 30*60*1000; // write battery stats every 30 minutes.
static final boolean MONITOR_CPU_USAGE = true;
@@ -210,14 +240,14 @@
// The flags that are set for all calls we make to the package manager.
static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
-
+
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
// Maximum number of recent tasks that we can remember.
static final int MAX_RECENT_TASKS = 20;
-
+
// Amount of time after a call to stopAppSwitches() during which we will
// prevent further untrusted switches from happening.
static final long APP_SWITCH_DELAY_TIME = 5*1000;
@@ -238,6 +268,24 @@
// The minimum amount of time between successive GC requests for a process.
static final int GC_MIN_INTERVAL = 60*1000;
+ // The minimum amount of time between successive PSS requests for a process.
+ static final int PSS_MIN_INTERVAL = 2*60*1000;
+
+ // The amount of time we will sample PSS of the current top process while the
+ // screen is on.
+ static final int PSS_TOP_INTERVAL = 2*60*1000;
+
+ // The amount of time we will sample PSS of any processes that more at least as
+ // important as perceptible while the screen is on.
+ static final int PSS_PERCEPTIBLE_INTERVAL = 10*60*1000;
+
+ // The maximum amount of time for a process to be around until we will take
+ // a PSS snapshot on its next oom change.
+ static final int PSS_MAX_INTERVAL = 30*60*1000;
+
+ // The minimum amount of time between successive PSS requests for a process.
+ static final int FULL_PSS_MIN_INTERVAL = 10*60*1000;
+
// The rate at which we check for apps using excessive power -- 15 mins.
static final int POWER_CHECK_DELAY = (DEBUG_POWER_QUICK ? 2 : 15) * 60*1000;
@@ -270,10 +318,11 @@
static final int PENDING_ACTIVITY_RESULT_TIMEOUT = 2*2000;
static final int MY_PID = Process.myPid();
-
+
static final String[] EMPTY_STRING_ARRAY = new String[0];
- public ActivityStack mMainStack;
+ /** Run all ActivityStacks through this */
+ ActivityStackSupervisor mStackSupervisor;
public IntentFirewall mIntentFirewall;
@@ -289,14 +338,22 @@
* due to app switches being disabled.
*/
static class PendingActivityLaunch {
- ActivityRecord r;
- ActivityRecord sourceRecord;
- int startFlags;
+ final ActivityRecord r;
+ final ActivityRecord sourceRecord;
+ final int startFlags;
+ final ActivityStack stack;
+
+ public PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
+ int _startFlags, ActivityStack _stack) {
+ r = _r;
+ sourceRecord = _sourceRecord;
+ startFlags = _startFlags;
+ stack = _stack;
+ }
}
-
+
final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
= new ArrayList<PendingActivityLaunch>();
-
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
@@ -332,7 +389,7 @@
/**
* List of intents that were used to start the most recent tasks.
*/
- final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
+ private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
public class PendingActivityExtras extends Binder implements Runnable {
public final ActivityRecord activity;
@@ -368,6 +425,12 @@
final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
/**
+ * Tracking long-term execution of processes to look for abuse and other
+ * bad app behavior.
+ */
+ final ProcessTracker mProcessTracker;
+
+ /**
* The currently running isolated processes.
*/
final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<ProcessRecord>();
@@ -382,7 +445,7 @@
* The currently running heavy-weight process, if any.
*/
ProcessRecord mHeavyWeightProcess = null;
-
+
/**
* The last time that various processes have crashed.
*/
@@ -418,7 +481,7 @@
}
final SparseArray<ForegroundToken> mForegroundProcesses
= new SparseArray<ForegroundToken>();
-
+
/**
* List of records for processes that someone had tried to start before the
* system was ready. We don't start them at that point, but ensure they
@@ -452,15 +515,24 @@
/**
* List of processes that should gc as soon as things are idle.
*/
- final ArrayList<ProcessRecord> mProcessesToGc
- = new ArrayList<ProcessRecord>();
+ final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
+
+ /**
+ * Processes we want to collect PSS data from.
+ */
+ final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
+
+ /**
+ * Last time we requested PSS data of all processes.
+ */
+ long mLastFullPssTime = SystemClock.uptimeMillis();
/**
* This is the process holding what we currently consider to be
* the "home" activity.
*/
ProcessRecord mHomeProcess;
-
+
/**
* This is the process holding the activity the user last visited that
* is in a different process from the one they are currently in.
@@ -505,11 +577,6 @@
final CompatModePackages mCompatModePackages;
/**
- * Set of PendingResultRecord objects that are currently active.
- */
- final HashSet mPendingResultRecords = new HashSet();
-
- /**
* Set of IntentSenderRecord objects that are currently active.
*/
final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
@@ -538,7 +605,8 @@
* broadcasts. Hash keys are the receiver IBinder, hash value is
* a ReceiverList.
*/
- final HashMap mRegisteredReceivers = new HashMap();
+ final HashMap<IBinder, ReceiverList> mRegisteredReceivers =
+ new HashMap<IBinder, ReceiverList>();
/**
* Resolver for broadcast intents to registered receivers.
@@ -585,8 +653,8 @@
* by the user ID the sticky is for, and can include UserHandle.USER_ALL
* for stickies that are sent to all users.
*/
- final SparseArray<HashMap<String, ArrayList<Intent>>> mStickyBroadcasts =
- new SparseArray<HashMap<String, ArrayList<Intent>>>();
+ final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
+ new SparseArray<ArrayMap<String, ArrayList<Intent>>>();
final ActiveServices mServices;
@@ -600,13 +668,8 @@
* List of PendingThumbnailsRecord objects of clients who are still
* waiting to receive all of the thumbnails for a task.
*/
- final ArrayList mPendingThumbnails = new ArrayList();
-
- /**
- * List of HistoryRecord objects that have been finished and must
- * still report back to a pending thumbnail receiver.
- */
- final ArrayList mCancelledThumbnails = new ArrayList();
+ final ArrayList<PendingThumbnailsRecord> mPendingThumbnails =
+ new ArrayList<PendingThumbnailsRecord>();
final ProviderMap mProviderMap;
@@ -619,10 +682,27 @@
= new ArrayList<ContentProviderRecord>();
/**
- * Global set of specific Uri permissions that have been granted.
+ * File storing persisted {@link #mGrantedUriPermissions}.
*/
- final private SparseArray<HashMap<Uri, UriPermission>> mGrantedUriPermissions
- = new SparseArray<HashMap<Uri, UriPermission>>();
+ private final AtomicFile mGrantFile;
+
+ /** XML constants used in {@link #mGrantFile} */
+ private static final String TAG_URI_GRANTS = "uri-grants";
+ private static final String TAG_URI_GRANT = "uri-grant";
+ private static final String ATTR_USER_HANDLE = "userHandle";
+ private static final String ATTR_SOURCE_PKG = "sourcePkg";
+ private static final String ATTR_TARGET_PKG = "targetPkg";
+ private static final String ATTR_URI = "uri";
+ private static final String ATTR_MODE_FLAGS = "modeFlags";
+
+ /**
+ * Global set of specific {@link Uri} permissions that have been granted.
+ * This optimized lookup structure maps from {@link UriPermission#targetUid}
+ * to {@link UriPermission#uri} to {@link UriPermission}.
+ */
+ @GuardedBy("this")
+ private final SparseArray<HashMap<Uri, UriPermission>>
+ mGrantedUriPermissions = new SparseArray<HashMap<Uri, UriPermission>>();
CoreSettingsObserver mCoreSettingsObserver;
@@ -647,12 +727,12 @@
* any user id that can impact battery performance.
*/
final BatteryStatsService mBatteryStatsService;
-
+
/**
* Information about component usage
*/
final UsageStatsService mUsageStatsService;
-
+
/**
* Information about and control over application operations
*/
@@ -670,7 +750,7 @@
* configurations.
*/
int mConfigurationSeq = 0;
-
+
/**
* Hardware-reported OpenGLES version.
*/
@@ -686,7 +766,7 @@
* Temporary to avoid allocations. Protected by main lock.
*/
final StringBuilder mStringBuilder = new StringBuilder(256);
-
+
/**
* Used to control how we initialize the service.
*/
@@ -707,7 +787,7 @@
int mFactoryTest;
boolean mCheckedForSetup;
-
+
/**
* The time at which we will allow normal application switches again,
* after a call to {@link #stopAppSwitches()}.
@@ -719,7 +799,7 @@
* is set; any switches after that will clear the time.
*/
boolean mDidAppSwitch;
-
+
/**
* Last time (in realtime) at which we checked for power usage.
*/
@@ -752,14 +832,6 @@
boolean mShuttingDown = false;
/**
- * Task identifier that activities are currently being started
- * in. Incremented each time a new task is created.
- * todo: Replace this with a TokenSpace class that generates non-repeating
- * integers that won't wrap.
- */
- int mCurTask = 1;
-
- /**
* Current sequence id for oom_adj computation traversal.
*/
int mAdjSeq = 0;
@@ -770,16 +842,16 @@
int mLruSeq = 0;
/**
- * Keep track of the non-hidden/empty process we last found, to help
- * determine how to distribute hidden/empty processes next time.
+ * Keep track of the non-cached/empty process we last found, to help
+ * determine how to distribute cached/empty processes next time.
*/
- int mNumNonHiddenProcs = 0;
+ int mNumNonCachedProcs = 0;
/**
- * Keep track of the number of hidden procs, to balance oom adj
+ * Keep track of the number of cached hidden procs, to balance oom adj
* distribution between those and empty procs.
*/
- int mNumHiddenProcs = 0;
+ int mNumCachedHiddenProcs = 0;
/**
* Keep track of the number of service processes we last found, to
@@ -793,13 +865,13 @@
* N procs were started.
*/
int[] mProcDeaths = new int[20];
-
+
/**
* This is set if we had to do a delayed dexopt of an app before launching
* it, to increasing the ANR timeouts in that case.
*/
boolean mDidDexOpt;
-
+
String mDebugApp = null;
boolean mWaitForDebugger = false;
boolean mDebugTransient = false;
@@ -839,7 +911,7 @@
* protect all related state.
*/
final Thread mProcessStatsThread;
-
+
/**
* Used to collect process stats when showing not responding dialog.
* Protected by mProcessStatsThread.
@@ -862,7 +934,7 @@
*/
boolean mBooted = false;
- int mProcessLimit = ProcessList.MAX_HIDDEN_APPS;
+ int mProcessLimit = ProcessList.MAX_CACHED_APPS;
int mProcessLimitOverride = -1;
WindowManagerService mWindowManager;
@@ -870,8 +942,7 @@
static ActivityManagerService mSelf;
static ActivityThread mSystemThread;
- private int mCurrentUserId = 0;
- private int[] mCurrentUserArray = new int[] { 0 };
+ int mCurrentUserId = 0;
private UserManagerService mUserManager;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -889,6 +960,7 @@
mAppThread = thread;
}
+ @Override
public void binderDied() {
if (localLOGV) Slog.v(
TAG, "Death received in " + this
@@ -927,10 +999,12 @@
static final int CONTINUE_USER_SWITCH_MSG = 35;
static final int USER_SWITCH_TIMEOUT_MSG = 36;
static final int IMMERSIVE_MODE_LOCK_MSG = 37;
+ static final int PERSIST_URI_GRANTS = 38;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
static final int FIRST_COMPAT_MODE_MSG = 300;
+ static final int FIRST_SUPERVISOR_STACK_MSG = 100;
AlertDialog mUidAlert;
CompatModeDialog mCompatModeDialog;
@@ -947,10 +1021,11 @@
// if (localLOGV) Slog.v(TAG, "Handler started!");
//}
+ @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_ERROR_MSG: {
- HashMap data = (HashMap) msg.obj;
+ HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
synchronized (ActivityManagerService.this) {
@@ -985,18 +1060,18 @@
}
}
}
-
+
ensureBootCompleted();
} break;
case SHOW_NOT_RESPONDING_MSG: {
synchronized (ActivityManagerService.this) {
- HashMap data = (HashMap) msg.obj;
+ HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
ProcessRecord proc = (ProcessRecord)data.get("app");
if (proc != null && proc.anrDialog != null) {
Slog.e(TAG, "App already has anr dialog: " + proc);
return;
}
-
+
Intent intent = new Intent("android.intent.action.ANR");
if (!mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -1017,7 +1092,7 @@
killAppAtUsersRequest(proc, null);
}
}
-
+
ensureBootCompleted();
} break;
case SHOW_STRICT_MODE_VIOLATION_MSG: {
@@ -1189,9 +1264,11 @@
synchronized (ActivityManagerService.this) {
int appid = msg.arg1;
boolean restart = (msg.arg2 == 1);
- String pkg = (String) msg.obj;
+ Bundle bundle = (Bundle)msg.obj;
+ String pkg = bundle.getString("pkg");
+ String reason = bundle.getString("reason");
forceStopPackageLocked(pkg, appid, restart, false, true, false,
- UserHandle.USER_ALL);
+ UserHandle.USER_ALL, reason);
}
} break;
case FINALIZE_PENDING_INTENT_MSG: {
@@ -1202,13 +1279,13 @@
if (inm == null) {
return;
}
-
+
ActivityRecord root = (ActivityRecord)msg.obj;
ProcessRecord process = root.app;
if (process == null) {
return;
}
-
+
try {
Context context = mContext.createPackageContext(process.info.packageName, 0);
String text = mContext.getString(R.string.heavy_weight_notification,
@@ -1226,7 +1303,7 @@
PendingIntent.getActivityAsUser(mContext, 0, root.intent,
PendingIntent.FLAG_CANCEL_CURRENT, null,
new UserHandle(root.userId)));
-
+
try {
int[] outId = new int[1];
inm.enqueueNotificationWithTag("android", "android", null,
@@ -1320,9 +1397,9 @@
StringBuilder dropBuilder = new StringBuilder(1024);
StringBuilder logBuilder = new StringBuilder(1024);
StringWriter oomSw = new StringWriter();
- PrintWriter oomPw = new PrintWriter(oomSw);
+ PrintWriter oomPw = new FastPrintWriter(oomSw, false, 256);
StringWriter catSw = new StringWriter();
- PrintWriter catPw = new PrintWriter(catSw);
+ PrintWriter catPw = new FastPrintWriter(catSw, false, 256);
String[] emptyArgs = new String[] { };
StringBuilder tag = new StringBuilder(128);
StringBuilder stack = new StringBuilder(128);
@@ -1332,6 +1409,7 @@
dropBuilder.append(stack);
dropBuilder.append('\n');
dropBuilder.append('\n');
+ oomPw.flush();
String oomString = oomSw.toString();
dropBuilder.append(oomString);
dropBuilder.append('\n');
@@ -1367,6 +1445,7 @@
catPw.println();
dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null);
}
+ catPw.flush();
dropBuilder.append(catSw.toString());
addErrorToDropBox("lowmem", null, "system_server", null,
null, tag.toString(), dropBuilder.toString(), null, null);
@@ -1409,6 +1488,55 @@
}
break;
}
+ case PERSIST_URI_GRANTS: {
+ writeGrantedUriPermissions();
+ break;
+ }
+ }
+ }
+ };
+
+ static final int COLLECT_PSS_BG_MSG = 1;
+
+ final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case COLLECT_PSS_BG_MSG: {
+ int i=0;
+ long start = SystemClock.uptimeMillis();
+ do {
+ ProcessRecord proc;
+ int oomAdj;
+ int pid;
+ synchronized (ActivityManagerService.this) {
+ if (i >= mPendingPssProcesses.size()) {
+ Slog.i(TAG, "Collected PSS of " + i + " processes in "
+ + (SystemClock.uptimeMillis()-start) + "ms");
+ mPendingPssProcesses.clear();
+ return;
+ }
+ proc = mPendingPssProcesses.get(i);
+ if (proc.thread != null) {
+ oomAdj = proc.setAdj;
+ pid = proc.pid;
+ } else {
+ proc = null;
+ oomAdj = 0;
+ pid = 0;
+ }
+ i++;
+ }
+ if (proc != null) {
+ long pss = Debug.getPss(pid);
+ synchronized (ActivityManagerService.this) {
+ if (proc.thread != null && proc.setAdj == oomAdj && proc.pid == pid) {
+ proc.baseProcessTracker.addPss(pss, true);
+ }
+ }
+ }
+ } while (true);
+ }
}
}
};
@@ -1421,6 +1549,7 @@
ServiceManager.addService("meminfo", new MemBinder(m));
ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
ServiceManager.addService("dbinfo", new DbBinder(m));
+ ServiceManager.addService("procstats", new ProcBinder(m));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(m));
}
@@ -1430,7 +1559,7 @@
mSelf.mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS);
mSystemThread.installSystemApplicationInfo(info);
-
+
synchronized (mSelf) {
ProcessRecord app = mSelf.newProcessRecordLocked(
mSystemThread.getApplicationThread(), info,
@@ -1452,6 +1581,8 @@
public void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
+ mStackSupervisor.setWindowManager(wm);
+ wm.createStack(HOME_STACK_ID, -1, StackBox.TASK_STACK_GOES_OVER, 1.0f);
}
public void startObservingNativeCrashes() {
@@ -1480,9 +1611,10 @@
context.setTheme(android.R.style.Theme_Holo);
m.mContext = context;
m.mFactoryTest = factoryTest;
- m.mMainStack = new ActivityStack(m, context, true, thr.mLooper);
m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());
+ m.mStackSupervisor = new ActivityStackSupervisor(m, context, thr.mLooper);
+
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
m.mAppOpsService.publish(context);
@@ -1493,14 +1625,18 @@
}
m.startRunning(null, null, null, null);
-
+
return context;
}
public static ActivityManagerService self() {
return mSelf;
}
-
+
+ public IAppOpsService getAppOpsService() {
+ return mAppOpsService;
+ }
+
static class AThread extends Thread {
ActivityManagerService mService;
Looper mLooper;
@@ -1510,6 +1646,7 @@
super("ActivityManager");
}
+ @Override
public void run() {
Looper.prepare();
@@ -1522,6 +1659,7 @@
synchronized (this) {
mService = m;
mLooper = Looper.myLooper();
+ Watchdog.getInstance().addThread(new Handler(mLooper), getName());
notifyAll();
}
@@ -1628,9 +1766,29 @@
}
}
+ static class ProcBinder extends Binder {
+ ActivityManagerService mActivityManagerService;
+ ProcBinder(ActivityManagerService activityManagerService) {
+ mActivityManagerService = activityManagerService;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump procstats from from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " without permission " + android.Manifest.permission.DUMP);
+ return;
+ }
+
+ mActivityManagerService.mProcessTracker.dump(fd, pw, args);
+ }
+ }
+
private ActivityManagerService() {
Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
-
+
mFgBroadcastQueue = new BroadcastQueue(this, "foreground", BROADCAST_FG_TIMEOUT);
mBgBroadcastQueue = new BroadcastQueue(this, "background", BROADCAST_BG_TIMEOUT);
mBroadcastQueues[0] = mFgBroadcastQueue;
@@ -1650,9 +1808,13 @@
: mBatteryStatsService.getActiveStatistics().getIsOnBattery();
mBatteryStatsService.getActiveStatistics().setCallback(this);
- mUsageStatsService = new UsageStatsService(new File(
- systemDir, "usagestats").toString());
+ mProcessTracker = new ProcessTracker(this, new File(systemDir, "procstats"));
+
+ mUsageStatsService = new UsageStatsService(new File(systemDir, "usagestats").toString());
mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"));
+
+ mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
+
mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
// User 0 is the first and only user that runs at boot.
@@ -1668,13 +1830,14 @@
mConfigurationSeq = mConfiguration.seq = 1;
mProcessStats.init();
-
+
mCompatModePackages = new CompatModePackages(this, systemDir);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
mProcessStatsThread = new Thread("ProcessStats") {
+ @Override
public void run() {
while (true) {
try {
@@ -1712,7 +1875,9 @@
// We need to tell all apps about the system property change.
ArrayList<IBinder> procs = new ArrayList<IBinder>();
synchronized(this) {
- for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+ final int NP = mProcessNames.getMap().size();
+ for (int ip=0; ip<NP; ip++) {
+ SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord app = apps.valueAt(ia);
@@ -1793,7 +1958,7 @@
(softIrq * 100) / total);
}
}
-
+
long[] cpuSpeedTimes = mProcessStats.getLastCpuSpeedTimes();
final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
synchronized(bstats) {
@@ -1820,6 +1985,15 @@
st.rel_stime-otherSTime);
ps.addSpeedStepTimes(cpuSpeedTimes);
pr.curCpuTime += (st.rel_utime+st.rel_stime) * 10;
+ } else if (st.uid >= Process.FIRST_APPLICATION_UID) {
+ BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
+ if (ps == null) {
+ st.batteryStats = ps = bstats.getProcessStatsLocked(st.uid,
+ "(Unknown)");
+ }
+ ps.addCpuTimeLocked(st.rel_utime-otherUTime,
+ st.rel_stime-otherSTime);
+ ps.addSpeedStepTimes(cpuSpeedTimes);
} else {
BatteryStatsImpl.Uid.Proc ps =
bstats.getProcessStatsLocked(st.name, st.pid);
@@ -1843,7 +2017,7 @@
}
}
}
-
+
@Override
public void batteryNeedsCpuUpdate() {
updateCpuStatsNow();
@@ -1881,7 +2055,9 @@
final void setFocusedActivityLocked(ActivityRecord r) {
if (mFocusedActivity != r) {
+ if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivitiyLocked: r=" + r);
mFocusedActivity = r;
+ mStackSupervisor.setFocusedStack(r);
if (r != null) {
mWindowManager.setFocusedApp(r.appToken, true);
}
@@ -1889,6 +2065,31 @@
}
}
+ @Override
+ public void setFocusedStack(int stackId) {
+ if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: stackId=" + stackId);
+ synchronized (ActivityManagerService.this) {
+ ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack != null) {
+ ActivityRecord r = stack.topRunningActivityLocked(null);
+ if (r != null) {
+ setFocusedActivityLocked(r);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void notifyActivityDrawn(IBinder token) {
+ if (DEBUG_VISBILITY) Slog.d(TAG, "notifyActivityDrawn: token=" + token);
+ synchronized (this) {
+ ActivityRecord r= mStackSupervisor.isInAnyStackLocked(token);
+ if (r != null) {
+ r.task.stack.notifyActivityDrawnLocked(r);
+ }
+ }
+ }
+
final void applyUpdateLockStateLocked(ActivityRecord r) {
// Modifications to the UpdateLock state are done on our handler, outside
// the activity manager's locks. The new state is determined based on the
@@ -1899,16 +2100,23 @@
mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r));
}
+ final void showAskCompatModeDialogLocked(ActivityRecord r) {
+ Message msg = Message.obtain();
+ msg.what = SHOW_COMPAT_MODE_DIALOG_MSG;
+ msg.obj = r.task.askedCompatMode ? null : r;
+ mHandler.sendMessage(msg);
+ }
+
private final void updateLruProcessInternalLocked(ProcessRecord app, int bestPos) {
// put it on the LRU to keep track of when it should be exited.
int lrui = mLruProcesses.indexOf(app);
if (lrui >= 0) mLruProcesses.remove(lrui);
-
+
int i = mLruProcesses.size()-1;
int skipTop = 0;
-
+
app.lruSeq = mLruSeq;
-
+
// compute the new weight for this process.
app.lastActivityTime = SystemClock.uptimeMillis();
if (app.activities.size() > 0) {
@@ -1919,22 +2127,22 @@
// If this process contains content providers, we want to keep
// it a little more strongly.
app.lruWeight = app.lastActivityTime - ProcessList.CONTENT_APP_IDLE_OFFSET;
- // Also don't let it kick out the first few "real" hidden processes.
- skipTop = ProcessList.MIN_HIDDEN_APPS;
+ // Also don't let it kick out the first few "real" cached processes.
+ skipTop = ProcessList.MIN_CACHED_APPS;
} else {
// If this process doesn't have activities, we less strongly
// want to keep it around, and generally want to avoid getting
// in front of any very recently used activities.
app.lruWeight = app.lastActivityTime - ProcessList.EMPTY_APP_IDLE_OFFSET;
- // Also don't let it kick out the first few "real" hidden processes.
- skipTop = ProcessList.MIN_HIDDEN_APPS;
+ // Also don't let it kick out the first few "real" cached processes.
+ skipTop = ProcessList.MIN_CACHED_APPS;
}
while (i >= 0) {
ProcessRecord p = mLruProcesses.get(i);
// If this app shouldn't be in front of the first N background
- // apps, then skip over that many that are currently hidden.
- if (skipTop > 0 && p.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+ // apps, then skip over that many that are currently cached.
+ if (skipTop > 0 && p.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
skipTop--;
}
if (p.lruWeight <= app.lruWeight || i < bestPos) {
@@ -1946,7 +2154,7 @@
if (i < 0) {
mLruProcesses.add(0, app);
}
-
+
// If the app is currently using a content provider or service,
// bump those processes as well.
if (app.connections.size() > 0) {
@@ -2004,14 +2212,14 @@
} catch (RemoteException e) {
}
}
-
+
boolean isNextTransitionForward() {
int transit = mWindowManager.getPendingAppTransition();
return transit == AppTransition.TRANSIT_ACTIVITY_OPEN
|| transit == AppTransition.TRANSIT_TASK_OPEN
|| transit == AppTransition.TRANSIT_TASK_TO_FRONT;
}
-
+
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
@@ -2039,14 +2247,14 @@
// come up (we have a pid but not yet its thread), so keep it.
if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
// If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName);
+ app.addPackage(info.packageName, mProcessTracker);
return app;
- } else {
- // An application record is attached to a previous process,
- // clean it up now.
- if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG, "App died: " + app);
- handleAppDiedLocked(app, true, true);
}
+
+ // An application record is attached to a previous process,
+ // clean it up now.
+ if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG, "App died: " + app);
+ handleAppDiedLocked(app, true, true);
}
String hostingNameStr = hostingName != null
@@ -2094,7 +2302,7 @@
}
} else {
// If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName);
+ app.addPackage(info.packageName, mProcessTracker);
}
// If the system is not ready yet, then hold off on starting this
@@ -2116,7 +2324,7 @@
boolean isAllowedWhileBooting(ApplicationInfo ai) {
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
-
+
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
if (app.pid > 0 && app.pid != MY_PID) {
@@ -2132,10 +2340,10 @@
mProcessesOnHold.remove(app);
updateCpuStats();
-
+
System.arraycopy(mProcDeaths, 0, mProcDeaths, 1, mProcDeaths.length-1);
mProcDeaths[0] = 0;
-
+
try {
int uid = app.uid;
@@ -2218,16 +2426,16 @@
app.batteryStats.incStartsLocked();
}
}
-
+
EventLog.writeEvent(EventLogTags.AM_PROC_START,
UserHandle.getUserId(uid), startResult.pid, uid,
app.processName, hostingType,
hostingNameStr != null ? hostingNameStr : "");
-
+
if (app.persistent) {
Watchdog.getInstance().processStarted(app.processName, startResult.pid);
}
-
+
StringBuilder buf = mStringBuilder;
buf.setLength(0);
buf.append("Start proc ");
@@ -2269,11 +2477,19 @@
}
}
- void updateUsageStats(ActivityRecord resumedComponent, boolean resumed) {
+ void updateUsageStats(ActivityRecord component, boolean resumed) {
+ if (DEBUG_SWITCH) Slog.d(TAG, "updateUsageStats: comp=" + component + "res=" + resumed);
+ final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
if (resumed) {
- mUsageStatsService.noteResumeComponent(resumedComponent.realActivity);
+ mUsageStatsService.noteResumeComponent(component.realActivity);
+ synchronized (stats) {
+ stats.noteActivityResumedLocked(component.app.uid);
+ }
} else {
- mUsageStatsService.notePauseComponent(resumedComponent.realActivity);
+ mUsageStatsService.notePauseComponent(component.realActivity);
+ synchronized (stats) {
+ stats.noteActivityPausedLocked(component.app.uid);
+ }
}
}
@@ -2312,8 +2528,7 @@
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- mMainStack.startActivityLocked(null, intent, null, aInfo,
- null, null, 0, 0, 0, null, 0, null, false, null);
+ mStackSupervisor.startHomeActivity(intent, aInfo);
}
}
@@ -2331,7 +2546,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags, userId);
-
+
if (info != null) {
ai = info.activityInfo;
}
@@ -2351,7 +2566,7 @@
if (mCheckedForSetup) {
return;
}
-
+
// We will show this screen if the current one is a different
// version than the last one shown, and we are not running in
// low-level factory test mode.
@@ -2360,12 +2575,12 @@
Settings.Global.getInt(resolver,
Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
mCheckedForSetup = true;
-
+
// See if we should be showing the platform update setup UI.
Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
List<ResolveInfo> ris = mSelf.mContext.getPackageManager()
.queryIntentActivities(intent, PackageManager.GET_META_DATA);
-
+
// We don't allow third party apps to replace this.
ResolveInfo ri = null;
for (int i=0; ris != null && i<ris.size(); i++) {
@@ -2375,7 +2590,7 @@
break;
}
}
-
+
if (ri != null) {
String vers = ri.activityInfo.metaData != null
? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
@@ -2390,13 +2605,13 @@
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
- mMainStack.startActivityLocked(null, intent, null, ri.activityInfo,
+ mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo,
null, null, 0, 0, 0, null, 0, null, false, null);
}
}
}
}
-
+
CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
}
@@ -2407,6 +2622,7 @@
}
}
+ @Override
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
synchronized (this) {
@@ -2414,6 +2630,7 @@
}
}
+ @Override
public void setFrontActivityScreenCompatMode(int mode) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setFrontActivityScreenCompatMode");
@@ -2422,6 +2639,7 @@
}
}
+ @Override
public int getPackageScreenCompatMode(String packageName) {
enforceNotIsolatedCaller("getPackageScreenCompatMode");
synchronized (this) {
@@ -2429,6 +2647,7 @@
}
}
+ @Override
public void setPackageScreenCompatMode(String packageName, int mode) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setPackageScreenCompatMode");
@@ -2437,6 +2656,7 @@
}
}
+ @Override
public boolean getPackageAskScreenCompat(String packageName) {
enforceNotIsolatedCaller("getPackageAskScreenCompat");
synchronized (this) {
@@ -2444,6 +2664,7 @@
}
}
+ @Override
public void setPackageAskScreenCompat(String packageName, boolean ask) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setPackageAskScreenCompat");
@@ -2452,11 +2673,6 @@
}
}
- void reportResumedActivityLocked(ActivityRecord r) {
- //Slog.i(TAG, "**** REPORT RESUME: " + r);
- updateUsageStats(r, true);
- }
-
private void dispatchProcessesChanged() {
int N;
synchronized (this) {
@@ -2469,6 +2685,7 @@
mPendingProcessChanges.clear();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "*** Delivering " + N + " process changes");
}
+
int i = mProcessObservers.beginBroadcast();
while (i > 0) {
i--;
@@ -2520,12 +2737,13 @@
}
for (int i=0; i<N; i++) {
PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
- mMainStack.startActivityUncheckedLocked(pal.r, pal.sourceRecord,
- pal.startFlags, doResume && i == (N-1), null);
+ mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, pal.startFlags,
+ doResume && i == (N-1), null);
}
mPendingActivityLaunches.clear();
}
+ @Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags,
@@ -2535,6 +2753,7 @@
startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId());
}
+ @Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags,
@@ -2542,11 +2761,13 @@
enforceNotIsolatedCaller("startActivity");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, true, "startActivity", null);
- return mMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
+ // TODO: Switch to user app stacks here.
+ return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
null, null, options, userId);
}
+ @Override
public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, String profileFile,
@@ -2555,12 +2776,14 @@
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, true, "startActivityAndWait", null);
WaitResult res = new WaitResult();
- mMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
+ // TODO: Switch to user app stacks here.
+ mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
res, null, options, UserHandle.getCallingUserId());
return res;
}
+ @Override
public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Configuration config,
@@ -2568,12 +2791,14 @@
enforceNotIsolatedCaller("startActivityWithConfig");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, true, "startActivityWithConfig", null);
- int ret = mMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags,
+ // TODO: Switch to user app stacks here.
+ int ret = mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
+ resolvedType, resultTo, resultWho, requestCode, startFlags,
null, null, null, config, options, userId);
return ret;
}
+ @Override
public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
@@ -2583,20 +2808,20 @@
if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
-
+
IIntentSender sender = intent.getTarget();
if (!(sender instanceof PendingIntentRecord)) {
throw new IllegalArgumentException("Bad PendingIntent object");
}
-
+
PendingIntentRecord pir = (PendingIntentRecord)sender;
-
+
synchronized (this) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
- if (mMainStack.mResumedActivity != null
- && mMainStack.mResumedActivity.info.applicationInfo.uid ==
- Binder.getCallingUid()) {
+ final ActivityStack stack = getFocusedStack();
+ if (stack.mResumedActivity != null &&
+ stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
}
}
@@ -2604,7 +2829,8 @@
resultTo, resultWho, requestCode, flagsMask, flagsValues, options);
return ret;
}
-
+
+ @Override
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) {
// Refuse possible leaked file descriptors
@@ -2613,7 +2839,7 @@
}
synchronized (this) {
- ActivityRecord r = mMainStack.isInStackLocked(callingActivity);
+ final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
if (r == null) {
ActivityOptions.abort(options);
return false;
@@ -2687,7 +2913,7 @@
}
final long origId = Binder.clearCallingIdentity();
- int res = mMainStack.startActivityLocked(r.app.thread, intent,
+ int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
r.resolvedType, aInfo, resultTo != null ? resultTo.appToken : null,
resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage, 0,
options, false, null);
@@ -2708,19 +2934,22 @@
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, true, "startActivityInPackage", null);
- int ret = mMainStack.startActivityMayWait(null, uid, callingPackage, intent, resolvedType,
+ // TODO: Switch to user app stacks here.
+ int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
null, null, null, null, options, userId);
return ret;
}
+ @Override
public final int startActivities(IApplicationThread caller, String callingPackage,
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options,
int userId) {
enforceNotIsolatedCaller("startActivities");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, true, "startActivity", null);
- int ret = mMainStack.startActivities(caller, -1, callingPackage, intents,
+ // TODO: Switch to user app stacks here.
+ int ret = mStackSupervisor.startActivities(caller, -1, callingPackage, intents,
resolvedTypes, resultTo, options, userId);
return ret;
}
@@ -2731,7 +2960,8 @@
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, true, "startActivityInPackage", null);
- int ret = mMainStack.startActivities(null, uid, callingPackage, intents, resolvedTypes,
+ // TODO: Switch to user app stacks here.
+ int ret = mStackSupervisor.startActivities(null, uid, callingPackage, intents, resolvedTypes,
resultTo, options, userId);
return ret;
}
@@ -2764,31 +2994,42 @@
mRecentTasks.add(0, task);
}
- public void setRequestedOrientation(IBinder token,
- int requestedOrientation) {
+ @Override
+ public void reportActivityFullyDrawn(IBinder token) {
synchronized (this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return;
+ }
+ r.reportFullyDrawnLocked();
+ }
+ }
+
+ @Override
+ public void setRequestedOrientation(IBinder token, int requestedOrientation) {
+ synchronized (this) {
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return;
}
final long origId = Binder.clearCallingIdentity();
mWindowManager.setAppOrientation(r.appToken, requestedOrientation);
Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration,
- r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
+ mConfiguration, r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
if (config != null) {
r.frozenBeforeDestroy = true;
if (!updateConfigurationLocked(config, r, false, false)) {
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
}
}
Binder.restoreCallingIdentity(origId);
}
}
+ @Override
public int getRequestedOrientation(IBinder token) {
synchronized (this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
@@ -2798,13 +3039,14 @@
/**
* This is the internal entry point for handling Activity.finish().
- *
+ *
* @param token The Binder token referencing the Activity we want to finish.
* @param resultCode Result code, if any, from this Activity.
* @param resultData Result data (Intent), if any, from this Activity.
- *
+ *
* @return Returns true if the activity successfully finished, or false if it is still running.
*/
+ @Override
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData) {
// Refuse possible leaked file descriptors
if (resultData != null && resultData.hasFileDescriptors() == true) {
@@ -2812,9 +3054,13 @@
}
synchronized(this) {
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return true;
+ }
if (mController != null) {
// Find the first activity that is not finishing.
- ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
+ ActivityRecord next = r.task.stack.topRunningActivityLocked(token, 0);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
@@ -2824,20 +3070,21 @@
mController = null;
Watchdog.getInstance().setActivityController(null);
}
-
+
if (!resumeOK) {
return false;
}
}
}
final long origId = Binder.clearCallingIdentity();
- boolean res = mMainStack.requestFinishActivityLocked(token, resultCode,
+ boolean res = r.task.stack.requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
Binder.restoreCallingIdentity(origId);
return res;
}
}
+ @Override
public final void finishHeavyWeightApp() {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2848,31 +3095,29 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
+
synchronized(this) {
if (mHeavyWeightProcess == null) {
return;
}
-
+
ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>(
mHeavyWeightProcess.activities);
for (int i=0; i<activities.size(); i++) {
ActivityRecord r = activities.get(i);
if (!r.finishing) {
- int index = mMainStack.indexOfTokenLocked(r.appToken);
- if (index >= 0) {
- mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED,
- null, "finish-heavy", true);
- }
+ r.task.stack.finishActivityLocked(r, Activity.RESULT_CANCELED,
+ null, "finish-heavy", true);
}
}
-
+
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
}
}
-
+
+ @Override
public void crashApplication(int uid, int initialPid, String packageName,
String message) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
@@ -2884,10 +3129,10 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
+
synchronized(this) {
ProcessRecord proc = null;
-
+
// Figure out which process to kill. We don't trust that initialPid
// still has any relation to current pids, so must scan through the
// list.
@@ -2901,21 +3146,19 @@
proc = p;
break;
}
- for (String str : p.pkgList) {
- if (str.equals(packageName)) {
- proc = p;
- }
+ if (p.pkgList.containsKey(packageName)) {
+ proc = p;
}
}
}
-
+
if (proc == null) {
Slog.w(TAG, "crashApplication: nothing for uid=" + uid
+ " initialPid=" + initialPid
+ " packageName=" + packageName);
return;
}
-
+
if (proc.thread != null) {
if (proc.pid == Process.myPid()) {
Log.w(TAG, "crashApplication: trying to crash self!");
@@ -2930,61 +3173,66 @@
}
}
}
-
+
+ @Override
public final void finishSubActivity(IBinder token, String resultWho,
int requestCode) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
- mMainStack.finishSubActivityLocked(token, resultWho, requestCode);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.task.stack.finishSubActivityLocked(r, resultWho, requestCode);
+ }
Binder.restoreCallingIdentity(origId);
}
}
+ @Override
public boolean finishActivityAffinity(IBinder token) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
- boolean res = mMainStack.finishActivityAffinityLocked(token);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ boolean res = false;
+ if (r != null) {
+ res = r.task.stack.finishActivityAffinityLocked(r);
+ }
Binder.restoreCallingIdentity(origId);
return res;
}
}
+ @Override
public boolean willActivityBeVisible(IBinder token) {
synchronized(this) {
- int i;
- for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
- if (r.appToken == token) {
- return true;
- }
- if (r.fullscreen && !r.finishing) {
- return false;
- }
+ ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack != null) {
+ return stack.willActivityBeVisibleLocked(token);
}
- return true;
+ return false;
}
}
-
+
+ @Override
public void overridePendingTransition(IBinder token, String packageName,
int enterAnim, int exitAnim) {
synchronized(this) {
- ActivityRecord self = mMainStack.isInStackLocked(token);
+ ActivityRecord self = ActivityRecord.isInStackLocked(token);
if (self == null) {
return;
}
final long origId = Binder.clearCallingIdentity();
-
+
if (self.state == ActivityState.RESUMED
|| self.state == ActivityState.PAUSING) {
mWindowManager.overridePendingAppTransition(packageName,
enterAnim, exitAnim, null);
}
-
+
Binder.restoreCallingIdentity(origId);
}
}
-
+
/**
* Main function for removing an existing process from the activity manager
* as a result of that process going away. Clears out all connections
@@ -3001,21 +3249,10 @@
clearProfilerLocked();
}
- // Just in case...
- if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) {
- if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG,
- "App died while pausing: " + mMainStack.mPausingActivity);
- mMainStack.mPausingActivity = null;
- }
- if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) {
- mMainStack.mLastPausedActivity = null;
- }
-
- // Remove this application's activities from active lists.
- boolean hasVisibleActivities = mMainStack.removeHistoryRecordsForAppLocked(app);
+ mStackSupervisor.handleAppDiedLocked(app, restarting);
app.activities.clear();
-
+
if (app.instrumentationClass != null) {
Slog.w(TAG, "Crash of app " + app.processName
+ " running instrumentation " + app.instrumentationClass);
@@ -3023,19 +3260,6 @@
info.putString("shortMsg", "Process crashed.");
finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
}
-
- if (!restarting) {
- if (!mMainStack.resumeTopActivityLocked(null)) {
- // If there was nothing to resume, and we are not already
- // restarting this process, but there is a visible activity that
- // is hosted by the process... then make sure all visible
- // activities are running, taking care of restarting this
- // process.
- if (hasVisibleActivities) {
- mMainStack.ensureActivitiesVisibleLocked(null, 0);
- }
- }
- }
}
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
@@ -3064,7 +3288,7 @@
IApplicationThread thread) {
mProcDeaths[0]++;
-
+
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
stats.noteProcessDiedLocked(app.info.uid, pid);
@@ -3091,12 +3315,12 @@
boolean haveBg = false;
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
- if (rec.thread != null && rec.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+ if (rec.thread != null && rec.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
haveBg = true;
break;
}
}
-
+
if (!haveBg) {
EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
long now = SystemClock.uptimeMillis();
@@ -3178,6 +3402,7 @@
// Use a FileObserver to detect when traces finish writing.
// The order of traces is considered important to maintain for legibility.
FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) {
+ @Override
public synchronized void onEvent(int event, String path) { notify(); }
};
@@ -3303,7 +3528,7 @@
File lastTracesFile = null;
File curTracesFile = null;
for (int i=9; i>=0; i--) {
- String name = String.format("slow%02d.txt", i);
+ String name = String.format(Locale.US, "slow%02d.txt", i);
curTracesFile = new File(tracesDir, name);
if (curTracesFile.exists()) {
if (lastTracesFile != null) {
@@ -3343,7 +3568,7 @@
if (MONITOR_CPU_USAGE) {
updateCpuStatsNow();
}
-
+
synchronized (this) {
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
if (mShuttingDown) {
@@ -3356,7 +3581,7 @@
Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
return;
}
-
+
// In case we come through here for the same app before completing
// this one, mark as anring now so we will bail out.
app.notResponding = true;
@@ -3367,11 +3592,11 @@
// Dump thread traces as quickly as we can, starting with "interesting" processes.
firstPids.add(app.pid);
-
+
int parentPid = app.pid;
if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid;
if (parentPid != app.pid) firstPids.add(parentPid);
-
+
if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
@@ -3446,7 +3671,7 @@
// Unless configured otherwise, swallow ANRs in background processes & kill the process.
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-
+
synchronized (this) {
if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
Slog.w(TAG, "Killing " + app + ": background ANR");
@@ -3455,16 +3680,16 @@
Process.killProcessQuiet(app.pid);
return;
}
-
+
// Set the app's notResponding state, and look up the errorReportReceiver
makeAppNotRespondingLocked(app,
activity != null ? activity.shortComponentName : null,
annotation != null ? "ANR " + annotation : "ANR",
info.toString());
-
+
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
- HashMap map = new HashMap();
+ HashMap<String, Object> map = new HashMap<String, Object>();
msg.what = SHOW_NOT_RESPONDING_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
@@ -3472,7 +3697,7 @@
if (activity != null) {
map.put("activity", activity);
}
-
+
mHandler.sendMessage(msg);
}
}
@@ -3500,7 +3725,8 @@
});
}
}
-
+
+ @Override
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
@@ -3532,17 +3758,21 @@
android.Manifest.permission.CLEAR_APP_USER_DATA,
pid, uid, -1, true)
== PackageManager.PERMISSION_GRANTED) {
- forceStopPackageLocked(packageName, pkgUid);
+ forceStopPackageLocked(packageName, pkgUid, "clear data");
} else {
throw new SecurityException(pid+" does not have permission:"+
android.Manifest.permission.CLEAR_APP_USER_DATA+" to clear data" +
"for process:"+packageName);
}
}
-
+
try {
- //clear application user data
+ // Clear application user data
pm.clearApplicationUserData(packageName, observer, userId);
+
+ // Remove all permissions granted from/to this package
+ removeUriPermissionsForPackageLocked(packageName, userId, true);
+
Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
Uri.fromParts("package", packageName, null));
intent.putExtra(Intent.EXTRA_UID, pkgUid);
@@ -3556,6 +3786,7 @@
return true;
}
+ @Override
public void killBackgroundProcesses(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED &&
@@ -3592,6 +3823,7 @@
}
}
+ @Override
public void killAllBackgroundProcesses() {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3602,12 +3834,14 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
+
long callingId = Binder.clearCallingIdentity();
try {
synchronized(this) {
ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
- for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+ final int NP = mProcessNames.getMap().size();
+ for (int ip=0; ip<NP; ip++) {
+ SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord app = apps.valueAt(ia);
@@ -3617,13 +3851,13 @@
}
if (app.removed) {
procs.add(app);
- } else if (app.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+ } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
app.removed = true;
procs.add(app);
}
}
}
-
+
int N = procs.size();
for (int i=0; i<N; i++) {
removeProcessLocked(procs.get(i), false, true, "kill all background");
@@ -3634,6 +3868,7 @@
}
}
+ @Override
public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3644,7 +3879,8 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ final int callingPid = Binder.getCallingPid();
+ userId = handleIncomingUser(callingPid, Binder.getCallingUid(),
userId, true, true, "forceStopPackage", null);
long callingId = Binder.clearCallingIdentity();
try {
@@ -3670,7 +3906,7 @@
+ packageName + ": " + e);
}
if (isUserRunningLocked(user, false)) {
- forceStopPackageLocked(packageName, pkgUid);
+ forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
}
}
}
@@ -3682,7 +3918,8 @@
/*
* The pkg name and app id have to be specified.
*/
- public void killApplicationWithAppId(String pkg, int appid) {
+ @Override
+ public void killApplicationWithAppId(String pkg, int appid, String reason) {
if (pkg == null) {
return;
}
@@ -3698,7 +3935,10 @@
Message msg = mHandler.obtainMessage(KILL_APPLICATION_MSG);
msg.arg1 = appid;
msg.arg2 = 0;
- msg.obj = pkg;
+ Bundle bundle = new Bundle();
+ bundle.putString("pkg", pkg);
+ bundle.putString("reason", reason);
+ msg.obj = bundle;
mHandler.sendMessage(msg);
} else {
throw new SecurityException(callerUid + " cannot kill pkg: " +
@@ -3706,6 +3946,7 @@
}
}
+ @Override
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
@@ -3743,39 +3984,67 @@
}
mWindowManager.closeSystemDialogs(reason);
- for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
- if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
- r.stack.finishActivityLocked(r, i,
- Activity.RESULT_CANCELED, null, "close-sys", true);
- }
- }
+ mStackSupervisor.closeSystemDialogsLocked();
broadcastIntentLocked(null, null, intent, null,
null, 0, null, null, null, AppOpsManager.OP_NONE, false, false, -1,
Process.SYSTEM_UID, UserHandle.USER_ALL);
}
- public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
- throws RemoteException {
+ @Override
+ public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids) {
enforceNotIsolatedCaller("getProcessMemoryInfo");
Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
for (int i=pids.length-1; i>=0; i--) {
+ ProcessRecord proc;
+ int oomAdj;
+ synchronized (this) {
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pids[i]);
+ oomAdj = proc != null ? proc.setAdj : 0;
+ }
+ }
infos[i] = new Debug.MemoryInfo();
Debug.getMemoryInfo(pids[i], infos[i]);
+ if (proc != null) {
+ synchronized (this) {
+ if (proc.thread != null && proc.setAdj == oomAdj) {
+ // Record this for posterity if the process has been stable.
+ proc.baseProcessTracker.addPss(infos[i].getTotalPss(), false);
+ }
+ }
+ }
}
return infos;
}
- public long[] getProcessPss(int[] pids) throws RemoteException {
+ @Override
+ public long[] getProcessPss(int[] pids) {
enforceNotIsolatedCaller("getProcessPss");
long[] pss = new long[pids.length];
for (int i=pids.length-1; i>=0; i--) {
+ ProcessRecord proc;
+ int oomAdj;
+ synchronized (this) {
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pids[i]);
+ oomAdj = proc != null ? proc.setAdj : 0;
+ }
+ }
pss[i] = Debug.getPss(pids[i]);
+ if (proc != null) {
+ synchronized (this) {
+ if (proc.thread != null && proc.setAdj == oomAdj) {
+ // Record this for posterity if the process has been stable.
+ proc.baseProcessTracker.addPss(pss[i], false);
+ }
+ }
+ }
}
return pss;
}
+ @Override
public void killApplicationProcess(String processName, int uid) {
if (processName == null) {
return;
@@ -3803,9 +4072,9 @@
}
}
- private void forceStopPackageLocked(final String packageName, int uid) {
+ private void forceStopPackageLocked(final String packageName, int uid, String reason) {
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
- false, true, false, UserHandle.getUserId(uid));
+ false, true, false, UserHandle.getUserId(uid), reason);
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
if (!mProcessesReady) {
@@ -3820,8 +4089,8 @@
MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
}
- private void forceStopUserLocked(int userId) {
- forceStopPackageLocked(null, -1, false, false, true, false, userId);
+ private void forceStopUserLocked(int userId, String reason) {
+ forceStopPackageLocked(null, -1, false, false, true, false, userId, reason);
Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
@@ -3841,7 +4110,9 @@
// same UID (except for the system or root user), and all whose name
// matches the package name.
final String procNamePrefix = packageName != null ? (packageName + ":") : null;
- for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+ final int NP = mProcessNames.getMap().size();
+ for (int ip=0; ip<NP; ip++) {
+ SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord app = apps.valueAt(ia);
@@ -3880,7 +4151,7 @@
if (userId != UserHandle.USER_ALL && app.userId != userId) {
continue;
}
- if (!app.pkgList.contains(packageName)) {
+ if (!app.pkgList.containsKey(packageName)) {
continue;
}
}
@@ -3893,7 +4164,7 @@
procs.add(app);
}
}
-
+
int N = procs.size();
for (int i=0; i<N; i++) {
removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
@@ -3903,7 +4174,7 @@
private final boolean forceStopPackageLocked(String name, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
- boolean evenPersistent, int userId) {
+ boolean evenPersistent, int userId, String reason) {
int i;
int N;
@@ -3921,15 +4192,15 @@
if (doit) {
if (name != null) {
- Slog.i(TAG, "Force stopping package " + name + " appid=" + appId
- + " user=" + userId);
+ Slog.i(TAG, "Force stopping " + name + " appid=" + appId
+ + " user=" + userId + ": " + reason);
} else {
- Slog.i(TAG, "Force stopping user " + userId);
+ Slog.i(TAG, "Force stopping u" + userId + ": " + reason);
}
- Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
- while (badApps.hasNext()) {
- SparseArray<Long> ba = badApps.next();
+ final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+ for (int ip=pmap.size()-1; ip>=0; ip--) {
+ SparseArray<Long> ba = pmap.valueAt(ip);
for (i=ba.size()-1; i>=0; i--) {
boolean remove = false;
final int entUid = ba.keyAt(i);
@@ -3951,45 +4222,20 @@
}
}
if (ba.size() == 0) {
- badApps.remove();
+ pmap.removeAt(ip);
}
}
}
boolean didSomething = killPackageProcessesLocked(name, appId, userId,
-100, callerWillRestart, true, doit, evenPersistent,
- name == null ? ("force stop user " + userId) : ("force stop " + name));
-
- TaskRecord lastTask = null;
- for (i=0; i<mMainStack.mHistory.size(); i++) {
- ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
- final boolean samePackage = r.packageName.equals(name)
- || (name == null && r.userId == userId);
- if ((userId == UserHandle.USER_ALL || r.userId == userId)
- && (samePackage || r.task == lastTask)
- && (r.app == null || evenPersistent || !r.app.persistent)) {
- if (!doit) {
- if (r.finishing) {
- // If this activity is just finishing, then it is not
- // interesting as far as something to stop.
- continue;
- }
- return true;
- }
- didSomething = true;
- Slog.i(TAG, " Force finishing activity " + r);
- if (samePackage) {
- if (r.app != null) {
- r.app.removed = true;
- }
- r.app = null;
- }
- lastTask = r.task;
- if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "force-stop", true)) {
- i--;
- }
+ name == null ? ("stop user " + userId) : ("stop " + name));
+
+ if (mStackSupervisor.forceStopPackageLocked(name, doit, evenPersistent, userId)) {
+ if (!doit) {
+ return true;
}
+ didSomething = true;
}
if (mServices.forceStopLocked(name, userId, evenPersistent, doit)) {
@@ -4017,6 +4263,9 @@
removeDyingProviderLocked(null, providers.get(i), true);
}
+ // Remove transient permissions granted from/to this package/user
+ removeUriPermissionsForPackageLocked(name, userId, false);
+
if (name == null) {
// Remove pending intents. For now we only do this when force
// stopping users, because we have some problems when doing this
@@ -4077,11 +4326,11 @@
}
}
if (mBooted) {
- mMainStack.resumeTopActivityLocked(null);
- mMainStack.scheduleIdleLocked();
+ mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.scheduleIdleLocked();
}
}
-
+
return didSomething;
}
@@ -4111,7 +4360,7 @@
handleAppDiedLocked(app, true, allowRestart);
mLruProcesses.remove(app);
Process.killProcessQuiet(pid);
-
+
if (app.persistent && !app.isolated) {
if (!callerWillRestart) {
addAppLocked(app.info, false);
@@ -4122,7 +4371,7 @@
} else {
mRemovedProcesses.add(app);
}
-
+
return needRestart;
}
@@ -4134,9 +4383,9 @@
if (knownApp != null && knownApp.thread == null) {
mPidsSelfLocked.remove(pid);
gone = true;
- }
+ }
}
-
+
if (gone) {
Slog.w(TAG, "Process " + app + " failed to attach");
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
@@ -4216,20 +4465,20 @@
if (localLOGV) Slog.v(
TAG, "Binding process pid " + pid + " to record " + app);
- String processName = app.processName;
+ final String processName = app.processName;
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
- app.resetPackageList();
+ app.resetPackageList(mProcessTracker);
startProcessLocked(app, "link fail", processName);
return false;
}
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
-
+
app.thread = thread;
app.curAdj = app.setAdj = -100;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -4242,12 +4491,12 @@
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
- List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
+ List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
if (!normalMode) {
Slog.i(TAG, "Launching preboot mode app: " + app);
}
-
+
if (localLOGV) Slog.v(
TAG, "New app record " + app
+ " thread=" + thread.asBinder() + " pid=" + pid);
@@ -4285,7 +4534,7 @@
|| (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
}
-
+
ensurePackageDexOpt(app.instrumentationInfo != null
? app.instrumentationInfo.packageName
: app.info.packageName);
@@ -4315,7 +4564,7 @@
// an infinite loop of restarting processes...
Slog.w(TAG, "Exception thrown during bind!", e);
- app.resetPackageList();
+ app.resetPackageList(mProcessTracker);
app.unlinkDeathRecipient();
startProcessLocked(app, "bind fail", processName);
return false;
@@ -4331,23 +4580,13 @@
boolean didSomething = false;
// See if the top visible activity is waiting to run in this process...
- ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
- if (hr != null && normalMode) {
- if (hr.app == null && app.uid == hr.info.applicationInfo.uid
- && processName.equals(hr.processName)) {
- try {
- if (mHeadless) {
- Slog.e(TAG, "Starting activities not supported on headless device: " + hr);
- } else if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
- didSomething = true;
- }
- } catch (Exception e) {
- Slog.w(TAG, "Exception in new application when starting activity "
- + hr.intent.getComponent().flattenToShortString(), e);
- badApp = true;
+ if (normalMode) {
+ try {
+ if (mStackSupervisor.attachApplicationLocked(app, mHeadless)) {
+ didSomething = true;
}
- } else {
- mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0);
+ } catch (Exception e) {
+ badApp = true;
}
}
@@ -4363,7 +4602,7 @@
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
- didSomething = sendPendingBroadcastsLocked(app);
+ didSomething |= sendPendingBroadcastsLocked(app);
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it 'bad'
badApp = true;
@@ -4398,6 +4637,7 @@
return true;
}
+ @Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
@@ -4407,13 +4647,16 @@
}
}
+ @Override
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
- ActivityRecord r = mMainStack.activityIdleInternal(token, false, config);
- if (stopProfiling) {
- synchronized (this) {
- if (mProfileProc == r.app) {
- if (mProfileFd != null) {
+ synchronized (this) {
+ ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack != null) {
+ ActivityRecord r =
+ mStackSupervisor.activityIdleInternalLocked(token, false, config);
+ if (stopProfiling) {
+ if ((mProfileProc == r.app) && (mProfileFd != null)) {
try {
mProfileFd.close();
} catch (IOException e) {
@@ -4436,11 +4679,13 @@
}
}
+ @Override
public void showBootMessage(final CharSequence msg, final boolean always) {
enforceNotIsolatedCaller("showBootMessage");
mWindowManager.showBootMessage(msg, always);
}
+ @Override
public void dismissKeyguardOnNextActivity() {
enforceNotIsolatedCaller("dismissKeyguardOnNextActivity");
final long token = Binder.clearCallingIdentity();
@@ -4450,7 +4695,7 @@
mLockScreenShown = false;
comeOutOfSleepIfNeededLocked();
}
- mMainStack.dismissKeyguardOnNextActivityLocked();
+ mStackSupervisor.setDismissKeyguard(true);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -4468,7 +4713,8 @@
if (pkgs != null) {
for (String pkg : pkgs) {
synchronized (ActivityManagerService.this) {
- if (forceStopPackageLocked(pkg, -1, false, false, false, false, 0)) {
+ if (forceStopPackageLocked(pkg, -1, false, false, false, false, 0,
+ "finished booting")) {
setResultCode(Activity.RESULT_OK);
return;
}
@@ -4506,8 +4752,19 @@
final int userId = mStartedUsers.keyAt(i);
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null,
+ broadcastIntentLocked(null, null, intent, null,
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ synchronized (ActivityManagerService.this) {
+ requestPssAllProcsLocked(SystemClock.uptimeMillis(),
+ true);
+ }
+ }
+ },
+ 0, null, null,
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
AppOpsManager.OP_NONE, false, false, MY_PID, Process.SYSTEM_UID,
userId);
@@ -4536,18 +4793,31 @@
}
}
+ @Override
public final void activityResumed(IBinder token) {
final long origId = Binder.clearCallingIdentity();
- mMainStack.activityResumed(token);
+ synchronized(this) {
+ ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack != null) {
+ ActivityRecord.activityResumedLocked(token);
+ }
+ }
Binder.restoreCallingIdentity(origId);
}
+ @Override
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
- mMainStack.activityPaused(token, false);
+ synchronized(this) {
+ ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack != null) {
+ stack.activityPausedLocked(token, false);
+ }
+ }
Binder.restoreCallingIdentity(origId);
}
+ @Override
public final void activityStopped(IBinder token, Bundle icicle, Bitmap thumbnail,
CharSequence description) {
if (localLOGV) Slog.v(
@@ -4563,9 +4833,9 @@
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
- r = mMainStack.isInStackLocked(token);
+ r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- r.stack.activityStoppedLocked(r, icicle, thumbnail, description);
+ r.task.stack.activityStoppedLocked(r, icicle, thumbnail, description);
}
}
@@ -4578,11 +4848,18 @@
Binder.restoreCallingIdentity(origId);
}
+ @Override
public final void activityDestroyed(IBinder token) {
if (DEBUG_SWITCH) Slog.v(TAG, "ACTIVITY DESTROYED: " + token);
- mMainStack.activityDestroyed(token);
+ synchronized (this) {
+ ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack != null) {
+ stack.activityDestroyedLocked(token);
+ }
+ }
}
+ @Override
public String getCallingPackage(IBinder token) {
synchronized (this) {
ActivityRecord r = getCallingRecordLocked(token);
@@ -4590,6 +4867,7 @@
}
}
+ @Override
public ComponentName getCallingActivity(IBinder token) {
synchronized (this) {
ActivityRecord r = getCallingRecordLocked(token);
@@ -4598,16 +4876,17 @@
}
private ActivityRecord getCallingRecordLocked(IBinder token) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return null;
}
return r.resultTo;
}
+ @Override
public ComponentName getActivityClassForToken(IBinder token) {
synchronized(this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return null;
}
@@ -4615,9 +4894,10 @@
}
}
+ @Override
public String getPackageForToken(IBinder token) {
synchronized(this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return null;
}
@@ -4625,6 +4905,7 @@
}
}
+ @Override
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
@@ -4695,7 +4976,7 @@
}
}
}
-
+
IIntentSender getIntentSenderLocked(int type, String packageName,
int callingUid, int userId, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
@@ -4704,7 +4985,7 @@
Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
ActivityRecord activity = null;
if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
- activity = mMainStack.isInStackLocked(token);
+ activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
return null;
}
@@ -4761,6 +5042,7 @@
return rec;
}
+ @Override
public void cancelIntentSender(IIntentSender sender) {
if (!(sender instanceof PendingIntentRecord)) {
return;
@@ -4794,6 +5076,7 @@
}
}
+ @Override
public String getPackageForIntentSender(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return null;
@@ -4806,6 +5089,7 @@
return null;
}
+ @Override
public int getUidForIntentSender(IIntentSender sender) {
if (sender instanceof PendingIntentRecord) {
try {
@@ -4817,6 +5101,7 @@
return -1;
}
+ @Override
public boolean isIntentSenderTargetedToPackage(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return false;
@@ -4838,6 +5123,7 @@
return false;
}
+ @Override
public boolean isIntentSenderAnActivity(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return false;
@@ -4853,6 +5139,7 @@
return false;
}
+ @Override
public Intent getIntentForIntentSender(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return null;
@@ -4865,16 +5152,18 @@
return null;
}
+ @Override
public void setProcessLimit(int max) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
"setProcessLimit()");
synchronized (this) {
- mProcessLimit = max < 0 ? ProcessList.MAX_HIDDEN_APPS : max;
+ mProcessLimit = max < 0 ? ProcessList.MAX_CACHED_APPS : max;
mProcessLimitOverride = max;
}
trimApplications();
}
+ @Override
public int getProcessLimit() {
synchronized (this) {
return mProcessLimitOverride;
@@ -4900,7 +5189,8 @@
updateOomAdjLocked();
}
}
-
+
+ @Override
public void setProcessForeground(IBinder token, int pid, boolean isForeground) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
"setProcessForeground()");
@@ -4924,6 +5214,7 @@
}
if (isForeground && token != null) {
ForegroundToken newToken = new ForegroundToken() {
+ @Override
public void binderDied() {
foregroundTokenDied(this);
}
@@ -4958,6 +5249,7 @@
mActivityManagerService = activityManagerService;
}
+ @Override
public boolean checkPermission(String permission, int pid, int uid) {
return mActivityManagerService.checkPermission(permission, pid,
uid) == PackageManager.PERMISSION_GRANTED;
@@ -4965,12 +5257,14 @@
}
class IntentFirewallInterface implements IntentFirewall.AMSInterface {
+ @Override
public int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
return ActivityManagerService.this.checkComponentPermission(permission, pid, uid,
owningUid, exported);
}
+ @Override
public Object getAMSLock() {
return ActivityManagerService.this;
}
@@ -5009,6 +5303,7 @@
*
* This can be called with or without the global lock held.
*/
+ @Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
@@ -5130,6 +5425,38 @@
return readMet && writeMet;
}
+ private ProviderInfo getProviderInfoLocked(String authority, int userHandle) {
+ ProviderInfo pi = null;
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle);
+ if (cpr != null) {
+ pi = cpr.info;
+ } else {
+ try {
+ pi = AppGlobals.getPackageManager().resolveContentProvider(
+ authority, PackageManager.GET_URI_PERMISSION_PATTERNS, userHandle);
+ } catch (RemoteException ex) {
+ }
+ }
+ return pi;
+ }
+
+ private UriPermission findOrCreateUriPermissionLocked(
+ String sourcePkg, String targetPkg, int targetUid, Uri uri) {
+ HashMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
+ if (targetUris == null) {
+ targetUris = Maps.newHashMap();
+ mGrantedUriPermissions.put(targetUid, targetUris);
+ }
+
+ UriPermission perm = targetUris.get(uri);
+ if (perm == null) {
+ perm = new UriPermission(sourcePkg, targetPkg, targetUid, uri);
+ targetUris.put(uri, perm);
+ }
+
+ return perm;
+ }
+
private final boolean checkUriPermissionLocked(Uri uri, int uid,
int modeFlags) {
// Root gets to do everything.
@@ -5143,6 +5470,7 @@
return (modeFlags&perm.modeFlags) == modeFlags;
}
+ @Override
public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
enforceNotIsolatedCaller("checkUriPermission");
@@ -5176,6 +5504,7 @@
*/
int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
Uri uri, int modeFlags, int lastTargetUid) {
+ final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
@@ -5196,20 +5525,8 @@
return -1;
}
- String name = uri.getAuthority();
- ProviderInfo pi = null;
- ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
- UserHandle.getUserId(callingUid));
- if (cpr != null) {
- pi = cpr.info;
- } else {
- try {
- pi = pm.resolveContentProvider(name,
- PackageManager.GET_URI_PERMISSION_PATTERNS,
- UserHandle.getUserId(callingUid));
- } catch (RemoteException ex) {
- }
- }
+ final String authority = uri.getAuthority();
+ final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid));
if (pi == null) {
Slog.w(TAG, "No content provider found for permission check: " + uri.toSafeString());
return -1;
@@ -5231,7 +5548,7 @@
if (targetUid >= 0) {
// First... does the target actually need this permission?
- if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
+ if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags) && !persist) {
// No need to grant the target this permission.
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Target " + targetPkg + " already has full permission to " + uri);
@@ -5250,7 +5567,7 @@
allowed = false;
}
}
- if (allowed) {
+ if (allowed && !persist) {
return -1;
}
}
@@ -5294,6 +5611,7 @@
return targetUid;
}
+ @Override
public int checkGrantUriPermission(int callingUid, String targetPkg,
Uri uri, int modeFlags) {
enforceNotIsolatedCaller("checkGrantUriPermission");
@@ -5302,8 +5620,9 @@
}
}
- void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg,
- Uri uri, int modeFlags, UriPermissionOwner owner) {
+ void grantUriPermissionUncheckedLocked(
+ int targetUid, String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
+ final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
@@ -5316,32 +5635,20 @@
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Granting " + targetPkg + "/" + targetUid + " permission to " + uri);
-
- HashMap<Uri, UriPermission> targetUris
- = mGrantedUriPermissions.get(targetUid);
- if (targetUris == null) {
- targetUris = new HashMap<Uri, UriPermission>();
- mGrantedUriPermissions.put(targetUid, targetUris);
+
+ final String authority = uri.getAuthority();
+ final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(targetUid));
+ if (pi == null) {
+ Slog.w(TAG, "No content provider found for grant: " + uri.toSafeString());
+ return;
}
- UriPermission perm = targetUris.get(uri);
- if (perm == null) {
- perm = new UriPermission(targetUid, uri);
- targetUris.put(uri, perm);
- }
-
- perm.modeFlags |= modeFlags;
- if (owner == null) {
- perm.globalModeFlags |= modeFlags;
- } else {
- if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
- perm.readOwners.add(owner);
- owner.addReadPermission(perm);
- }
- if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
- perm.writeOwners.add(owner);
- owner.addWritePermission(perm);
- }
+ final UriPermission perm = findOrCreateUriPermissionLocked(
+ pi.packageName, targetPkg, targetUid, uri);
+ final boolean persistChanged = perm.grantModes(modeFlags, persist, owner);
+ if (persistChanged) {
+ mHandler.removeMessages(PERSIST_URI_GRANTS);
+ mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget();
}
}
@@ -5364,10 +5671,10 @@
final int targetUid;
final int flags;
- NeededUriGrants(String _targetPkg, int _targetUid, int _flags) {
- targetPkg = _targetPkg;
- targetUid = _targetUid;
- flags = _flags;
+ NeededUriGrants(String targetPkg, int targetUid, int flags) {
+ this.targetPkg = targetPkg;
+ this.targetUid = targetUid;
+ this.flags = flags;
}
}
@@ -5395,11 +5702,11 @@
return null;
}
if (data != null) {
- int target = checkGrantUriPermissionLocked(callingUid, targetPkg, data,
+ int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, data,
mode, needed != null ? needed.targetUid : -1);
- if (target > 0) {
+ if (targetUid > 0) {
if (needed == null) {
- needed = new NeededUriGrants(targetPkg, target, mode);
+ needed = new NeededUriGrants(targetPkg, targetUid, mode);
}
needed.add(data);
}
@@ -5408,12 +5715,12 @@
for (int i=0; i<clip.getItemCount(); i++) {
Uri uri = clip.getItemAt(i).getUri();
if (uri != null) {
- int target = -1;
- target = checkGrantUriPermissionLocked(callingUid, targetPkg, uri,
+ int targetUid = -1;
+ targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri,
mode, needed != null ? needed.targetUid : -1);
- if (target > 0) {
+ if (targetUid > 0) {
if (needed == null) {
- needed = new NeededUriGrants(targetPkg, target, mode);
+ needed = new NeededUriGrants(targetPkg, targetUid, mode);
}
needed.add(uri);
}
@@ -5457,6 +5764,7 @@
grantUriPermissionUncheckedFromIntentLocked(needed, owner);
}
+ @Override
public void grantUriPermission(IApplicationThread caller, String targetPkg,
Uri uri, int modeFlags) {
enforceNotIsolatedCaller("grantUriPermission");
@@ -5483,44 +5791,25 @@
if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION
|Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) {
HashMap<Uri, UriPermission> perms
- = mGrantedUriPermissions.get(perm.uid);
+ = mGrantedUriPermissions.get(perm.targetUid);
if (perms != null) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Removing " + perm.uid + " permission to " + perm.uri);
+ "Removing " + perm.targetUid + " permission to " + perm.uri);
perms.remove(perm.uri);
if (perms.size() == 0) {
- mGrantedUriPermissions.remove(perm.uid);
+ mGrantedUriPermissions.remove(perm.targetUid);
}
}
}
}
- private void revokeUriPermissionLocked(int callingUid, Uri uri,
- int modeFlags) {
- modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- if (modeFlags == 0) {
- return;
- }
+ private void revokeUriPermissionLocked(
+ int callingUid, Uri uri, int modeFlags, boolean persist) {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + uri);
- if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Revoking all granted permissions to " + uri);
-
final IPackageManager pm = AppGlobals.getPackageManager();
-
final String authority = uri.getAuthority();
- ProviderInfo pi = null;
- int userId = UserHandle.getUserId(callingUid);
- ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
- if (cpr != null) {
- pi = cpr.info;
- } else {
- try {
- pi = pm.resolveContentProvider(authority,
- PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
- } catch (RemoteException ex) {
- }
- }
+ final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid));
if (pi == null) {
Slog.w(TAG, "No content provider found for permission revoke: " + uri.toSafeString());
return;
@@ -5536,6 +5825,8 @@
//}
}
+ boolean persistChanged = false;
+
// Go through all of the permissions and remove any that match.
final List<String> SEGMENTS = uri.getPathSegments();
if (SEGMENTS != null) {
@@ -5565,8 +5856,8 @@
}
}
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Revoking " + perm.uid + " permission to " + perm.uri);
- perm.clearModes(modeFlags);
+ "Revoking " + perm.targetUid + " permission to " + perm.uri);
+ persistChanged |= perm.clearModes(modeFlags, persist);
if (perm.modeFlags == 0) {
it.remove();
}
@@ -5579,8 +5870,14 @@
}
}
}
+
+ if (persistChanged) {
+ mHandler.removeMessages(PERSIST_URI_GRANTS);
+ mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget();
+ }
}
+ @Override
public void revokeUriPermission(IApplicationThread caller, Uri uri,
int modeFlags) {
enforceNotIsolatedCaller("revokeUriPermission");
@@ -5596,6 +5893,7 @@
return;
}
+ final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
@@ -5603,26 +5901,64 @@
}
final IPackageManager pm = AppGlobals.getPackageManager();
-
final String authority = uri.getAuthority();
- ProviderInfo pi = null;
- ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, r.userId);
- if (cpr != null) {
- pi = cpr.info;
- } else {
- try {
- pi = pm.resolveContentProvider(authority,
- PackageManager.GET_URI_PERMISSION_PATTERNS, r.userId);
- } catch (RemoteException ex) {
- }
- }
+ final ProviderInfo pi = getProviderInfoLocked(authority, r.userId);
if (pi == null) {
Slog.w(TAG, "No content provider found for permission revoke: "
+ uri.toSafeString());
return;
}
- revokeUriPermissionLocked(r.uid, uri, modeFlags);
+ revokeUriPermissionLocked(r.uid, uri, modeFlags, persist);
+ }
+ }
+
+ /**
+ * Remove any {@link UriPermission} granted <em>from</em> or <em>to</em> the
+ * given package.
+ *
+ * @param packageName Package name to match, or {@code null} to apply to all
+ * packages.
+ * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
+ * to all users.
+ * @param persist If persistent grants should be removed.
+ */
+ private void removeUriPermissionsForPackageLocked(
+ String packageName, int userHandle, boolean persist) {
+ if (userHandle == UserHandle.USER_ALL && packageName == null) {
+ throw new IllegalArgumentException("Must narrow by either package or user");
+ }
+
+ boolean persistChanged = false;
+
+ final int size = mGrantedUriPermissions.size();
+ for (int i = 0; i < size; i++) {
+ // Only inspect grants matching user
+ if (userHandle == UserHandle.USER_ALL
+ || userHandle == UserHandle.getUserId(mGrantedUriPermissions.keyAt(i))) {
+ final Iterator<UriPermission> it = mGrantedUriPermissions.valueAt(i)
+ .values().iterator();
+ while (it.hasNext()) {
+ final UriPermission perm = it.next();
+
+ // Only inspect grants matching package
+ if (packageName == null || perm.sourcePkg.equals(packageName)
+ || perm.targetPkg.equals(packageName)) {
+ persistChanged |= perm.clearModes(~0, persist);
+
+ // Only remove when no modes remain; any persisted grants
+ // will keep this alive.
+ if (perm.modeFlags == 0) {
+ it.remove();
+ }
+ }
+ }
+ }
+ }
+
+ if (persistChanged) {
+ mHandler.removeMessages(PERSIST_URI_GRANTS);
+ mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget();
}
}
@@ -5677,6 +6013,104 @@
}
}
+ private void writeGrantedUriPermissions() {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG, "writeGrantedUriPermissions()");
+
+ // Snapshot permissions so we can persist without lock
+ ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
+ synchronized (this) {
+ final int size = mGrantedUriPermissions.size();
+ for (int i = 0 ; i < size; i++) {
+ for (UriPermission perm : mGrantedUriPermissions.valueAt(i).values()) {
+ if (perm.persistedModeFlags != 0) {
+ persist.add(perm.snapshot());
+ }
+ }
+ }
+ }
+
+ FileOutputStream fos = null;
+ try {
+ fos = mGrantFile.startWrite();
+
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(fos, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, TAG_URI_GRANTS);
+ for (UriPermission.Snapshot perm : persist) {
+ out.startTag(null, TAG_URI_GRANT);
+ writeIntAttribute(out, ATTR_USER_HANDLE, perm.userHandle);
+ out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
+ out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
+ out.attribute(null, ATTR_URI, String.valueOf(perm.uri));
+ writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
+ out.endTag(null, TAG_URI_GRANT);
+ }
+ out.endTag(null, TAG_URI_GRANTS);
+ out.endDocument();
+
+ mGrantFile.finishWrite(fos);
+ } catch (IOException e) {
+ if (fos != null) {
+ mGrantFile.failWrite(fos);
+ }
+ }
+ }
+
+ private void readGrantedUriPermissionsLocked() {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG, "readGrantedUriPermissions()");
+
+ FileInputStream fis = null;
+ try {
+ fis = mGrantFile.openRead();
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(fis, null);
+
+ int type;
+ while ((type = in.next()) != END_DOCUMENT) {
+ final String tag = in.getName();
+ if (type == START_TAG) {
+ if (TAG_URI_GRANT.equals(tag)) {
+ final int userHandle = readIntAttribute(in, ATTR_USER_HANDLE);
+ final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
+ final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
+ final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
+ final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
+
+ // Sanity check that provider still belongs to source package
+ final ProviderInfo pi = getProviderInfoLocked(
+ uri.getAuthority(), userHandle);
+ if (pi != null && sourcePkg.equals(pi.packageName)) {
+ int targetUid = -1;
+ try {
+ targetUid = AppGlobals.getPackageManager()
+ .getPackageUid(targetPkg, userHandle);
+ } catch (RemoteException e) {
+ }
+ if (targetUid != -1) {
+ final UriPermission perm = findOrCreateUriPermissionLocked(
+ sourcePkg, targetPkg, targetUid, uri);
+ perm.grantModes(modeFlags, true, null);
+ }
+ } else {
+ Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
+ + " but instead found " + pi);
+ }
+ }
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Missing grants is okay
+ } catch (IOException e) {
+ Log.wtf(TAG, "Failed reading Uri grants", e);
+ } catch (XmlPullParserException e) {
+ Log.wtf(TAG, "Failed reading Uri grants", e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ }
+
+ @Override
public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
synchronized (this) {
ProcessRecord app =
@@ -5691,14 +6125,15 @@
}
}
+ @Override
public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
- final long hiddenAppMem = mProcessList.getMemLevel(ProcessList.HIDDEN_APP_MIN_ADJ);
+ final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ);
outInfo.availMem = Process.getFreeMemory();
outInfo.totalMem = Process.getTotalMemory();
outInfo.threshold = homeAppMem;
- outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((hiddenAppMem-homeAppMem)/2));
- outInfo.hiddenAppThreshold = hiddenAppMem;
+ outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
+ outInfo.hiddenAppThreshold = cachedAppMem;
outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
ProcessList.SERVICE_ADJ);
outInfo.visibleAppThreshold = mProcessList.getMemLevel(
@@ -5711,12 +6146,12 @@
// TASK MANAGEMENT
// =========================================================
- public List getTasks(int maxNum, int flags,
+ @Override
+ public List<RunningTaskInfo> getTasks(int maxNum, int flags,
IThumbnailReceiver receiver) {
- ArrayList list = new ArrayList();
+ ArrayList<RunningTaskInfo> list = new ArrayList<RunningTaskInfo>();
- PendingThumbnailsRecord pending = null;
- IApplicationThread topThumbnail = null;
+ PendingThumbnailsRecord pending = new PendingThumbnailsRecord(receiver);
ActivityRecord topRecord = null;
synchronized(this) {
@@ -5742,88 +6177,20 @@
throw new SecurityException(msg);
}
- int pos = mMainStack.mHistory.size()-1;
- ActivityRecord next =
- pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
- ActivityRecord top = null;
- TaskRecord curTask = null;
- int numActivities = 0;
- int numRunning = 0;
- while (pos >= 0 && maxNum > 0) {
- final ActivityRecord r = next;
- pos--;
- next = pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
+ // TODO: Improve with MRU list from all ActivityStacks.
+ topRecord = mStackSupervisor.getTasksLocked(maxNum, receiver, pending, list);
- // Initialize state for next task if needed.
- if (top == null ||
- (top.state == ActivityState.INITIALIZING
- && top.task == r.task)) {
- top = r;
- curTask = r.task;
- numActivities = numRunning = 0;
- }
-
- // Add 'r' into the current task.
- numActivities++;
- if (r.app != null && r.app.thread != null) {
- numRunning++;
- }
-
- if (localLOGV) Slog.v(
- TAG, r.intent.getComponent().flattenToShortString()
- + ": task=" + r.task);
-
- // If the next one is a different task, generate a new
- // TaskInfo entry for what we have.
- if (next == null || next.task != curTask) {
- ActivityManager.RunningTaskInfo ci
- = new ActivityManager.RunningTaskInfo();
- ci.id = curTask.taskId;
- ci.baseActivity = r.intent.getComponent();
- ci.topActivity = top.intent.getComponent();
- if (top.thumbHolder != null) {
- ci.description = top.thumbHolder.lastDescription;
- }
- ci.numActivities = numActivities;
- ci.numRunning = numRunning;
- //System.out.println(
- // "#" + maxNum + ": " + " descr=" + ci.description);
- if (ci.thumbnail == null && receiver != null) {
- if (localLOGV) Slog.v(
- TAG, "State=" + top.state + "Idle=" + top.idle
- + " app=" + top.app
- + " thr=" + (top.app != null ? top.app.thread : null));
- if (top.state == ActivityState.RESUMED
- || top.state == ActivityState.PAUSING) {
- if (top.idle && top.app != null
- && top.app.thread != null) {
- topRecord = top;
- topThumbnail = top.app.thread;
- } else {
- top.thumbnailNeeded = true;
- }
- }
- if (pending == null) {
- pending = new PendingThumbnailsRecord(receiver);
- }
- pending.pendingRecords.add(top);
- }
- list.add(ci);
- maxNum--;
- top = null;
- }
- }
-
- if (pending != null) {
+ if (!pending.pendingRecords.isEmpty()) {
mPendingThumbnails.add(pending);
}
}
if (localLOGV) Slog.v(TAG, "We have pending thumbnails: " + pending);
- if (topThumbnail != null) {
+ if (topRecord != null) {
if (localLOGV) Slog.v(TAG, "Requesting top thumbnail");
try {
+ IApplicationThread topThumbnail = topRecord.app.thread;
topThumbnail.requestThumbnail(topRecord.appToken);
} catch (Exception e) {
Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
@@ -5845,6 +6212,7 @@
return list;
}
+ @Override
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags, int userId) {
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
@@ -5889,7 +6257,8 @@
}
rti.origActivity = tr.origActivity;
rti.description = tr.lastDescription;
-
+ rti.stackId = tr.stack.mStackId;
+
if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) {
// Check whether this activity is currently available.
try {
@@ -5917,49 +6286,55 @@
}
}
- private TaskRecord taskForIdLocked(int id) {
+ private TaskRecord recentTaskForIdLocked(int id) {
final int N = mRecentTasks.size();
- for (int i=0; i<N; i++) {
- TaskRecord tr = mRecentTasks.get(i);
- if (tr.taskId == id) {
- return tr;
+ for (int i=0; i<N; i++) {
+ TaskRecord tr = mRecentTasks.get(i);
+ if (tr.taskId == id) {
+ return tr;
+ }
}
- }
- return null;
+ return null;
}
+ @Override
public ActivityManager.TaskThumbnails getTaskThumbnails(int id) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
"getTaskThumbnails()");
- TaskRecord tr = taskForIdLocked(id);
+ TaskRecord tr = recentTaskForIdLocked(id);
if (tr != null) {
- return mMainStack.getTaskThumbnailsLocked(tr);
+ return tr.getTaskThumbnailsLocked();
}
}
return null;
}
+ @Override
public Bitmap getTaskTopThumbnail(int id) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
"getTaskTopThumbnail()");
- TaskRecord tr = taskForIdLocked(id);
+ TaskRecord tr = recentTaskForIdLocked(id);
if (tr != null) {
- return mMainStack.getTaskTopThumbnailLocked(tr);
+ return tr.getTaskTopThumbnailLocked();
}
}
return null;
}
+ @Override
public boolean removeSubTask(int taskId, int subTaskIndex) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
"removeSubTask()");
long ident = Binder.clearCallingIdentity();
try {
- return mMainStack.removeTaskActivitiesLocked(taskId, subTaskIndex,
- true) != null;
+ TaskRecord tr = recentTaskForIdLocked(taskId);
+ if (tr != null) {
+ return tr.removeTaskActivitiesLocked(subTaskIndex, true) != null;
+ }
+ return false;
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5967,6 +6342,8 @@
}
private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) {
+ mRecentTasks.remove(tr);
+ mStackSupervisor.removeTask(tr);
final boolean killProcesses = (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0;
Intent baseIntent = new Intent(
tr.intent != null ? tr.intent : tr.affinityIntent);
@@ -5983,14 +6360,15 @@
// Find any running processes associated with this app.
final String pkg = component.getPackageName();
ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
- HashMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
- for (SparseArray<ProcessRecord> uids : pmap.values()) {
- for (int i=0; i<uids.size(); i++) {
- ProcessRecord proc = uids.valueAt(i);
+ ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
+ for (int i=0; i<pmap.size(); i++) {
+ SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+ for (int j=0; j<uids.size(); j++) {
+ ProcessRecord proc = uids.valueAt(j);
if (proc.userId != tr.userId) {
continue;
}
- if (!proc.pkgList.contains(pkg)) {
+ if (!proc.pkgList.containsKey(pkg)) {
continue;
}
procs.add(proc);
@@ -6013,43 +6391,30 @@
}
}
+ @Override
public boolean removeTask(int taskId, int flags) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
"removeTask()");
long ident = Binder.clearCallingIdentity();
try {
- ActivityRecord r = mMainStack.removeTaskActivitiesLocked(taskId, -1,
- false);
- if (r != null) {
- mRecentTasks.remove(r.task);
- cleanUpRemovedTaskLocked(r.task, flags);
- return true;
- } else {
- TaskRecord tr = null;
- int i=0;
- while (i < mRecentTasks.size()) {
- TaskRecord t = mRecentTasks.get(i);
- if (t.taskId == taskId) {
- tr = t;
- break;
- }
- i++;
+ TaskRecord tr = recentTaskForIdLocked(taskId);
+ if (tr != null) {
+ ActivityRecord r = tr.removeTaskActivitiesLocked(-1, false);
+ if (r != null) {
+ cleanUpRemovedTaskLocked(tr, flags);
+ return true;
}
- if (tr != null) {
- if (tr.numActivities <= 0) {
- // Caller is just removing a recent task that is
- // not actively running. That is easy!
- mRecentTasks.remove(i);
- cleanUpRemovedTaskLocked(tr, flags);
- return true;
- } else {
- Slog.w(TAG, "removeTask: task " + taskId
- + " does not have activities to remove, "
- + " but numActivities=" + tr.numActivities
- + ": " + tr);
- }
+ if (tr.mActivities.size() == 0) {
+ // Caller is just removing a recent task that is
+ // not actively running. That is easy!
+ cleanUpRemovedTaskLocked(tr, flags);
+ return true;
}
+ Slog.w(TAG, "removeTask: task " + taskId
+ + " does not have activities to remove, "
+ + " but numActivities=" + tr.numActivities
+ + ": " + tr);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -6058,50 +6423,15 @@
return false;
}
- private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
- int j;
- TaskRecord startTask = ((ActivityRecord)mMainStack.mHistory.get(startIndex)).task;
- TaskRecord jt = startTask;
-
- // First look backwards
- for (j=startIndex-1; j>=0; j--) {
- ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
- if (r.task != jt) {
- jt = r.task;
- if (affinity.equals(jt.affinity)) {
- return j;
- }
- }
- }
-
- // Now look forwards
- final int N = mMainStack.mHistory.size();
- jt = startTask;
- for (j=startIndex+1; j<N; j++) {
- ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
- if (r.task != jt) {
- if (affinity.equals(jt.affinity)) {
- return j;
- }
- jt = r.task;
- }
- }
-
- // Might it be at the top?
- if (affinity.equals(((ActivityRecord)mMainStack.mHistory.get(N-1)).task.affinity)) {
- return N-1;
- }
-
- return -1;
- }
-
/**
* TODO: Add mController hook
*/
+ @Override
public void moveTaskToFront(int task, int flags, Bundle options) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskToFront()");
+ if (DEBUG_STACK) Slog.d(TAG, "moveTaskToFront: moving task=" + task);
synchronized(this) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), "Task to front")) {
@@ -6110,34 +6440,7 @@
}
final long origId = Binder.clearCallingIdentity();
try {
- TaskRecord tr = taskForIdLocked(task);
- if (tr != null) {
- if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
- mMainStack.mUserLeaving = true;
- }
- if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
- // Caller wants the home activity moved with it. To accomplish this,
- // we'll just move the home task to the top first.
- mMainStack.moveHomeToFrontLocked();
- }
- mMainStack.moveTaskToFrontLocked(tr, null, options);
- return;
- }
- for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
- ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
- if (hr.task.taskId == task) {
- if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
- mMainStack.mUserLeaving = true;
- }
- if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
- // Caller wants the home activity moved with it. To accomplish this,
- // we'll just move the home task to the top first.
- mMainStack.moveHomeToFrontLocked();
- }
- mMainStack.moveTaskToFrontLocked(hr.task, null, options);
- return;
- }
- }
+ mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -6145,21 +6448,26 @@
}
}
- public void moveTaskToBack(int task) {
+ @Override
+ public void moveTaskToBack(int taskId) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskToBack()");
synchronized(this) {
- if (mMainStack.mResumedActivity != null
- && mMainStack.mResumedActivity.task.taskId == task) {
- if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
- Binder.getCallingUid(), "Task to back")) {
- return;
+ TaskRecord tr = recentTaskForIdLocked(taskId);
+ if (tr != null) {
+ if (DEBUG_STACK) Slog.d(TAG, "moveTaskToBack: moving task=" + tr);
+ ActivityStack stack = tr.stack;
+ if (stack.mResumedActivity != null && stack.mResumedActivity.task == tr) {
+ if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+ Binder.getCallingUid(), "Task to back")) {
+ return;
+ }
}
+ final long origId = Binder.clearCallingIdentity();
+ stack.moveTaskToBackLocked(taskId, null);
+ Binder.restoreCallingIdentity(origId);
}
- final long origId = Binder.clearCallingIdentity();
- mMainStack.moveTaskToBackLocked(task, null);
- Binder.restoreCallingIdentity(origId);
}
}
@@ -6172,19 +6480,21 @@
* of a task; if true it will work for any activity in a task.
* @return Returns true if the move completed, false if not.
*/
+ @Override
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
enforceNotIsolatedCaller("moveActivityTaskToBack");
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
- int taskId = getTaskForActivityLocked(token, !nonRoot);
+ int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
if (taskId >= 0) {
- return mMainStack.moveTaskToBackLocked(taskId, null);
+ return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId, null);
}
Binder.restoreCallingIdentity(origId);
}
return false;
}
+ @Override
public void moveTaskBackwards(int task) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskBackwards()");
@@ -6204,27 +6514,151 @@
Slog.e(TAG, "moveTaskBackwards not yet implemented!");
}
- public int getTaskForActivity(IBinder token, boolean onlyRoot) {
- synchronized(this) {
- return getTaskForActivityLocked(token, onlyRoot);
+ @Override
+ public int createStack(int taskId, int relativeStackBoxId, int position, float weight) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "createStack()");
+ if (DEBUG_STACK) Slog.d(TAG, "createStack: taskId=" + taskId + " relStackBoxId=" +
+ relativeStackBoxId + " position=" + position + " weight=" + weight);
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ int stackId = mStackSupervisor.createStack();
+ mWindowManager.createStack(stackId, relativeStackBoxId, position, weight);
+ if (taskId > 0) {
+ moveTaskToStack(taskId, stackId, true);
+ }
+ return stackId;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
- int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
- final int N = mMainStack.mHistory.size();
- TaskRecord lastTask = null;
- for (int i=0; i<N; i++) {
- ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
- if (r.appToken == token) {
- if (!onlyRoot || lastTask != r.task) {
- return r.task.taskId;
- }
- return -1;
- }
- lastTask = r.task;
+ @Override
+ public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "moveTaskToStack()");
+ if (stackId == HOME_STACK_ID) {
+ Slog.e(TAG, "moveTaskToStack: Attempt to move task " + taskId + " to home stack",
+ new RuntimeException("here").fillInStackTrace());
}
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (DEBUG_STACK) Slog.d(TAG, "moveTaskToStack: moving task=" + taskId + " to stackId="
+ + stackId + " toTop=" + toTop);
+ mStackSupervisor.moveTaskToStack(taskId, stackId, toTop);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
- return -1;
+ @Override
+ public void resizeStackBox(int stackBoxId, float weight) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "resizeStackBox()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mWindowManager.resizeStackBox(stackBoxId, weight);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private ArrayList<StackInfo> getStacks() {
+ synchronized (this) {
+ ArrayList<ActivityManager.StackInfo> list = new ArrayList<ActivityManager.StackInfo>();
+ ArrayList<ActivityStack> stacks = mStackSupervisor.getStacks();
+ for (ActivityStack stack : stacks) {
+ ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
+ int stackId = stack.mStackId;
+ stackInfo.stackId = stackId;
+ stackInfo.bounds = mWindowManager.getStackBounds(stackId);
+ ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ final int numTasks = tasks.size();
+ int[] taskIds = new int[numTasks];
+ String[] taskNames = new String[numTasks];
+ for (int i = 0; i < numTasks; ++i) {
+ final TaskRecord task = tasks.get(i);
+ taskIds[i] = task.taskId;
+ taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
+ : task.realActivity != null ? task.realActivity.flattenToString()
+ : task.getTopActivity() != null ? task.getTopActivity().packageName
+ : "unknown";
+ }
+ stackInfo.taskIds = taskIds;
+ stackInfo.taskNames = taskNames;
+ list.add(stackInfo);
+ }
+ return list;
+ }
+ }
+
+ private void addStackInfoToStackBoxInfo(StackBoxInfo stackBoxInfo, List<StackInfo> stackInfos) {
+ final int stackId = stackBoxInfo.stackId;
+ if (stackId >= 0) {
+ for (StackInfo stackInfo : stackInfos) {
+ if (stackId == stackInfo.stackId) {
+ stackBoxInfo.stack = stackInfo;
+ stackInfos.remove(stackInfo);
+ return;
+ }
+ }
+ } else {
+ addStackInfoToStackBoxInfo(stackBoxInfo.children[0], stackInfos);
+ addStackInfoToStackBoxInfo(stackBoxInfo.children[1], stackInfos);
+ }
+ }
+
+ @Override
+ public List<StackBoxInfo> getStackBoxes() {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "getStackBoxes()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ List<StackBoxInfo> stackBoxInfos = mWindowManager.getStackBoxInfos();
+ synchronized (this) {
+ List<StackInfo> stackInfos = getStacks();
+ for (StackBoxInfo stackBoxInfo : stackBoxInfos) {
+ addStackInfoToStackBoxInfo(stackBoxInfo, stackInfos);
+ }
+ }
+ return stackBoxInfos;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public StackBoxInfo getStackBoxInfo(int stackBoxId) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "getStackBoxInfo()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ List<StackBoxInfo> stackBoxInfos = mWindowManager.getStackBoxInfos();
+ StackBoxInfo info = null;
+ synchronized (this) {
+ List<StackInfo> stackInfos = getStacks();
+ for (StackBoxInfo stackBoxInfo : stackBoxInfos) {
+ addStackInfoToStackBoxInfo(stackBoxInfo, stackInfos);
+ if (stackBoxInfo.stackBoxId == stackBoxId) {
+ info = stackBoxInfo;
+ }
+ }
+ }
+ return info;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public int getTaskForActivity(IBinder token, boolean onlyRoot) {
+ synchronized(this) {
+ return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
+ }
}
// =========================================================
@@ -6241,14 +6675,14 @@
final void sendPendingThumbnail(ActivityRecord r, IBinder token,
Bitmap thumbnail, CharSequence description, boolean always) {
- TaskRecord task = null;
- ArrayList receivers = null;
+ TaskRecord task;
+ ArrayList<PendingThumbnailsRecord> receivers = null;
//System.out.println("Send pending thumbnail: " + r);
synchronized(this) {
if (r == null) {
- r = mMainStack.isInStackLocked(token);
+ r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return;
}
@@ -6268,12 +6702,11 @@
int N = mPendingThumbnails.size();
int i=0;
while (i<N) {
- PendingThumbnailsRecord pr =
- (PendingThumbnailsRecord)mPendingThumbnails.get(i);
+ PendingThumbnailsRecord pr = mPendingThumbnails.get(i);
//System.out.println("Looking in " + pr.pendingRecords);
if (pr.pendingRecords.remove(r)) {
if (receivers == null) {
- receivers = new ArrayList();
+ receivers = new ArrayList<PendingThumbnailsRecord>();
}
receivers.add(pr);
if (pr.pendingRecords.size() == 0) {
@@ -6291,8 +6724,7 @@
final int N = receivers.size();
for (int i=0; i<N; i++) {
try {
- PendingThumbnailsRecord pr =
- (PendingThumbnailsRecord)receivers.get(i);
+ PendingThumbnailsRecord pr = receivers.get(i);
pr.receiver.newThumbnail(
task != null ? task.taskId : -1, thumbnail, description);
if (pr.finished) {
@@ -6322,6 +6754,7 @@
int userId = app.userId;
if (providers != null) {
int N = providers.size();
+ app.pubProviders.ensureCapacity(N + app.pubProviders.size());
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
@@ -6347,7 +6780,7 @@
if (DEBUG_MU)
Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
app.pubProviders.put(cpi.name, cpr);
- app.addPackage(cpi.applicationInfo.packageName);
+ app.addPackage(cpi.applicationInfo.packageName, mProcessTracker);
ensurePackageDexOpt(cpi.applicationInfo.packageName);
}
}
@@ -6647,7 +7080,7 @@
if (DEBUG_PROVIDER) {
RuntimeException e = new RuntimeException("here");
- Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.uid
+ Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + (r != null ? r.uid : null)
+ " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e);
}
@@ -7064,7 +7497,6 @@
if (isolated) {
int userId = UserHandle.getUserId(uid);
int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
- uid = 0;
while (true) {
if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
|| mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
@@ -7085,7 +7517,8 @@
synchronized (stats) {
ps = stats.getProcessStatsLocked(info.uid, proc);
}
- return new ProcessRecord(ps, thread, info, proc, uid);
+ return new ProcessRecord(ps, thread, info, proc, uid,
+ mProcessTracker.getProcessStateLocked(info.packageName, info.uid, proc));
}
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
@@ -7133,13 +7566,10 @@
"unhandledBack()");
synchronized(this) {
- int count = mMainStack.mHistory.size();
- if (DEBUG_SWITCH) Slog.d(
- TAG, "Performing unhandledBack(): stack size = " + count);
- if (count > 1) {
- final long origId = Binder.clearCallingIdentity();
- mMainStack.finishActivityLocked((ActivityRecord)mMainStack.mHistory.get(count-1),
- count-1, Activity.RESULT_CANCELED, null, "unhandled-back", true);
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ getFocusedStack().unhandledBackLocked();
+ } finally {
Binder.restoreCallingIdentity(origId);
}
}
@@ -7180,7 +7610,7 @@
// Actually is sleeping or shutting down or whatever else in the future
// is an inactive state.
- public boolean isSleeping() {
+ public boolean isSleepingOrShuttingDown() {
return mSleeping || mShuttingDown;
}
@@ -7197,7 +7627,7 @@
if (!mSleeping) {
mSleeping = true;
- mMainStack.stopIfSleepingLocked();
+ mStackSupervisor.goingToSleepLocked();
// Initialize the wake times of all processes.
checkExcessivePowerUsageLocked(false);
@@ -7208,57 +7638,41 @@
}
}
+ @Override
public boolean shutdown(int timeout) {
if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SHUTDOWN);
}
-
+
boolean timedout = false;
-
+
synchronized(this) {
mShuttingDown = true;
updateEventDispatchingLocked();
-
- if (mMainStack.mResumedActivity != null) {
- mMainStack.stopIfSleepingLocked();
- final long endTime = System.currentTimeMillis() + timeout;
- while (mMainStack.mResumedActivity != null
- || mMainStack.mPausingActivity != null) {
- long delay = endTime - System.currentTimeMillis();
- if (delay <= 0) {
- Slog.w(TAG, "Activity manager shutdown timed out");
- timedout = true;
- break;
- }
- try {
- this.wait();
- } catch (InterruptedException e) {
- }
- }
- }
+ timedout = mStackSupervisor.shutdownLocked(timeout);
}
mAppOpsService.shutdown();
mUsageStatsService.shutdown();
mBatteryStatsService.shutdown();
-
+ synchronized (this) {
+ mProcessTracker.shutdownLocked();
+ }
+
return timedout;
}
public final void activitySlept(IBinder token) {
- if (localLOGV) Slog.v(
- TAG, "Activity slept: token=" + token);
-
- ActivityRecord r = null;
+ if (localLOGV) Slog.v(TAG, "Activity slept: token=" + token);
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
- r = mMainStack.isInStackLocked(token);
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- mMainStack.activitySleptLocked(r);
+ mStackSupervisor.activitySleptLocked(r);
}
}
@@ -7269,8 +7683,7 @@
if (!mWentToSleep && !mLockScreenShown) {
if (mSleeping) {
mSleeping = false;
- mMainStack.awakeFromSleepingLocked();
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.comeOutOfSleepIfNeededLocked();
}
}
}
@@ -7301,8 +7714,13 @@
}
synchronized(this) {
- mLockScreenShown = shown;
- comeOutOfSleepIfNeededLocked();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mLockScreenShown = shown;
+ comeOutOfSleepIfNeededLocked();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -7360,33 +7778,36 @@
enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
"setDebugApp()");
- // Note that this is not really thread safe if there are multiple
- // callers into it at the same time, but that's not a situation we
- // care about.
- if (persistent) {
- final ContentResolver resolver = mContext.getContentResolver();
- Settings.Global.putString(
- resolver, Settings.Global.DEBUG_APP,
- packageName);
- Settings.Global.putInt(
- resolver, Settings.Global.WAIT_FOR_DEBUGGER,
- waitForDebugger ? 1 : 0);
- }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // Note that this is not really thread safe if there are multiple
+ // callers into it at the same time, but that's not a situation we
+ // care about.
+ if (persistent) {
+ final ContentResolver resolver = mContext.getContentResolver();
+ Settings.Global.putString(
+ resolver, Settings.Global.DEBUG_APP,
+ packageName);
+ Settings.Global.putInt(
+ resolver, Settings.Global.WAIT_FOR_DEBUGGER,
+ waitForDebugger ? 1 : 0);
+ }
- synchronized (this) {
- if (!persistent) {
- mOrigDebugApp = mDebugApp;
- mOrigWaitForDebugger = mWaitForDebugger;
+ synchronized (this) {
+ if (!persistent) {
+ mOrigDebugApp = mDebugApp;
+ mOrigWaitForDebugger = mWaitForDebugger;
+ }
+ mDebugApp = packageName;
+ mWaitForDebugger = waitForDebugger;
+ mDebugTransient = !persistent;
+ if (packageName != null) {
+ forceStopPackageLocked(packageName, -1, false, false, true, true,
+ UserHandle.USER_ALL, "set debug app");
+ }
}
- mDebugApp = packageName;
- mWaitForDebugger = waitForDebugger;
- mDebugTransient = !persistent;
- if (packageName != null) {
- final long origId = Binder.clearCallingIdentity();
- forceStopPackageLocked(packageName, -1, false, false, true, true,
- UserHandle.USER_ALL);
- Binder.restoreCallingIdentity(origId);
- }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -7427,6 +7848,7 @@
}
}
+ @Override
public void setAlwaysFinish(boolean enabled) {
enforceCallingPermission(android.Manifest.permission.SET_ALWAYS_FINISH,
"setAlwaysFinish()");
@@ -7440,6 +7862,7 @@
}
}
+ @Override
public void setActivityController(IActivityController controller) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"setActivityController()");
@@ -7449,6 +7872,7 @@
}
}
+ @Override
public void setUserIsMonkey(boolean userIsMonkey) {
synchronized (this) {
synchronized (mPidsSelfLocked) {
@@ -7466,6 +7890,7 @@
}
}
+ @Override
public boolean isUserAMonkey() {
synchronized (this) {
// If there is a controller also implies the user is a monkey.
@@ -7562,7 +7987,7 @@
PendingActivityExtras pae;
Bundle extras = new Bundle();
synchronized (this) {
- ActivityRecord activity = mMainStack.mResumedActivity;
+ ActivityRecord activity = getFocusedStack().mResumedActivity;
if (activity == null) {
Slog.w(TAG, "getTopActivityExtras failed: no resumed activity");
return null;
@@ -7621,15 +8046,56 @@
}
}
+ @Override
public void unregisterProcessObserver(IProcessObserver observer) {
synchronized (this) {
mProcessObservers.unregister(observer);
}
}
+ @Override
+ public void convertFromTranslucent(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return;
+ }
+ if (r.changeWindowTranslucency(true)) {
+ mWindowManager.setAppFullscreen(token, true);
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void convertToTranslucent(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return;
+ }
+ if (r.changeWindowTranslucency(false)) {
+ r.task.stack.convertToTranslucent(r);
+ mWindowManager.setAppFullscreen(token, false);
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
public void setImmersive(IBinder token, boolean immersive) {
synchronized(this) {
- final ActivityRecord r = mMainStack.isInStackLocked(token);
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
throw new IllegalArgumentException();
}
@@ -7645,9 +8111,10 @@
}
}
+ @Override
public boolean isImmersive(IBinder token) {
synchronized (this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
throw new IllegalArgumentException();
}
@@ -7658,7 +8125,7 @@
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("startActivity");
synchronized (this) {
- ActivityRecord r = mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = getFocusedStack().topRunningActivityLocked(null);
return (r != null) ? r.immersive : false;
}
}
@@ -7717,7 +8184,7 @@
String reason = (pReason == null) ? "Unknown" : pReason;
// XXX Note: don't acquire main activity lock here, because the window
// manager calls in with its locks held.
-
+
boolean killed = false;
synchronized (mPidsSelfLocked) {
int[] types = new int[pids.length];
@@ -7732,12 +8199,12 @@
}
}
}
-
- // If the worst oom_adj is somewhere in the hidden proc LRU range,
- // then constrain it so we will kill all hidden procs.
- if (worstType < ProcessList.HIDDEN_APP_MAX_ADJ
- && worstType > ProcessList.HIDDEN_APP_MIN_ADJ) {
- worstType = ProcessList.HIDDEN_APP_MIN_ADJ;
+
+ // If the worst oom_adj is somewhere in the cached proc LRU range,
+ // then constrain it so we will kill all cached procs.
+ if (worstType < ProcessList.CACHED_APP_MAX_ADJ
+ && worstType > ProcessList.CACHED_APP_MIN_ADJ) {
+ worstType = ProcessList.CACHED_APP_MIN_ADJ;
}
// If this is not a secure call, don't let it kill processes that
@@ -7899,7 +8366,7 @@
// no need to synchronize(this) just to read & return the value
return mSystemReady;
}
-
+
private static File getCalledPreBootReceiversFile() {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
@@ -8146,6 +8613,10 @@
retrieveSettings();
+ synchronized (this) {
+ readGrantedUriPermissionsLocked();
+ }
+
if (goingCallback != null) goingCallback.run();
synchronized (this) {
@@ -8207,7 +8678,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
sendUserSwitchBroadcastsLocked(-1, mCurrentUserId);
}
}
@@ -8302,15 +8773,7 @@
+ " has crashed too many times: killing!");
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.userId, app.info.processName, app.uid);
- for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
- if (r.app == app) {
- Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "crashed", false);
- }
- }
+ mStackSupervisor.handleAppCrashLocked(app);
if (!app.persistent) {
// We don't want to start this process again until the user
// explicitly does so... but for persistent process, we really
@@ -8330,38 +8793,12 @@
// annoy the user repeatedly. Unless it is persistent, since those
// processes run critical code.
removeProcessLocked(app, false, false, "crash");
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
return false;
}
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
} else {
- ActivityRecord r = mMainStack.topRunningActivityLocked(null);
- if (r != null && r.app == app) {
- // If the top running activity is from this crashing
- // process, then terminate it to avoid getting in a loop.
- Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- int index = mMainStack.indexOfActivityLocked(r);
- r.stack.finishActivityLocked(r, index,
- Activity.RESULT_CANCELED, null, "crashed", false);
- // Also terminate any activities below it that aren't yet
- // stopped, to avoid a situation where one will get
- // re-start our crashing activity once it gets resumed again.
- index--;
- if (index >= 0) {
- r = (ActivityRecord)mMainStack.mHistory.get(index);
- if (r.state == ActivityState.RESUMED
- || r.state == ActivityState.PAUSING
- || r.state == ActivityState.PAUSED) {
- if (!r.isHomeActivity || mHomeProcess != r.app) {
- Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- r.stack.finishActivityLocked(r, index,
- Activity.RESULT_CANCELED, null, "crashed", false);
- }
- }
- }
- }
+ mStackSupervisor.finishTopRunningActivityLocked(app);
}
// Bump up the crash count of any services currently running in the proc.
@@ -8381,10 +8818,10 @@
// from blocking the user to manually clear the list.
if (app == mHomeProcess && mHomeProcess.activities.size() > 0
&& (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Iterator it = mHomeProcess.activities.iterator();
+ Iterator<ActivityRecord> it = mHomeProcess.activities.iterator();
while (it.hasNext()) {
- ActivityRecord r = (ActivityRecord)it.next();
- if (r.isHomeActivity) {
+ ActivityRecord r = it.next();
+ if (r.isHomeActivity()) {
Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
try {
ActivityThread.getPackageManager()
@@ -8670,7 +9107,9 @@
}
synchronized (this) {
- for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+ final int NP = mProcessNames.getMap().size();
+ for (int ip=0; ip<NP; ip++) {
+ SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord p = apps.valueAt(ia);
@@ -8711,7 +9150,8 @@
int flags = process.info.flags;
IPackageManager pm = AppGlobals.getPackageManager();
sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n");
- for (String pkg : process.pkgList) {
+ for (int ip=0; ip<process.pkgList.size(); ip++) {
+ String pkg = process.pkgList.keyAt(ip);
sb.append("Package: ").append(pkg);
try {
PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId());
@@ -9029,9 +9469,9 @@
}
static int oomAdjToImportance(int adj, ActivityManager.RunningAppProcessInfo currApp) {
- if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+ if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
if (currApp != null) {
- currApp.lru = adj - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
+ currApp.lru = adj - ProcessList.CACHED_APP_MIN_ADJ + 1;
}
return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
} else if (adj >= ProcessList.SERVICE_B_ADJ) {
@@ -9344,39 +9784,28 @@
// No piece of data specified, dump everything.
synchronized (this) {
- boolean needSep;
- needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
- if (needSep) {
- pw.println(" ");
- }
+ dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+ pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- needSep = dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
- if (needSep) {
- pw.println(" ");
- }
+ dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+ pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
- if (needSep) {
- pw.println(" ");
- }
+ dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+ pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- needSep = mServices.dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
- if (needSep) {
- pw.println(" ");
- }
+ mServices.dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+ pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
- if (needSep) {
- pw.println(" ");
- }
+ dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+ pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
@@ -9385,57 +9814,32 @@
Binder.restoreCallingIdentity(origId);
}
- boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
- pw.println(" Main stack:");
- dumpHistoryList(fd, pw, mMainStack.mHistory, " ", "Hist", true, !dumpAll, dumpClient,
+
+ boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
dumpPackage);
- pw.println(" ");
- pw.println(" Running activities (most recent first):");
- dumpHistoryList(fd, pw, mMainStack.mLRUActivities, " ", "Run", false, !dumpAll, false,
- dumpPackage);
- if (mMainStack.mWaitingVisibleActivities.size() > 0) {
- pw.println(" ");
- pw.println(" Activities waiting for another to become visible:");
- dumpHistoryList(fd, pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false,
- !dumpAll, false, dumpPackage);
- }
- if (mMainStack.mStoppingActivities.size() > 0) {
- pw.println(" ");
- pw.println(" Activities waiting to stop:");
- dumpHistoryList(fd, pw, mMainStack.mStoppingActivities, " ", "Stop", false,
- !dumpAll, false, dumpPackage);
- }
- if (mMainStack.mGoingToSleepActivities.size() > 0) {
- pw.println(" ");
- pw.println(" Activities waiting to sleep:");
- dumpHistoryList(fd, pw, mMainStack.mGoingToSleepActivities, " ", "Sleep", false,
- !dumpAll, false, dumpPackage);
- }
- if (mMainStack.mFinishingActivities.size() > 0) {
- pw.println(" ");
- pw.println(" Activities waiting to finish:");
- dumpHistoryList(fd, pw, mMainStack.mFinishingActivities, " ", "Fin", false,
- !dumpAll, false, dumpPackage);
+ boolean needSep = printedAnything;
+
+ boolean printed = ActivityStackSupervisor.printThisActivity(pw, mFocusedActivity,
+ dumpPackage, needSep, " mFocusedActivity: ");
+ if (printed) {
+ printedAnything = true;
+ needSep = false;
}
- pw.println(" ");
- if (mMainStack.mPausingActivity != null) {
- pw.println(" mPausingActivity: " + mMainStack.mPausingActivity);
- }
- pw.println(" mResumedActivity: " + mMainStack.mResumedActivity);
- pw.println(" mFocusedActivity: " + mFocusedActivity);
- if (dumpAll) {
- pw.println(" mLastPausedActivity: " + mMainStack.mLastPausedActivity);
- pw.println(" mSleepTimeout: " + mMainStack.mSleepTimeout);
- pw.println(" mDismissKeyguardOnNextActivity: "
- + mMainStack.mDismissKeyguardOnNextActivity);
+ if (dumpPackage == null) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ printedAnything = true;
+ mStackSupervisor.dump(pw, " ");
}
if (mRecentTasks.size() > 0) {
- pw.println();
- pw.println(" Recent tasks:");
+ boolean printedHeader = false;
final int N = mRecentTasks.size();
for (int i=0; i<N; i++) {
@@ -9446,6 +9850,14 @@
continue;
}
}
+ if (!printedHeader) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" Recent tasks:");
+ printedHeader = true;
+ printedAnything = true;
+ }
pw.print(" * Recent #"); pw.print(i); pw.print(": ");
pw.println(tr);
if (dumpAll) {
@@ -9453,33 +9865,34 @@
}
}
}
-
- if (dumpAll) {
- pw.println(" ");
- pw.println(" mCurTask: " + mCurTask);
+
+ if (!printedAnything) {
+ pw.println(" (nothing)");
}
-
- return true;
}
- boolean dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
+ boolean printedAnything = false;
int numPers = 0;
pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
if (dumpAll) {
- for (SparseArray<ProcessRecord> procs : mProcessNames.getMap().values()) {
+ final int NP = mProcessNames.getMap().size();
+ for (int ip=0; ip<NP; ip++) {
+ SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
final int NA = procs.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord r = procs.valueAt(ia);
- if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
if (!needSep) {
pw.println(" All known processes:");
needSep = true;
+ printedAnything = true;
}
pw.print(r.persistent ? " *PERS*" : " *APP*");
pw.print(" UID "); pw.print(procs.keyAt(ia));
@@ -9493,41 +9906,50 @@
}
if (mIsolatedProcesses.size() > 0) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Isolated process list (sorted by uid):");
+ boolean printed = false;
for (int i=0; i<mIsolatedProcesses.size(); i++) {
ProcessRecord r = mIsolatedProcesses.valueAt(i);
- if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" Isolated process list (sorted by uid):");
+ printedAnything = true;
+ printed = true;
+ needSep = true;
+ }
pw.println(String.format("%sIsolated #%2d: %s",
" ", i, r.toString()));
}
}
if (mLruProcesses.size() > 0) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Process LRU list (sorted by oom_adj):");
- dumpProcessOomList(pw, this, mLruProcesses, " ",
- "Proc", "PERS", false, dumpPackage);
- needSep = true;
+ boolean printed = dumpProcessOomList(pw, this, mLruProcesses, " ",
+ "Proc", "PERS", false, dumpPackage, needSep,
+ " Process LRU list (sorted by oom_adj):");
+ if (printed) {
+ needSep = true;
+ printedAnything = true;
+ }
}
- if (dumpAll) {
+ if (dumpAll || dumpPackage != null) {
synchronized (mPidsSelfLocked) {
boolean printed = false;
for (int i=0; i<mPidsSelfLocked.size(); i++) {
ProcessRecord r = mPidsSelfLocked.valueAt(i);
- if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" PID mappings:");
printed = true;
+ printedAnything = true;
}
pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i));
pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
@@ -9542,14 +9964,15 @@
ProcessRecord r = mPidsSelfLocked.get(
mForegroundProcesses.valueAt(i).pid);
if (dumpPackage != null && (r == null
- || !dumpPackage.equals(r.info.packageName))) {
+ || !r.pkgList.containsKey(dumpPackage))) {
continue;
}
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Foreground Processes:");
printed = true;
+ printedAnything = true;
}
pw.print(" PID #"); pw.print(mForegroundProcesses.keyAt(i));
pw.print(": "); pw.println(mForegroundProcesses.valueAt(i));
@@ -9558,24 +9981,27 @@
}
if (mPersistentStartingProcesses.size() > 0) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
+ printedAnything = true;
pw.println(" Persisent processes that are starting:");
dumpProcessList(pw, this, mPersistentStartingProcesses, " ",
"Starting Norm", "Restarting PERS", dumpPackage);
}
if (mRemovedProcesses.size() > 0) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
+ printedAnything = true;
pw.println(" Processes that are being removed:");
dumpProcessList(pw, this, mRemovedProcesses, " ",
"Removed Norm", "Removed PERS", dumpPackage);
}
if (mProcessesOnHold.size() > 0) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
+ printedAnything = true;
pw.println(" Processes that are on old until the system is ready:");
dumpProcessList(pw, this, mProcessesOnHold, " ",
"OnHold Norm", "OnHold PERS", dumpPackage);
@@ -9586,23 +10012,25 @@
if (mProcessCrashTimes.getMap().size() > 0) {
boolean printed = false;
long now = SystemClock.uptimeMillis();
- for (Map.Entry<String, SparseArray<Long>> procs
- : mProcessCrashTimes.getMap().entrySet()) {
- String pname = procs.getKey();
- SparseArray<Long> uids = procs.getValue();
+ final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+ final int NP = pmap.size();
+ for (int ip=0; ip<NP; ip++) {
+ String pname = pmap.keyAt(ip);
+ SparseArray<Long> uids = pmap.valueAt(ip);
final int N = uids.size();
for (int i=0; i<N; i++) {
int puid = uids.keyAt(i);
ProcessRecord r = mProcessNames.get(pname, puid);
if (dumpPackage != null && (r == null
- || !dumpPackage.equals(r.info.packageName))) {
+ || !r.pkgList.containsKey(dumpPackage))) {
continue;
}
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Time since processes crashed:");
printed = true;
+ printedAnything = true;
}
pw.print(" Process "); pw.print(pname);
pw.print(" uid "); pw.print(puid);
@@ -9615,22 +10043,24 @@
if (mBadProcesses.getMap().size() > 0) {
boolean printed = false;
- for (Map.Entry<String, SparseArray<Long>> procs
- : mBadProcesses.getMap().entrySet()) {
- String pname = procs.getKey();
- SparseArray<Long> uids = procs.getValue();
+ final ArrayMap<String, SparseArray<Long>> pmap = mBadProcesses.getMap();
+ final int NP = pmap.size();
+ for (int ip=0; ip<NP; ip++) {
+ String pname = pmap.keyAt(ip);
+ SparseArray<Long> uids = pmap.valueAt(ip);
final int N = uids.size();
for (int i=0; i<N; i++) {
int puid = uids.keyAt(i);
ProcessRecord r = mProcessNames.get(pname, puid);
if (dumpPackage != null && (r == null
- || !dumpPackage.equals(r.info.packageName))) {
+ || !r.pkgList.containsKey(dumpPackage))) {
continue;
}
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Bad processes:");
+ printedAnything = true;
}
pw.print(" Bad process "); pw.print(pname);
pw.print(" uid "); pw.print(puid);
@@ -9640,42 +10070,66 @@
}
}
- pw.println();
- pw.println(" mStartedUsers:");
- for (int i=0; i<mStartedUsers.size(); i++) {
- UserStartedState uss = mStartedUsers.valueAt(i);
- pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
- pw.print(": "); uss.dump("", pw);
+ if (dumpPackage == null) {
+ pw.println();
+ needSep = false;
+ pw.println(" mStartedUsers:");
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ UserStartedState uss = mStartedUsers.valueAt(i);
+ pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
+ pw.print(": "); uss.dump("", pw);
+ }
+ pw.print(" mStartedUserArray: [");
+ for (int i=0; i<mStartedUserArray.length; i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(mStartedUserArray[i]);
+ }
+ pw.println("]");
+ pw.print(" mUserLru: [");
+ for (int i=0; i<mUserLru.size(); i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(mUserLru.get(i));
+ }
+ pw.println("]");
+ if (dumpAll) {
+ pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray));
+ }
}
- pw.print(" mStartedUserArray: [");
- for (int i=0; i<mStartedUserArray.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(mStartedUserArray[i]);
+ if (mHomeProcess != null && (dumpPackage == null
+ || mHomeProcess.pkgList.containsKey(dumpPackage))) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ pw.println(" mHomeProcess: " + mHomeProcess);
}
- pw.println("]");
- pw.print(" mUserLru: [");
- for (int i=0; i<mUserLru.size(); i++) {
- if (i > 0) pw.print(", ");
- pw.print(mUserLru.get(i));
+ if (mPreviousProcess != null && (dumpPackage == null
+ || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ pw.println(" mPreviousProcess: " + mPreviousProcess);
}
- pw.println("]");
- if (dumpAll) {
- pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray));
- }
- pw.println(" mHomeProcess: " + mHomeProcess);
- pw.println(" mPreviousProcess: " + mPreviousProcess);
if (dumpAll) {
StringBuilder sb = new StringBuilder(128);
sb.append(" mPreviousProcessVisibleTime: ");
TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
pw.println(sb);
}
- if (mHeavyWeightProcess != null) {
+ if (mHeavyWeightProcess != null && (dumpPackage == null
+ || mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
}
- pw.println(" mConfiguration: " + mConfiguration);
+ if (dumpPackage == null) {
+ pw.println(" mConfiguration: " + mConfiguration);
+ }
if (dumpAll) {
- pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange);
+ pw.println(" mConfigWillChange: " + getFocusedStack().mConfigWillChange);
if (mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
for (Map.Entry<String, Integer> entry
@@ -9694,57 +10148,83 @@
}
}
}
- if (mSleeping || mWentToSleep || mLockScreenShown) {
- pw.println(" mSleeping=" + mSleeping + " mWentToSleep=" + mWentToSleep
- + " mLockScreenShown " + mLockScreenShown);
- }
- if (mShuttingDown) {
- pw.println(" mShuttingDown=" + mShuttingDown);
+ if (dumpPackage == null) {
+ if (mSleeping || mWentToSleep || mLockScreenShown) {
+ pw.println(" mSleeping=" + mSleeping + " mWentToSleep=" + mWentToSleep
+ + " mLockScreenShown " + mLockScreenShown);
+ }
+ if (mShuttingDown) {
+ pw.println(" mShuttingDown=" + mShuttingDown);
+ }
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
- pw.println(" mDebugApp=" + mDebugApp + "/orig=" + mOrigDebugApp
- + " mDebugTransient=" + mDebugTransient
- + " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
+ if (dumpPackage == null || dumpPackage.equals(mDebugApp)
+ || dumpPackage.equals(mOrigDebugApp)) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ pw.println(" mDebugApp=" + mDebugApp + "/orig=" + mOrigDebugApp
+ + " mDebugTransient=" + mDebugTransient
+ + " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
+ }
}
if (mOpenGlTraceApp != null) {
- pw.println(" mOpenGlTraceApp=" + mOpenGlTraceApp);
+ if (dumpPackage == null || dumpPackage.equals(mOpenGlTraceApp)) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ pw.println(" mOpenGlTraceApp=" + mOpenGlTraceApp);
+ }
}
if (mProfileApp != null || mProfileProc != null || mProfileFile != null
|| mProfileFd != null) {
- pw.println(" mProfileApp=" + mProfileApp + " mProfileProc=" + mProfileProc);
- pw.println(" mProfileFile=" + mProfileFile + " mProfileFd=" + mProfileFd);
- pw.println(" mProfileType=" + mProfileType + " mAutoStopProfiler="
- + mAutoStopProfiler);
+ if (dumpPackage == null || dumpPackage.equals(mProfileApp)) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ pw.println(" mProfileApp=" + mProfileApp + " mProfileProc=" + mProfileProc);
+ pw.println(" mProfileFile=" + mProfileFile + " mProfileFd=" + mProfileFd);
+ pw.println(" mProfileType=" + mProfileType + " mAutoStopProfiler="
+ + mAutoStopProfiler);
+ }
}
- if (mAlwaysFinishActivities || mController != null) {
- pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities
- + " mController=" + mController);
+ if (dumpPackage == null) {
+ if (mAlwaysFinishActivities || mController != null) {
+ pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities
+ + " mController=" + mController);
+ }
+ if (dumpAll) {
+ pw.println(" Total persistent processes: " + numPers);
+ pw.println(" mStartRunning=" + mStartRunning
+ + " mProcessesReady=" + mProcessesReady
+ + " mSystemReady=" + mSystemReady);
+ pw.println(" mBooting=" + mBooting
+ + " mBooted=" + mBooted
+ + " mFactoryTest=" + mFactoryTest);
+ pw.print(" mLastPowerCheckRealtime=");
+ TimeUtils.formatDuration(mLastPowerCheckRealtime, pw);
+ pw.println("");
+ pw.print(" mLastPowerCheckUptime=");
+ TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
+ pw.println("");
+ pw.println(" mGoingToSleep=" + mStackSupervisor.mGoingToSleep);
+ pw.println(" mLaunchingActivity=" + mStackSupervisor.mLaunchingActivity);
+ pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
+ pw.println(" mNumNonCachedProcs=" + mNumNonCachedProcs
+ + " (" + mLruProcesses.size() + " total)"
+ + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
+ + " mNumServiceProcs=" + mNumServiceProcs
+ + " mNewNumServiceProcs=" + mNewNumServiceProcs);
+ }
}
- if (dumpAll) {
- pw.println(" Total persistent processes: " + numPers);
- pw.println(" mStartRunning=" + mStartRunning
- + " mProcessesReady=" + mProcessesReady
- + " mSystemReady=" + mSystemReady);
- pw.println(" mBooting=" + mBooting
- + " mBooted=" + mBooted
- + " mFactoryTest=" + mFactoryTest);
- pw.print(" mLastPowerCheckRealtime=");
- TimeUtils.formatDuration(mLastPowerCheckRealtime, pw);
- pw.println("");
- pw.print(" mLastPowerCheckUptime=");
- TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
- pw.println("");
- pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep);
- pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity);
- pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
- pw.println(" mNumNonHiddenProcs=" + mNumNonHiddenProcs
- + " mNumHiddenProcs=" + mNumHiddenProcs
- + " mNumServiceProcs=" + mNumServiceProcs
- + " mNewNumServiceProcs=" + mNewNumServiceProcs);
+
+ if (!printedAnything) {
+ pw.println(" (nothing)");
}
-
- return true;
}
boolean dumpProcessesToGc(FileDescriptor fd, PrintWriter pw, String[] args,
@@ -9758,7 +10238,7 @@
continue;
}
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Processes that are waiting to GC:");
printed = true;
@@ -9781,7 +10261,7 @@
boolean needSep = false;
if (mLruProcesses.size() > 0) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" OOM levels:");
pw.print(" SYSTEM_ADJ: "); pw.println(ProcessList.SYSTEM_ADJ);
@@ -9795,14 +10275,14 @@
pw.print(" HOME_APP_ADJ: "); pw.println(ProcessList.HOME_APP_ADJ);
pw.print(" PREVIOUS_APP_ADJ: "); pw.println(ProcessList.PREVIOUS_APP_ADJ);
pw.print(" SERVICE_B_ADJ: "); pw.println(ProcessList.SERVICE_B_ADJ);
- pw.print(" HIDDEN_APP_MIN_ADJ: "); pw.println(ProcessList.HIDDEN_APP_MIN_ADJ);
- pw.print(" HIDDEN_APP_MAX_ADJ: "); pw.println(ProcessList.HIDDEN_APP_MAX_ADJ);
+ pw.print(" CACHED_APP_MIN_ADJ: "); pw.println(ProcessList.CACHED_APP_MIN_ADJ);
+ pw.print(" CACHED_APP_MAX_ADJ: "); pw.println(ProcessList.CACHED_APP_MAX_ADJ);
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Process OOM control:");
dumpProcessOomList(pw, this, mLruProcesses, " ",
- "Proc", "PERS", true, null);
+ "Proc", "PERS", true, null, false, null);
needSep = true;
}
@@ -9920,32 +10400,10 @@
*/
protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
int opti, boolean dumpAll) {
- ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
-
- if ("all".equals(name)) {
- synchronized (this) {
- for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
- activities.add(r1);
- }
- }
- } else if ("top".equals(name)) {
- synchronized (this) {
- final int N = mMainStack.mHistory.size();
- if (N > 0) {
- activities.add((ActivityRecord)mMainStack.mHistory.get(N-1));
- }
- }
- } else {
- ItemMatcher matcher = new ItemMatcher();
- matcher.build(name);
-
- synchronized (this) {
- for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
- if (matcher.match(r1, r1.intent.getComponent())) {
- activities.add(r1);
- }
- }
- }
+ ArrayList<ActivityRecord> activities;
+
+ synchronized (this) {
+ activities = mStackSupervisor.getDumpActivitiesLocked(name);
}
if (activities.size() <= 0) {
@@ -9953,12 +10411,12 @@
}
String[] newArgs = new String[args.length - opti];
- if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+ System.arraycopy(args, opti, newArgs, 0, args.length - opti);
TaskRecord lastTask = null;
boolean needSep = false;
for (int i=activities.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)activities.get(i);
+ ActivityRecord r = activities.get(i);
if (needSep) {
pw.println();
}
@@ -10016,10 +10474,11 @@
}
}
- boolean dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
boolean onlyHistory = false;
+ boolean printedAnything = false;
if ("history".equals(dumpPackage)) {
if (opti < args.length && "-s".equals(args[opti])) {
@@ -10044,6 +10503,7 @@
pw.println(" Registered Receivers:");
needSep = true;
printed = true;
+ printedAnything = true;
}
pw.print(" * "); pw.println(r);
r.dump(pw, " ");
@@ -10054,11 +10514,13 @@
"\n Receiver Resolver Table:" : " Receiver Resolver Table:",
" ", dumpPackage, false)) {
needSep = true;
+ printedAnything = true;
}
}
for (BroadcastQueue q : mBroadcastQueues) {
needSep = q.dumpLocked(fd, pw, args, opti, dumpAll, dumpPackage, needSep);
+ printedAnything |= needSep;
}
needSep = true;
@@ -10069,6 +10531,7 @@
pw.println();
}
needSep = true;
+ printedAnything = true;
pw.print(" Sticky broadcasts for user ");
pw.print(mStickyBroadcasts.keyAt(user)); pw.println(":");
StringBuilder sb = new StringBuilder(128);
@@ -10106,21 +10569,26 @@
pw.println(" mHandler:");
mHandler.dump(new PrintWriterPrinter(pw), " ");
needSep = true;
+ printedAnything = true;
}
- return needSep;
+ if (!printedAnything) {
+ pw.println(" (nothing)");
+ }
}
- boolean dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ void dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
- boolean needSep = true;
+ boolean needSep;
+ boolean printedAnything = false;
ItemMatcher matcher = new ItemMatcher();
matcher.build(args, opti);
pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
- mProviderMap.dumpProvidersLocked(pw, dumpAll);
+ needSep = mProviderMap.dumpProvidersLocked(pw, dumpAll, dumpPackage);
+ printedAnything |= needSep;
if (mLaunchingProviders.size() > 0) {
boolean printed = false;
@@ -10130,10 +10598,11 @@
continue;
}
if (!printed) {
- if (needSep) pw.println(" ");
+ if (needSep) pw.println();
needSep = true;
pw.println(" Launching content providers:");
printed = true;
+ printedAnything = true;
}
pw.print(" Launching #"); pw.print(i); pw.print(": ");
pw.println(r);
@@ -10141,13 +10610,29 @@
}
if (mGrantedUriPermissions.size() > 0) {
- if (needSep) pw.println();
- needSep = true;
- pw.println("Granted Uri Permissions:");
+ boolean printed = false;
+ int dumpUid = -2;
+ if (dumpPackage != null) {
+ try {
+ dumpUid = mContext.getPackageManager().getPackageUid(dumpPackage, 0);
+ } catch (NameNotFoundException e) {
+ dumpUid = -1;
+ }
+ }
for (int i=0; i<mGrantedUriPermissions.size(); i++) {
int uid = mGrantedUriPermissions.keyAt(i);
+ if (dumpUid >= -1 && UserHandle.getAppId(uid) != dumpUid) {
+ continue;
+ }
HashMap<Uri, UriPermission> perms
= mGrantedUriPermissions.valueAt(i);
+ if (!printed) {
+ if (needSep) pw.println();
+ needSep = true;
+ pw.println(" Granted Uri Permissions:");
+ printed = true;
+ printedAnything = true;
+ }
pw.print(" * UID "); pw.print(uid);
pw.println(" holds:");
for (UriPermission perm : perms.values()) {
@@ -10157,18 +10642,20 @@
}
}
}
- needSep = true;
}
-
- return needSep;
+
+ if (!printedAnything) {
+ pw.println(" (nothing)");
+ }
}
- boolean dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ void dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
- boolean needSep = false;
-
+ boolean printed = false;
+
+ pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
+
if (mIntentSenderRecords.size() > 0) {
- boolean printed = false;
Iterator<WeakReference<PendingIntentRecord>> it
= mIntentSenderRecords.values().iterator();
while (it.hasNext()) {
@@ -10178,11 +10665,7 @@
|| !dumpPackage.equals(rec.key.packageName))) {
continue;
}
- if (!printed) {
- pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
- printed = true;
- }
- needSep = true;
+ printed = true;
if (rec != null) {
pw.print(" * "); pw.println(rec);
if (dumpAll) {
@@ -10193,76 +10676,9 @@
}
}
}
-
- return needSep;
- }
- private static final void dumpHistoryList(FileDescriptor fd, PrintWriter pw, List list,
- String prefix, String label, boolean complete, boolean brief, boolean client,
- String dumpPackage) {
- TaskRecord lastTask = null;
- boolean needNL = false;
- final String innerPrefix = prefix + " ";
- final String[] args = new String[0];
- for (int i=list.size()-1; i>=0; i--) {
- final ActivityRecord r = (ActivityRecord)list.get(i);
- if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
- continue;
- }
- final boolean full = !brief && (complete || !r.isInHistory());
- if (needNL) {
- pw.println(" ");
- needNL = false;
- }
- if (lastTask != r.task) {
- lastTask = r.task;
- pw.print(prefix);
- pw.print(full ? "* " : " ");
- pw.println(lastTask);
- if (full) {
- lastTask.dump(pw, prefix + " ");
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- if (lastTask.intent != null) {
- pw.print(prefix); pw.print(" ");
- pw.println(lastTask.intent.toInsecureStringWithClip());
- }
- }
- }
- pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
- pw.print(" #"); pw.print(i); pw.print(": ");
- pw.println(r);
- if (full) {
- r.dump(pw, innerPrefix);
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
- if (r.app != null) {
- pw.print(innerPrefix); pw.println(r.app);
- }
- }
- if (client && r.app != null && r.app.thread != null) {
- // flush anything that is already in the PrintWriter since the thread is going
- // to write to the file descriptor directly
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
- r.appToken, innerPrefix, args);
- // Short timeout, since blocking here can
- // deadlock with the application.
- tp.go(fd, 2000);
- } finally {
- tp.kill();
- }
- } catch (IOException e) {
- pw.println(innerPrefix + "Failure while dumping the activity: " + e);
- } catch (RemoteException e) {
- pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
- }
- needNL = true;
- }
+ if (!printed) {
+ pw.println(" (nothing)");
}
}
@@ -10298,13 +10714,13 @@
private static final boolean dumpProcessOomList(PrintWriter pw,
ActivityManagerService service, List<ProcessRecord> origList,
String prefix, String normalLabel, String persistentLabel,
- boolean inclDetails, String dumpPackage) {
+ boolean inclDetails, String dumpPackage, boolean needSep, String header) {
ArrayList<Pair<ProcessRecord, Integer>> list
= new ArrayList<Pair<ProcessRecord, Integer>>(origList.size());
for (int i=0; i<origList.size(); i++) {
ProcessRecord r = origList.get(i);
- if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i));
@@ -10313,7 +10729,14 @@
if (list.size() <= 0) {
return false;
}
-
+
+ if (header != null) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(header);
+ }
+
Comparator<Pair<ProcessRecord, Integer>> comparator
= new Comparator<Pair<ProcessRecord, Integer>>() {
@Override
@@ -10339,8 +10762,8 @@
for (int i=list.size()-1; i>=0; i--) {
ProcessRecord r = list.get(i).first;
String oomAdj;
- if (r.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
- oomAdj = buildOomTag("bak", " ", r.setAdj, ProcessList.HIDDEN_APP_MIN_ADJ);
+ if (r.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+ oomAdj = buildOomTag("cch", " ", r.setAdj, ProcessList.CACHED_APP_MIN_ADJ);
} else if (r.setAdj >= ProcessList.SERVICE_B_ADJ) {
oomAdj = buildOomTag("svcb ", null, r.setAdj, ProcessList.SERVICE_B_ADJ);
} else if (r.setAdj >= ProcessList.PREVIOUS_APP_ADJ) {
@@ -10415,8 +10838,8 @@
pw.print(prefix);
pw.print(" ");
pw.print("oom: max="); pw.print(r.maxAdj);
- pw.print(" hidden="); pw.print(r.hiddenAdj);
- pw.print(" client="); pw.print(r.clientHiddenAdj);
+ pw.print(" cached="); pw.print(r.cachedAdj);
+ pw.print(" client="); pw.print(r.clientCachedAdj);
pw.print(" empty="); pw.print(r.emptyAdj);
pw.print(" curRaw="); pw.print(r.curRawAdj);
pw.print(" setRaw="); pw.print(r.setRawAdj);
@@ -10425,7 +10848,7 @@
pw.print(prefix);
pw.print(" ");
pw.print("keeping="); pw.print(r.keeping);
- pw.print(" hidden="); pw.print(r.hidden);
+ pw.print(" cached="); pw.print(r.cached);
pw.print(" empty="); pw.print(r.empty);
pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
@@ -10566,23 +10989,37 @@
}
final static class MemItem {
+ final boolean isProc;
final String label;
final String shortLabel;
final long pss;
final int id;
+ final boolean hasActivities;
ArrayList<MemItem> subitems;
- public MemItem(String _label, String _shortLabel, long _pss, int _id) {
+ public MemItem(String _label, String _shortLabel, long _pss, int _id,
+ boolean _hasActivities) {
+ isProc = true;
label = _label;
shortLabel = _shortLabel;
pss = _pss;
id = _id;
+ hasActivities = _hasActivities;
+ }
+
+ public MemItem(String _label, String _shortLabel, long _pss, int _id) {
+ isProc = false;
+ label = _label;
+ shortLabel = _shortLabel;
+ pss = _pss;
+ id = _id;
+ hasActivities = false;
}
}
- static final void dumpMemItems(PrintWriter pw, String prefix, ArrayList<MemItem> items,
- boolean sort) {
- if (sort) {
+ static final void dumpMemItems(PrintWriter pw, String prefix, String tag,
+ ArrayList<MemItem> items, boolean sort, boolean isCompact) {
+ if (sort && !isCompact) {
Collections.sort(items, new Comparator<MemItem>() {
@Override
public int compare(MemItem lhs, MemItem rhs) {
@@ -10598,9 +11035,19 @@
for (int i=0; i<items.size(); i++) {
MemItem mi = items.get(i);
- pw.print(prefix); pw.printf("%7d kB: ", mi.pss); pw.println(mi.label);
+ if (!isCompact) {
+ pw.print(prefix); pw.printf("%7d kB: ", mi.pss); pw.println(mi.label);
+ } else if (mi.isProc) {
+ pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel);
+ pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss);
+ pw.println(mi.hasActivities ? ",a" : ",e");
+ } else {
+ pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(",");
+ pw.println(mi.pss);
+ }
if (mi.subitems != null) {
- dumpMemItems(pw, prefix + " ", mi.subitems, true);
+ dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems,
+ true, isCompact);
}
}
}
@@ -10637,20 +11084,28 @@
ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ,
ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
ProcessList.BACKUP_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
- ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.HIDDEN_APP_MAX_ADJ
+ ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MAX_ADJ
};
static final String[] DUMP_MEM_OOM_LABEL = new String[] {
"System", "Persistent", "Foreground",
"Visible", "Perceptible", "Heavy Weight",
"Backup", "A Services", "Home", "Previous",
- "B Services", "Background"
+ "B Services", "Cached"
+ };
+ static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] {
+ "sys", "pers", "fore",
+ "vis", "percept", "heavy",
+ "backup", "servicea", "home", "prev",
+ "serviceb", "cached"
};
final void dumpApplicationMemoryUsage(FileDescriptor fd,
PrintWriter pw, String prefix, String[] args, boolean brief,
PrintWriter categoryPw, StringBuilder outTag, StringBuilder outStack) {
- boolean dumpAll = false;
+ boolean dumpDetails = false;
+ boolean dumpDalvik = false;
boolean oomOnly = false;
+ boolean isCompact = false;
int opti = 0;
while (opti < args.length) {
@@ -10660,12 +11115,19 @@
}
opti++;
if ("-a".equals(opt)) {
- dumpAll = true;
+ dumpDetails = true;
+ dumpDalvik = true;
+ } else if ("-d".equals(opt)) {
+ dumpDalvik = true;
+ } else if ("-c".equals(opt)) {
+ isCompact = true;
} else if ("--oom".equals(opt)) {
oomOnly = true;
} else if ("-h".equals(opt)) {
- pw.println("meminfo dump options: [-a] [--oom] [process]");
+ pw.println("meminfo dump options: [-a] [-d] [-c] [--oom] [process]");
pw.println(" -a: include all available information for each process.");
+ pw.println(" -d: include dalvik details when dumping process details.");
+ pw.println(" -c: dump in a compact machine-parseable representation.");
pw.println(" --oom: only show processes organized by oom adj.");
pw.println("If [process] is specified it can be the name or ");
pw.println("pid of a specific process to dump.");
@@ -10684,14 +11146,13 @@
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
- if (procs.size() == 1 || isCheckinRequest) {
- dumpAll = true;
+ if (!brief && !oomOnly && (procs.size() == 1 || isCheckinRequest)) {
+ dumpDetails = true;
}
- if (isCheckinRequest) {
+ if (isCheckinRequest || isCompact) {
// short checkin version
- pw.println(uptime + "," + realtime);
- pw.flush();
+ pw.print("time,"); pw.print(uptime); pw.print(","); pw.println(realtime);
} else {
pw.println("Applications Memory Usage (kB):");
pw.println("Uptime: " + uptime + " Realtime: " + realtime);
@@ -10709,18 +11170,30 @@
new ArrayList[DUMP_MEM_OOM_LABEL.length];
long totalPss = 0;
+ long cachedPss = 0;
+ Debug.MemoryInfo mi = null;
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = procs.get(i);
- if (r.thread != null) {
- if (!isCheckinRequest && dumpAll) {
- pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **");
- pw.flush();
+ final ProcessRecord r = procs.get(i);
+ final IApplicationThread thread;
+ final int pid;
+ final int oomAdj;
+ final boolean hasActivities;
+ synchronized (this) {
+ thread = r.thread;
+ pid = r.pid;
+ oomAdj = r.getSetAdjWithServices();
+ hasActivities = r.hasActivities;
+ }
+ if (thread != null) {
+ if (!isCheckinRequest && dumpDetails) {
+ pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
}
- Debug.MemoryInfo mi = null;
- if (dumpAll) {
+ if (dumpDetails) {
try {
- mi = r.thread.dumpMemInfo(fd, isCheckinRequest, dumpAll, innerArgs);
+ pw.flush();
+ mi = null;
+ mi = thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs);
} catch (RemoteException e) {
if (!isCheckinRequest) {
pw.println("Got RemoteException!");
@@ -10728,15 +11201,30 @@
}
}
} else {
- mi = new Debug.MemoryInfo();
- Debug.getMemoryInfo(r.pid, mi);
+ if (mi == null) {
+ mi = new Debug.MemoryInfo();
+ }
+ if (!brief && !oomOnly) {
+ Debug.getMemoryInfo(pid, mi);
+ } else {
+ mi.dalvikPss = (int)Debug.getPss(pid);
+ }
+ }
+
+ final long myTotalPss = mi.getTotalPss();
+
+ synchronized (this) {
+ if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
+ // Record this for posterity if the process has been stable.
+ r.baseProcessTracker.addPss(myTotalPss, true);
+ }
}
if (!isCheckinRequest && mi != null) {
- long myTotalPss = mi.getTotalPss();
totalPss += myTotalPss;
- MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")",
- r.processName, myTotalPss, 0);
+ MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
+ (hasActivities ? " / activities)" : ")"),
+ r.processName, myTotalPss, pid, hasActivities);
procMems.add(pssItem);
nativePss += mi.nativePss;
@@ -10748,8 +11236,12 @@
otherPss -= mem;
}
+ if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+ cachedPss += myTotalPss;
+ }
+
for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
- if (r.setAdj <= DUMP_MEM_OOM_ADJ[oomIndex]
+ if (oomAdj <= DUMP_MEM_OOM_ADJ[oomIndex]
|| oomIndex == (oomPss.length-1)) {
oomPss[oomIndex] += myTotalPss;
if (oomProcs[oomIndex] == null) {
@@ -10777,7 +11269,8 @@
ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
for (int j=0; j<oomPss.length; j++) {
if (oomPss[j] != 0) {
- String label = DUMP_MEM_OOM_LABEL[j];
+ String label = isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
+ : DUMP_MEM_OOM_LABEL[j];
MemItem item = new MemItem(label, label, oomPss[j],
DUMP_MEM_OOM_ADJ[j]);
item.subitems = oomProcs[j];
@@ -10816,7 +11309,7 @@
}
}
for (int j=0; j<miCat.subitems.size(); j++) {
- MemItem mi = miCat.subitems.get(j);
+ MemItem memi = miCat.subitems.get(j);
if (j > 0) {
if (outTag != null) {
outTag.append(" ");
@@ -10826,10 +11319,10 @@
}
}
if (outTag != null && miCat.id <= ProcessList.FOREGROUND_APP_ADJ) {
- appendMemBucket(outTag, mi.pss, mi.shortLabel, false);
+ appendMemBucket(outTag, memi.pss, memi.shortLabel, false);
}
if (outStack != null) {
- appendMemBucket(outStack, mi.pss, mi.shortLabel, true);
+ appendMemBucket(outStack, memi.pss, memi.shortLabel, true);
}
}
if (outStack != null && miCat.id >= ProcessList.FOREGROUND_APP_ADJ) {
@@ -10847,45 +11340,79 @@
}
}
- if (!brief && !oomOnly) {
+ if (!brief && !oomOnly && !isCompact) {
pw.println();
pw.println("Total PSS by process:");
- dumpMemItems(pw, " ", procMems, true);
+ dumpMemItems(pw, " ", "proc", procMems, true, isCompact);
pw.println();
}
- pw.println("Total PSS by OOM adjustment:");
- dumpMemItems(pw, " ", oomMems, false);
- if (!oomOnly) {
- PrintWriter out = categoryPw != null ? categoryPw : pw;
- out.println();
- out.println("Total PSS by category:");
- dumpMemItems(out, " ", catMems, true);
+ if (!isCompact) {
+ pw.println("Total PSS by OOM adjustment:");
}
- pw.println();
- pw.print("Total PSS: "); pw.print(totalPss); pw.println(" kB");
- final int[] SINGLE_LONG_FORMAT = new int[] {
- Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG
- };
- long[] longOut = new long[1];
- Process.readProcFile("/sys/kernel/mm/ksm/pages_shared",
- SINGLE_LONG_FORMAT, null, longOut, null);
- long shared = longOut[0] * ProcessList.PAGE_SIZE / 1024;
- longOut[0] = 0;
- Process.readProcFile("/sys/kernel/mm/ksm/pages_sharing",
- SINGLE_LONG_FORMAT, null, longOut, null);
- long sharing = longOut[0] * ProcessList.PAGE_SIZE / 1024;
- longOut[0] = 0;
- Process.readProcFile("/sys/kernel/mm/ksm/pages_unshared",
- SINGLE_LONG_FORMAT, null, longOut, null);
- long unshared = longOut[0] * ProcessList.PAGE_SIZE / 1024;
- longOut[0] = 0;
- Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile",
- SINGLE_LONG_FORMAT, null, longOut, null);
- long voltile = longOut[0] * ProcessList.PAGE_SIZE / 1024;
- pw.print(" KSM: "); pw.print(sharing); pw.print(" kB saved from shared ");
- pw.print(shared); pw.println(" kB");
- pw.print(" "); pw.print(unshared); pw.print(" kB unshared; ");
- pw.print(voltile); pw.println(" kB volatile");
+ dumpMemItems(pw, " ", "oom", oomMems, false, isCompact);
+ if (!brief && !oomOnly) {
+ PrintWriter out = categoryPw != null ? categoryPw : pw;
+ if (!isCompact) {
+ out.println();
+ out.println("Total PSS by category:");
+ }
+ dumpMemItems(out, " ", "cat", catMems, true, isCompact);
+ }
+ if (!isCompact) {
+ pw.println();
+ }
+ if (!brief) {
+ MemInfoReader memInfo = new MemInfoReader();
+ memInfo.readMemInfo();
+ if (!isCompact) {
+ pw.print("Total RAM: "); pw.print(memInfo.getTotalSize()/1024);
+ pw.println(" kB");
+ pw.print(" Free RAM: "); pw.print(cachedPss + (memInfo.getCachedSize()/1024)
+ + (memInfo.getFreeSize()/1024)); pw.println(" kB");
+ } else {
+ pw.print("ram,"); pw.print(memInfo.getTotalSize()/1024); pw.print(",");
+ pw.print(cachedPss + (memInfo.getCachedSize()/1024)
+ + (memInfo.getFreeSize()/1024)); pw.print(",");
+ pw.println(totalPss - cachedPss);
+ }
+ }
+ if (!isCompact) {
+ pw.print(" Used PSS: "); pw.print(totalPss - cachedPss); pw.println(" kB");
+ }
+ if (!brief) {
+ final int[] SINGLE_LONG_FORMAT = new int[] {
+ Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG
+ };
+ long[] longOut = new long[1];
+ Process.readProcFile("/sys/kernel/mm/ksm/pages_shared",
+ SINGLE_LONG_FORMAT, null, longOut, null);
+ long shared = longOut[0] * ProcessList.PAGE_SIZE / 1024;
+ longOut[0] = 0;
+ Process.readProcFile("/sys/kernel/mm/ksm/pages_sharing",
+ SINGLE_LONG_FORMAT, null, longOut, null);
+ long sharing = longOut[0] * ProcessList.PAGE_SIZE / 1024;
+ longOut[0] = 0;
+ Process.readProcFile("/sys/kernel/mm/ksm/pages_unshared",
+ SINGLE_LONG_FORMAT, null, longOut, null);
+ long unshared = longOut[0] * ProcessList.PAGE_SIZE / 1024;
+ longOut[0] = 0;
+ Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile",
+ SINGLE_LONG_FORMAT, null, longOut, null);
+ long voltile = longOut[0] * ProcessList.PAGE_SIZE / 1024;
+ if (!isCompact) {
+ if (sharing != 0 || shared != 0 || unshared != 0 || voltile != 0) {
+ pw.print(" KSM: "); pw.print(sharing);
+ pw.print(" kB saved from shared ");
+ pw.print(shared); pw.println(" kB");
+ pw.print(" "); pw.print(unshared); pw.print(" kB unshared; ");
+ pw.print(voltile); pw.println(" kB volatile");
+ }
+ } else {
+ pw.print("ksm,"); pw.print(sharing); pw.print(",");
+ pw.print(shared); pw.print(","); pw.print(unshared); pw.print(",");
+ pw.println(voltile);
+ }
+ }
}
}
@@ -10976,6 +11503,7 @@
}
mProcessesToGc.remove(app);
+ mPendingPssProcesses.remove(app);
// Dismiss any open dialogs.
if (app.crashDialog != null && !app.forceCrashReport) {
@@ -10994,7 +11522,7 @@
app.crashing = false;
app.notResponding = false;
- app.resetPackageList();
+ app.resetPackageList(mProcessTracker);
app.unlinkDeathRecipient();
app.thread = null;
app.forcingToForeground = null;
@@ -11008,24 +11536,20 @@
boolean restart = false;
// Remove published content providers.
- if (!app.pubProviders.isEmpty()) {
- Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
- while (it.hasNext()) {
- ContentProviderRecord cpr = it.next();
-
- final boolean always = app.bad || !allowRestart;
- if (removeDyingProviderLocked(app, cpr, always) || always) {
- // We left the provider in the launching list, need to
- // restart it.
- restart = true;
- }
-
- cpr.provider = null;
- cpr.proc = null;
+ for (int i=app.pubProviders.size()-1; i>=0; i--) {
+ ContentProviderRecord cpr = app.pubProviders.valueAt(i);
+ final boolean always = app.bad || !allowRestart;
+ if (removeDyingProviderLocked(app, cpr, always) || always) {
+ // We left the provider in the launching list, need to
+ // restart it.
+ restart = true;
}
- app.pubProviders.clear();
+
+ cpr.provider = null;
+ cpr.proc = null;
}
-
+ app.pubProviders.clear();
+
// Take care of any launching providers waiting for this process.
if (checkAppInLaunchingProvidersLocked(app, false)) {
restart = true;
@@ -11555,7 +12079,7 @@
private final List getStickiesLocked(String action, IntentFilter filter,
List cur, int userId) {
final ContentResolver resolver = mContext.getContentResolver();
- HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+ ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if (stickies == null) {
return cur;
}
@@ -11613,7 +12137,7 @@
+ ") when registering receiver " + receiver);
}
if (callerApp.info.uid != Process.SYSTEM_UID &&
- !callerApp.pkgList.contains(callerPackage)) {
+ !callerApp.pkgList.containsKey(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
@@ -11967,7 +12491,8 @@
String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
if (list != null && (list.length > 0)) {
for (String pkg : list) {
- forceStopPackageLocked(pkg, -1, false, true, true, false, userId);
+ forceStopPackageLocked(pkg, -1, false, true, true, false, userId,
+ "storage unmount");
}
sendPackageBroadcastLocked(
IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list, userId);
@@ -11976,17 +12501,22 @@
Uri data = intent.getData();
String ssp;
if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
+ boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(
+ intent.getAction());
if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
forceStopPackageLocked(ssp,
intent.getIntExtra(Intent.EXTRA_UID, -1), false, true, true,
- false, userId);
+ false, userId, removed ? "pkg removed" : "pkg changed");
}
- if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ if (removed) {
sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
new String[] {ssp}, userId);
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
mAppOpsService.packageRemoved(
intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);
+
+ // Remove all permissions granted from/to this package
+ removeUriPermissionsForPackageLocked(ssp, userId, true);
}
}
}
@@ -12057,7 +12587,7 @@
// But first, if this is not a broadcast to all users, then
// make sure it doesn't conflict with an existing broadcast to
// all users.
- HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
+ ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
UserHandle.USER_ALL);
if (stickies != null) {
ArrayList<Intent> list = stickies.get(intent.getAction());
@@ -12074,9 +12604,9 @@
}
}
}
- HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+ ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if (stickies == null) {
- stickies = new HashMap<String, ArrayList<Intent>>();
+ stickies = new ArrayMap<String, ArrayList<Intent>>();
mStickyBroadcasts.put(userId, stickies);
}
ArrayList<Intent> list = stickies.get(intent.getAction());
@@ -12329,7 +12859,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+ ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if (stickies != null) {
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list != null) {
@@ -12450,7 +12980,8 @@
final long origId = Binder.clearCallingIdentity();
// Instrumentation can kill and relaunch even persistent processes
- forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, userId);
+ forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, userId,
+ "start instr");
ProcessRecord app = addAppLocked(ai, false);
app.instrumentationClass = className;
app.instrumentationInfo = ai;
@@ -12517,7 +13048,8 @@
app.instrumentationProfileFile = null;
app.instrumentationArguments = null;
- forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId);
+ forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId,
+ "finished inst");
}
public void finishInstrumentation(IApplicationThread target,
@@ -12563,6 +13095,10 @@
return config;
}
+ ActivityStack getFocusedStack() {
+ return mStackSupervisor.getFocusedStack();
+ }
+
public Configuration getConfiguration() {
Configuration ci;
synchronized(this) {
@@ -12624,9 +13160,7 @@
if (mHeadless) return true;
int changes = 0;
-
- boolean kept = true;
-
+
if (values != null) {
Configuration newConfig = new Configuration(mConfiguration);
changes = newConfig.updateFrom(values);
@@ -12704,25 +13238,27 @@
}
}
}
-
+
+ boolean kept = true;
+ final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
if (changes != 0 && starting == null) {
// If the configuration changed, and the caller is not already
// in the process of starting an activity, then find the top
// activity to check if its configuration needs to change.
- starting = mMainStack.topRunningActivityLocked(null);
+ starting = mainStack.topRunningActivityLocked(null);
}
-
+
if (starting != null) {
- kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
+ kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
- mMainStack.ensureActivitiesVisibleLocked(starting, changes);
+ mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
}
-
+
if (values != null && mWindowManager != null) {
mWindowManager.setNewConfiguration(mConfiguration);
}
-
+
return kept;
}
@@ -12738,7 +13274,7 @@
return !(config.keyboard == Configuration.KEYBOARD_NOKEYS
&& config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH);
}
-
+
/**
* Save the locale. You must be inside a synchronized (this) block.
*/
@@ -12764,96 +13300,13 @@
public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
Intent resultData) {
- ComponentName dest = destIntent.getComponent();
synchronized (this) {
- ActivityRecord srec = ActivityRecord.forToken(token);
- if (srec == null) {
- return false;
+ final ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack != null) {
+ return stack.navigateUpToLocked(token, destIntent, resultCode, resultData);
}
- ArrayList<ActivityRecord> history = srec.stack.mHistory;
- final int start = history.indexOf(srec);
- if (start < 0) {
- // Current activity is not in history stack; do nothing.
- return false;
- }
- int finishTo = start - 1;
- ActivityRecord parent = null;
- boolean foundParentInTask = false;
- if (dest != null) {
- TaskRecord tr = srec.task;
- for (int i = start - 1; i >= 0; i--) {
- ActivityRecord r = history.get(i);
- if (tr != r.task) {
- // Couldn't find parent in the same task; stop at the one above this.
- // (Root of current task; in-app "home" behavior)
- // Always at least finish the current activity.
- finishTo = Math.min(start - 1, i + 1);
- parent = history.get(finishTo);
- break;
- } else if (r.info.packageName.equals(dest.getPackageName()) &&
- r.info.name.equals(dest.getClassName())) {
- finishTo = i;
- parent = r;
- foundParentInTask = true;
- break;
- }
- }
- }
-
- if (mController != null) {
- ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
- if (next != null) {
- // ask watcher if this is allowed
- boolean resumeOK = true;
- try {
- resumeOK = mController.activityResuming(next.packageName);
- } catch (RemoteException e) {
- mController = null;
- Watchdog.getInstance().setActivityController(null);
- }
-
- if (!resumeOK) {
- return false;
- }
- }
- }
- final long origId = Binder.clearCallingIdentity();
- for (int i = start; i > finishTo; i--) {
- ActivityRecord r = history.get(i);
- mMainStack.requestFinishActivityLocked(r.appToken, resultCode, resultData,
- "navigate-up", true);
- // Only return the supplied result for the first activity finished
- resultCode = Activity.RESULT_CANCELED;
- resultData = null;
- }
-
- if (parent != null && foundParentInTask) {
- final int parentLaunchMode = parent.info.launchMode;
- final int destIntentFlags = destIntent.getFlags();
- if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
- parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
- parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
- (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent);
- } else {
- try {
- ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
- destIntent.getComponent(), 0, srec.userId);
- int res = mMainStack.startActivityLocked(srec.app.thread, destIntent,
- null, aInfo, parent.appToken, null,
- 0, -1, parent.launchedFromUid, parent.launchedFromPackage,
- 0, null, true, null);
- foundParentInTask = res == ActivityManager.START_SUCCESS;
- } catch (RemoteException e) {
- foundParentInTask = false;
- }
- mMainStack.requestFinishActivityLocked(parent.appToken, resultCode,
- resultData, "navigate-up", true);
- }
- }
- Binder.restoreCallingIdentity(origId);
- return foundParentInTask;
+ return false;
}
}
@@ -12899,18 +13352,18 @@
return null;
}
- private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj, int clientHiddenAdj,
+ private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, int clientCachedAdj,
int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
if (mAdjSeq == app.adjSeq) {
// This adjustment has already been computed. If we are calling
// from the top, we may have already computed our adjustment with
- // an earlier hidden adjustment that isn't really for us... if
- // so, use the new hidden adjustment.
- if (!recursed && app.hidden) {
+ // an earlier cached adjustment that isn't really for us... if
+ // so, use the new cached adjustment.
+ if (!recursed && app.cached) {
if (app.hasActivities) {
- app.curAdj = app.curRawAdj = app.nonStoppingAdj = hiddenAdj;
+ app.curAdj = app.curRawAdj = app.nonStoppingAdj = cachedAdj;
} else if (app.hasClientActivities) {
- app.curAdj = app.curRawAdj = app.nonStoppingAdj = clientHiddenAdj;
+ app.curAdj = app.curRawAdj = app.nonStoppingAdj = clientCachedAdj;
} else {
app.curAdj = app.curRawAdj = app.nonStoppingAdj = emptyAdj;
}
@@ -12921,14 +13374,14 @@
if (app.thread == null) {
app.adjSeq = mAdjSeq;
app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- return (app.curAdj=app.curRawAdj=ProcessList.HIDDEN_APP_MAX_ADJ);
+ return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
}
app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
app.adjSource = null;
app.adjTarget = null;
app.empty = false;
- app.hidden = false;
+ app.cached = false;
app.hasClientActivities = false;
final int activitiesSize = app.activities.size();
@@ -13006,12 +13459,12 @@
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "exec-service";
} else {
- // Assume process is hidden (has activities); we will correct
+ // Assume process is cached (has activities); we will correct
// later if this is not the case.
- adj = hiddenAdj;
+ adj = cachedAdj;
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- app.hidden = true;
- app.adjType = "bg-act";
+ app.cached = true;
+ app.adjType = "cch-act";
}
boolean hasStoppingActivities = false;
@@ -13027,7 +13480,7 @@
app.adjType = "visible";
}
schedGroup = Process.THREAD_GROUP_DEFAULT;
- app.hidden = false;
+ app.cached = false;
app.hasActivities = true;
foregroundActivities = true;
break;
@@ -13036,13 +13489,13 @@
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
app.adjType = "pausing";
}
- app.hidden = false;
+ app.cached = false;
foregroundActivities = true;
} else if (r.state == ActivityState.STOPPING) {
// We will apply the actual adjustment later, because
// we want to allow this process to immediately go through
// any memory trimming that is in effect.
- app.hidden = false;
+ app.cached = false;
foregroundActivities = true;
hasStoppingActivities = true;
}
@@ -13052,16 +13505,16 @@
}
}
- if (adj == hiddenAdj && !app.hasActivities) {
+ if (adj == cachedAdj && !app.hasActivities) {
if (app.hasClientActivities) {
- adj = clientHiddenAdj;
- app.adjType = "bg-client-act";
+ adj = clientCachedAdj;
+ app.adjType = "cch-client-act";
} else {
// Whoops, this process is completely empty as far as we know
// at this point.
adj = emptyAdj;
app.empty = true;
- app.adjType = "bg-empty";
+ app.adjType = "cch-empty";
}
}
@@ -13069,13 +13522,13 @@
if (app.foregroundServices) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- app.hidden = false;
+ app.cached = false;
app.adjType = "fg-service";
schedGroup = Process.THREAD_GROUP_DEFAULT;
} else if (app.forcingToForeground != null) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- app.hidden = false;
+ app.cached = false;
app.adjType = "force-fg";
app.adjSource = app.forcingToForeground;
schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -13090,7 +13543,7 @@
// We don't want to kill the current heavy-weight process.
adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- app.hidden = false;
+ app.cached = false;
app.adjType = "heavy";
}
@@ -13099,7 +13552,7 @@
// home app, so we don't want to let it go into the background.
adj = ProcessList.HOME_APP_ADJ;
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- app.hidden = false;
+ app.cached = false;
app.adjType = "home";
}
@@ -13110,7 +13563,7 @@
// a good experience around switching between two apps.
adj = ProcessList.PREVIOUS_APP_ADJ;
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- app.hidden = false;
+ app.cached = false;
app.adjType = "previous";
}
@@ -13123,6 +13576,7 @@
// infinite recursion.
app.adjSeq = mAdjSeq;
app.curRawAdj = app.nonStoppingAdj = adj;
+ app.hasStartedServices = false;
if (mBackupTarget != null && app == mBackupTarget.app) {
// If possible we want to avoid killing apps while they're being backed up
@@ -13130,7 +13584,7 @@
if (DEBUG_BACKUP) Slog.v(TAG, "oom BACKUP_APP_ADJ for " + app);
adj = ProcessList.BACKUP_APP_ADJ;
app.adjType = "backup";
- app.hidden = false;
+ app.cached = false;
}
}
@@ -13143,13 +13597,14 @@
while (jt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) {
ServiceRecord s = jt.next();
if (s.startRequested) {
+ app.hasStartedServices = true;
if (app.hasShownUi && app != mHomeProcess) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
// UI stuff. We'll tag it with a label just to help
// debug and understand what is going on.
if (adj > ProcessList.SERVICE_ADJ) {
- app.adjType = "started-bg-ui-services";
+ app.adjType = "cch-started-ui-services";
}
} else {
if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {
@@ -13159,173 +13614,174 @@
if (adj > ProcessList.SERVICE_ADJ) {
adj = ProcessList.SERVICE_ADJ;
app.adjType = "started-services";
- app.hidden = false;
+ app.cached = false;
}
}
// If we have let the service slide into the background
// state, still have some text describing what it is doing
// even though the service no longer has an impact.
if (adj > ProcessList.SERVICE_ADJ) {
- app.adjType = "started-bg-services";
+ app.adjType = "cch-started-services";
}
}
// Don't kill this process because it is doing work; it
// has said it is doing work.
app.keeping = true;
}
- if (s.connections.size() > 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- Iterator<ArrayList<ConnectionRecord>> kt
- = s.connections.values().iterator();
- while (kt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) {
- ArrayList<ConnectionRecord> clist = kt.next();
- for (int i=0; i<clist.size() && adj > ProcessList.FOREGROUND_APP_ADJ; i++) {
- // XXX should compute this based on the max of
- // all connected clients.
- ConnectionRecord cr = clist.get(i);
- if (cr.binding.client == app) {
- // Binding to ourself is not interesting.
- continue;
+ for (int conni = s.connections.size()-1;
+ conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+ || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
+ conni--) {
+ ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
+ for (int i = 0;
+ i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
+ || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
+ i++) {
+ // XXX should compute this based on the max of
+ // all connected clients.
+ ConnectionRecord cr = clist.get(i);
+ if (cr.binding.client == app) {
+ // Binding to ourself is not interesting.
+ continue;
+ }
+ if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
+ ProcessRecord client = cr.binding.client;
+ int clientAdj = adj;
+ int myCachedAdj = cachedAdj;
+ if (myCachedAdj > client.cachedAdj) {
+ if (client.cachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
+ myCachedAdj = client.cachedAdj;
+ } else {
+ myCachedAdj = ProcessList.VISIBLE_APP_ADJ;
+ }
}
- if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
- ProcessRecord client = cr.binding.client;
- int clientAdj = adj;
- int myHiddenAdj = hiddenAdj;
- if (myHiddenAdj > client.hiddenAdj) {
- if (client.hiddenAdj >= ProcessList.VISIBLE_APP_ADJ) {
- myHiddenAdj = client.hiddenAdj;
- } else {
- myHiddenAdj = ProcessList.VISIBLE_APP_ADJ;
- }
+ int myClientCachedAdj = clientCachedAdj;
+ if (myClientCachedAdj > client.clientCachedAdj) {
+ if (client.clientCachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
+ myClientCachedAdj = client.clientCachedAdj;
+ } else {
+ myClientCachedAdj = ProcessList.VISIBLE_APP_ADJ;
}
- int myClientHiddenAdj = clientHiddenAdj;
- if (myClientHiddenAdj > client.clientHiddenAdj) {
- if (client.clientHiddenAdj >= ProcessList.VISIBLE_APP_ADJ) {
- myClientHiddenAdj = client.clientHiddenAdj;
- } else {
- myClientHiddenAdj = ProcessList.VISIBLE_APP_ADJ;
- }
+ }
+ int myEmptyAdj = emptyAdj;
+ if (myEmptyAdj > client.emptyAdj) {
+ if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) {
+ myEmptyAdj = client.emptyAdj;
+ } else {
+ myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
}
- int myEmptyAdj = emptyAdj;
- if (myEmptyAdj > client.emptyAdj) {
- if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) {
- myEmptyAdj = client.emptyAdj;
- } else {
- myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
+ }
+ clientAdj = computeOomAdjLocked(client, myCachedAdj,
+ myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
+ String adjType = null;
+ if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+ // Not doing bind OOM management, so treat
+ // this guy more like a started service.
+ if (app.hasShownUi && app != mHomeProcess) {
+ // If this process has shown some UI, let it immediately
+ // go to the LRU list because it may be pretty heavy with
+ // UI stuff. We'll tag it with a label just to help
+ // debug and understand what is going on.
+ if (adj > clientAdj) {
+ adjType = "cch-bound-ui-services";
}
- }
- clientAdj = computeOomAdjLocked(client, myHiddenAdj,
- myClientHiddenAdj, myEmptyAdj, TOP_APP, true, doingAll);
- String adjType = null;
- if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
- // Not doing bind OOM management, so treat
- // this guy more like a started service.
- if (app.hasShownUi && app != mHomeProcess) {
- // If this process has shown some UI, let it immediately
- // go to the LRU list because it may be pretty heavy with
- // UI stuff. We'll tag it with a label just to help
- // debug and understand what is going on.
+ app.cached = false;
+ clientAdj = adj;
+ } else {
+ if (now >= (s.lastActivity
+ + ActiveServices.MAX_SERVICE_INACTIVITY)) {
+ // This service has not seen activity within
+ // recent memory, so allow it to drop to the
+ // LRU list if there is no other reason to keep
+ // it around. We'll also tag it with a label just
+ // to help debug and undertand what is going on.
if (adj > clientAdj) {
- adjType = "bound-bg-ui-services";
+ adjType = "cch-bound-services";
}
- app.hidden = false;
clientAdj = adj;
- } else {
- if (now >= (s.lastActivity
- + ActiveServices.MAX_SERVICE_INACTIVITY)) {
- // This service has not seen activity within
- // recent memory, so allow it to drop to the
- // LRU list if there is no other reason to keep
- // it around. We'll also tag it with a label just
- // to help debug and undertand what is going on.
- if (adj > clientAdj) {
- adjType = "bound-bg-services";
- }
- clientAdj = adj;
- }
- }
- } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
- if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) {
- // If this connection is keeping the service
- // created, then we want to try to better follow
- // its memory management semantics for activities.
- // That is, if it is sitting in the background
- // LRU list as a hidden process (with activities),
- // we don't want the service it is connected to
- // to go into the empty LRU and quickly get killed,
- // because I'll we'll do is just end up restarting
- // the service.
- app.hasClientActivities |= client.hasActivities;
}
}
- if (adj > clientAdj) {
- // If this process has recently shown UI, and
- // the process that is binding to it is less
- // important than being visible, then we don't
- // care about the binding as much as we care
- // about letting this process get into the LRU
- // list to be killed and restarted if needed for
- // memory.
- if (app.hasShownUi && app != mHomeProcess
- && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
- adjType = "bound-bg-ui-services";
- } else {
- if ((cr.flags&(Context.BIND_ABOVE_CLIENT
- |Context.BIND_IMPORTANT)) != 0) {
- adj = clientAdj;
- } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
- && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
- && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
- adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
- adj = clientAdj;
- } else {
- app.pendingUiClean = true;
- if (adj > ProcessList.VISIBLE_APP_ADJ) {
- adj = ProcessList.VISIBLE_APP_ADJ;
- }
- }
- if (!client.hidden) {
- app.hidden = false;
- }
- if (client.keeping) {
- app.keeping = true;
- }
- adjType = "service";
- }
- }
- if (adjType != null) {
- app.adjType = adjType;
- app.adjTypeCode = ActivityManager.RunningAppProcessInfo
- .REASON_SERVICE_IN_USE;
- app.adjSource = cr.binding.client;
- app.adjSourceOom = clientAdj;
- app.adjTarget = s.name;
- }
- if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
- if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- }
+ } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+ if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) {
+ // If this connection is keeping the service
+ // created, then we want to try to better follow
+ // its memory management semantics for activities.
+ // That is, if it is sitting in the background
+ // LRU list as a cached process (with activities),
+ // we don't want the service it is connected to
+ // to go into the empty LRU and quickly get killed,
+ // because I'll we'll do is just end up restarting
+ // the service.
+ app.hasClientActivities |= client.hasActivities;
}
}
- final ActivityRecord a = cr.activity;
- if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
- if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&
- (a.visible || a.state == ActivityState.RESUMED
- || a.state == ActivityState.PAUSING)) {
- adj = ProcessList.FOREGROUND_APP_ADJ;
- if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ if (adj > clientAdj) {
+ // If this process has recently shown UI, and
+ // the process that is binding to it is less
+ // important than being visible, then we don't
+ // care about the binding as much as we care
+ // about letting this process get into the LRU
+ // list to be killed and restarted if needed for
+ // memory.
+ if (app.hasShownUi && app != mHomeProcess
+ && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ adjType = "cch-bound-ui-services";
+ } else {
+ if ((cr.flags&(Context.BIND_ABOVE_CLIENT
+ |Context.BIND_IMPORTANT)) != 0) {
+ adj = clientAdj;
+ } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
+ && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+ && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+ } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
+ adj = clientAdj;
+ } else {
+ app.pendingUiClean = true;
+ if (adj > ProcessList.VISIBLE_APP_ADJ) {
+ adj = ProcessList.VISIBLE_APP_ADJ;
+ }
}
- app.hidden = false;
- app.adjType = "service";
- app.adjTypeCode = ActivityManager.RunningAppProcessInfo
- .REASON_SERVICE_IN_USE;
- app.adjSource = a;
- app.adjSourceOom = adj;
- app.adjTarget = s.name;
+ if (!client.cached) {
+ app.cached = false;
+ }
+ if (client.keeping) {
+ app.keeping = true;
+ }
+ adjType = "service";
}
}
+ if (adjType != null) {
+ app.adjType = adjType;
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE;
+ app.adjSource = cr.binding.client;
+ app.adjSourceOom = clientAdj;
+ app.adjTarget = s.name;
+ }
+ if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+ if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ }
+ }
+ }
+ final ActivityRecord a = cr.activity;
+ if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+ if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&
+ (a.visible || a.state == ActivityState.RESUMED
+ || a.state == ActivityState.PAUSING)) {
+ adj = ProcessList.FOREGROUND_APP_ADJ;
+ if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ }
+ app.cached = false;
+ app.adjType = "service";
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE;
+ app.adjSource = a;
+ app.adjSourceOom = adj;
+ app.adjTarget = s.name;
+ }
}
}
}
@@ -13336,93 +13792,91 @@
// application from running. By default we put the process in
// with the rest of the background processes; as we scan through
// its services we may bump it up from there.
- if (adj > hiddenAdj) {
- adj = hiddenAdj;
- app.hidden = false;
- app.adjType = "bg-services";
+ if (adj > cachedAdj) {
+ adj = cachedAdj;
+ app.cached = false;
+ app.adjType = "cch-services";
}
}
- if (app.pubProviders.size() != 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- Iterator<ContentProviderRecord> jt = app.pubProviders.values().iterator();
- while (jt.hasNext() && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- ContentProviderRecord cpr = jt.next();
- for (int i = cpr.connections.size()-1;
- i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
- i--) {
- ContentProviderConnection conn = cpr.connections.get(i);
- ProcessRecord client = conn.client;
- if (client == app) {
- // Being our own client is not interesting.
- continue;
- }
- int myHiddenAdj = hiddenAdj;
- if (myHiddenAdj > client.hiddenAdj) {
- if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) {
- myHiddenAdj = client.hiddenAdj;
- } else {
- myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
- }
- }
- int myClientHiddenAdj = clientHiddenAdj;
- if (myClientHiddenAdj > client.clientHiddenAdj) {
- if (client.clientHiddenAdj >= ProcessList.FOREGROUND_APP_ADJ) {
- myClientHiddenAdj = client.clientHiddenAdj;
- } else {
- myClientHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
- }
- }
- int myEmptyAdj = emptyAdj;
- if (myEmptyAdj > client.emptyAdj) {
- if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) {
- myEmptyAdj = client.emptyAdj;
- } else {
- myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ;
- }
- }
- int clientAdj = computeOomAdjLocked(client, myHiddenAdj,
- myClientHiddenAdj, myEmptyAdj, TOP_APP, true, doingAll);
- if (adj > clientAdj) {
- if (app.hasShownUi && app != mHomeProcess
- && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
- app.adjType = "bg-ui-provider";
- } else {
- adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
- ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
- app.adjType = "provider";
- }
- if (!client.hidden) {
- app.hidden = false;
- }
- if (client.keeping) {
- app.keeping = true;
- }
- app.adjTypeCode = ActivityManager.RunningAppProcessInfo
- .REASON_PROVIDER_IN_USE;
- app.adjSource = client;
- app.adjSourceOom = clientAdj;
- app.adjTarget = cpr.name;
- }
- if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ for (int provi = app.pubProviders.size()-1;
+ provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+ || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
+ provi--) {
+ ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
+ for (int i = cpr.connections.size()-1;
+ i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+ || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
+ i--) {
+ ContentProviderConnection conn = cpr.connections.get(i);
+ ProcessRecord client = conn.client;
+ if (client == app) {
+ // Being our own client is not interesting.
+ continue;
+ }
+ int myCachedAdj = cachedAdj;
+ if (myCachedAdj > client.cachedAdj) {
+ if (client.cachedAdj > ProcessList.FOREGROUND_APP_ADJ) {
+ myCachedAdj = client.cachedAdj;
+ } else {
+ myCachedAdj = ProcessList.FOREGROUND_APP_ADJ;
}
}
- // If the provider has external (non-framework) process
- // dependencies, ensure that its adjustment is at least
- // FOREGROUND_APP_ADJ.
- if (cpr.hasExternalProcessHandles()) {
- if (adj > ProcessList.FOREGROUND_APP_ADJ) {
- adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- app.hidden = false;
- app.keeping = true;
- app.adjType = "provider";
- app.adjTarget = cpr.name;
+ int myClientCachedAdj = clientCachedAdj;
+ if (myClientCachedAdj > client.clientCachedAdj) {
+ if (client.clientCachedAdj >= ProcessList.FOREGROUND_APP_ADJ) {
+ myClientCachedAdj = client.clientCachedAdj;
+ } else {
+ myClientCachedAdj = ProcessList.FOREGROUND_APP_ADJ;
}
}
+ int myEmptyAdj = emptyAdj;
+ if (myEmptyAdj > client.emptyAdj) {
+ if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) {
+ myEmptyAdj = client.emptyAdj;
+ } else {
+ myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ;
+ }
+ }
+ int clientAdj = computeOomAdjLocked(client, myCachedAdj,
+ myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
+ if (adj > clientAdj) {
+ if (app.hasShownUi && app != mHomeProcess
+ && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ app.adjType = "cch-ui-provider";
+ } else {
+ adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
+ ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
+ app.adjType = "provider";
+ }
+ if (!client.cached) {
+ app.cached = false;
+ }
+ if (client.keeping) {
+ app.keeping = true;
+ }
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_PROVIDER_IN_USE;
+ app.adjSource = client;
+ app.adjSourceOom = clientAdj;
+ app.adjTarget = cpr.name;
+ }
+ if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ }
+ }
+ // If the provider has external (non-framework) process
+ // dependencies, ensure that its adjustment is at least
+ // FOREGROUND_APP_ADJ.
+ if (cpr.hasExternalProcessHandles()) {
+ if (adj > ProcessList.FOREGROUND_APP_ADJ) {
+ adj = ProcessList.FOREGROUND_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.cached = false;
+ app.keeping = true;
+ app.adjType = "provider";
+ app.adjTarget = cpr.name;
+ }
}
}
@@ -13458,7 +13912,7 @@
schedGroup = Process.THREAD_GROUP_DEFAULT;
}
}
- if (adj < ProcessList.HIDDEN_APP_MIN_ADJ) {
+ if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
app.keeping = true;
}
@@ -13474,9 +13928,9 @@
adj = ProcessList.VISIBLE_APP_ADJ;
} else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- } else if (adj < ProcessList.HIDDEN_APP_MIN_ADJ) {
- adj = ProcessList.HIDDEN_APP_MIN_ADJ;
- } else if (adj < ProcessList.HIDDEN_APP_MAX_ADJ) {
+ } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
+ adj = ProcessList.CACHED_APP_MIN_ADJ;
+ } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
adj++;
}
}
@@ -13490,7 +13944,7 @@
// interesting in this process then we will push it to the
// background importance.
importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
- } else if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+ } else if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
} else if (adj >= ProcessList.SERVICE_B_ADJ) {
importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
@@ -13565,6 +14019,41 @@
}
/**
+ * Schedule PSS collection of a process.
+ */
+ void requestPssLocked(ProcessRecord proc, long now, boolean always) {
+ if (!always && now < (proc.lastPssTime+PSS_MIN_INTERVAL)) {
+ return;
+ }
+ if (mPendingPssProcesses.contains(proc)) {
+ return;
+ }
+ proc.lastPssTime = now;
+ if (mPendingPssProcesses.size() == 0) {
+ mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+ }
+ mPendingPssProcesses.add(proc);
+ }
+
+ /**
+ * Schedule PSS collection of all processes.
+ */
+ void requestPssAllProcsLocked(long now, boolean always) {
+ if (!always && now < (mLastFullPssTime+FULL_PSS_MIN_INTERVAL)) {
+ return;
+ }
+ mLastFullPssTime = now;
+ mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
+ mPendingPssProcesses.clear();
+ for (int i=mLruProcesses.size()-1; i>=0; i--) {
+ ProcessRecord app = mLruProcesses.get(i);
+ app.lastPssTime = now;
+ mPendingPssProcesses.add(app);
+ }
+ mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+ }
+
+ /**
* Ask a given process to GC right now.
*/
final void performAppGcLocked(ProcessRecord app) {
@@ -13594,8 +14083,7 @@
}
}
return !processingBroadcasts
- && (mSleeping || (mMainStack.mResumedActivity != null &&
- mMainStack.mResumedActivity.idle));
+ && (mSleeping || mStackSupervisor.allResumedActivitiesIdle());
}
/**
@@ -13776,6 +14264,7 @@
+ " during " + realtimeSince);
EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
app.processName, app.setAdj, "excessive wake lock");
+ app.baseProcessTracker.reportExcessiveWake(app.pkgList);
Process.killProcessQuiet(app.pid);
} else if (doCpuKills && uptimeSince > 0
&& ((cputimeUsed*100)/uptimeSince) >= 50) {
@@ -13788,6 +14277,7 @@
+ " during " + uptimeSince);
EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
app.processName, app.setAdj, "excessive cpu");
+ app.baseProcessTracker.reportExcessiveCpu(app.pkgList);
Process.killProcessQuiet(app.pid);
} else {
app.lastWakeTime = wtime;
@@ -13797,10 +14287,10 @@
}
}
- private final boolean updateOomAdjLocked(ProcessRecord app, int hiddenAdj,
- int clientHiddenAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) {
- app.hiddenAdj = hiddenAdj;
- app.clientHiddenAdj = clientHiddenAdj;
+ private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
+ int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll, long now) {
+ app.cachedAdj = cachedAdj;
+ app.clientCachedAdj = clientCachedAdj;
app.emptyAdj = emptyAdj;
if (app.thread == null) {
@@ -13811,7 +14301,7 @@
boolean success = true;
- computeOomAdjLocked(app, hiddenAdj, clientHiddenAdj, emptyAdj, TOP_APP, false, doingAll);
+ computeOomAdjLocked(app, cachedAdj, clientCachedAdj, emptyAdj, TOP_APP, false, doingAll);
if (app.curRawAdj != app.setRawAdj) {
if (wasKeeping && !app.keeping) {
@@ -13829,12 +14319,38 @@
app.setRawAdj = app.curRawAdj;
}
+ if (!mSleeping) {
+ if (app == TOP_APP && now > (app.lastPssTime+PSS_TOP_INTERVAL)) {
+ // For the current top application we will very aggressively collect
+ // PSS data to have a good measure of memory use while in the foreground.
+ requestPssLocked(app, now, true);
+ } else if (app.curAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
+ && now > (app.lastPssTime+PSS_TOP_INTERVAL)) {
+ // For any unkillable processes, we will more regularly collect their PSS
+ // since they have a significant impact on the memory state of the device.
+ requestPssLocked(app, now, true);
+ }
+ }
+
if (app.curAdj != app.setAdj) {
if (Process.setOomAdj(app.pid, app.curAdj)) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
TAG, "Set " + app.pid + " " + app.processName +
" adj " + app.curAdj + ": " + app.adjType);
+ if (app.setAdj == ProcessList.SERVICE_ADJ
+ && app.curAdj == ProcessList.SERVICE_B_ADJ) {
+ // If a service is dropping to the B list, it has been running for
+ // a while, take a PSS snapshot.
+ requestPssLocked(app, now, false);
+ } else if (now > (app.lastPssTime+PSS_MAX_INTERVAL)) {
+ requestPssLocked(app, now, true);
+ }
app.setAdj = app.curAdj;
+ app.setAdjChanged = true;
+ if (!doingAll) {
+ app.setProcessTrackerState(TOP_APP, mProcessTracker.getMemFactorLocked(),
+ now, mProcessList);
+ }
} else {
success = false;
Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);
@@ -13879,31 +14395,24 @@
}
private final ActivityRecord resumedAppLocked() {
- ActivityRecord resumedActivity = mMainStack.mResumedActivity;
- if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = mMainStack.mPausingActivity;
- if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = mMainStack.topRunningActivityLocked(null);
- }
- }
- return resumedActivity;
+ return mStackSupervisor.resumedAppLocked();
}
final boolean updateOomAdjLocked(ProcessRecord app) {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
int curAdj = app.curAdj;
- final boolean wasHidden = curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
- && curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ;
+ final boolean wasCached = curAdj >= ProcessList.CACHED_APP_MIN_ADJ
+ && curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
mAdjSeq++;
- boolean success = updateOomAdjLocked(app, app.hiddenAdj, app.clientHiddenAdj,
- app.emptyAdj, TOP_APP, false);
- final boolean nowHidden = app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
- && app.curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ;
- if (nowHidden != wasHidden) {
- // Changed to/from hidden state, so apps after it in the LRU
+ boolean success = updateOomAdjLocked(app, app.cachedAdj, app.clientCachedAdj,
+ app.emptyAdj, TOP_APP, false, SystemClock.uptimeMillis());
+ final boolean nowCached = app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
+ && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
+ if (nowCached != wasCached) {
+ // Changed to/from cached state, so apps after it in the LRU
// list may also be changed.
updateOomAdjLocked();
}
@@ -13913,7 +14422,8 @@
final void updateOomAdjLocked() {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
- final long oldTime = SystemClock.uptimeMillis() - ProcessList.MAX_EMPTY_TIME;
+ final long now = SystemClock.uptimeMillis();
+ final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
if (false) {
RuntimeException e = new RuntimeException();
@@ -13925,100 +14435,104 @@
mNewNumServiceProcs = 0;
final int emptyProcessLimit;
- final int hiddenProcessLimit;
+ final int cachedProcessLimit;
if (mProcessLimit <= 0) {
- emptyProcessLimit = hiddenProcessLimit = 0;
+ emptyProcessLimit = cachedProcessLimit = 0;
} else if (mProcessLimit == 1) {
emptyProcessLimit = 1;
- hiddenProcessLimit = 0;
+ cachedProcessLimit = 0;
} else {
- emptyProcessLimit = (mProcessLimit*2)/3;
- hiddenProcessLimit = mProcessLimit - emptyProcessLimit;
+ emptyProcessLimit = ProcessList.computeEmptyProcessLimit(mProcessLimit);
+ cachedProcessLimit = mProcessLimit - emptyProcessLimit;
}
// Let's determine how many processes we have running vs.
// how many slots we have for background processes; we may want
// to put multiple processes in a slot of there are enough of
// them.
- int numSlots = (ProcessList.HIDDEN_APP_MAX_ADJ
- - ProcessList.HIDDEN_APP_MIN_ADJ + 1) / 2;
- int numEmptyProcs = mLruProcesses.size()-mNumNonHiddenProcs-mNumHiddenProcs;
- if (numEmptyProcs > hiddenProcessLimit) {
- // If there are more empty processes than our limit on hidden
- // processes, then use the hidden process limit for the factor.
+ int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
+ - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
+ int numEmptyProcs = mLruProcesses.size()- mNumNonCachedProcs - mNumCachedHiddenProcs;
+ if (numEmptyProcs > cachedProcessLimit) {
+ // If there are more empty processes than our limit on cached
+ // processes, then use the cached process limit for the factor.
// This ensures that the really old empty processes get pushed
// down to the bottom, so if we are running low on memory we will
- // have a better chance at keeping around more hidden processes
+ // have a better chance at keeping around more cached processes
// instead of a gazillion empty processes.
- numEmptyProcs = hiddenProcessLimit;
+ numEmptyProcs = cachedProcessLimit;
}
int emptyFactor = numEmptyProcs/numSlots;
if (emptyFactor < 1) emptyFactor = 1;
- int hiddenFactor = (mNumHiddenProcs > 0 ? mNumHiddenProcs : 1)/numSlots;
- if (hiddenFactor < 1) hiddenFactor = 1;
- int stepHidden = 0;
+ int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots;
+ if (cachedFactor < 1) cachedFactor = 1;
+ int stepCached = 0;
int stepEmpty = 0;
- int numHidden = 0;
+ int numCached = 0;
int numEmpty = 0;
int numTrimming = 0;
- mNumNonHiddenProcs = 0;
- mNumHiddenProcs = 0;
+ mNumNonCachedProcs = 0;
+ mNumCachedHiddenProcs = 0;
// First update the OOM adjustment for each of the
// application processes based on their current state.
int i = mLruProcesses.size();
- int curHiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
- int nextHiddenAdj = curHiddenAdj+1;
- int curEmptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+ int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
+ int nextCachedAdj = curCachedAdj+1;
+ int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextEmptyAdj = curEmptyAdj+2;
- int curClientHiddenAdj = curEmptyAdj;
+ int curClientCachedAdj = curEmptyAdj;
+ boolean changed = false;
while (i > 0) {
i--;
ProcessRecord app = mLruProcesses.get(i);
- //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj);
- updateOomAdjLocked(app, curHiddenAdj, curClientHiddenAdj, curEmptyAdj, TOP_APP, true);
+ //Slog.i(TAG, "OOM " + app + ": cur cached=" + curCachedAdj);
+ app.setAdjChanged = false;
+ updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP,
+ true, now);
+ changed |= app.setAdjChanged;
if (!app.killedBackground) {
- if (app.curRawAdj == curHiddenAdj && app.hasActivities) {
- // This process was assigned as a hidden process... step the
- // hidden level.
- mNumHiddenProcs++;
- if (curHiddenAdj != nextHiddenAdj) {
- stepHidden++;
- if (stepHidden >= hiddenFactor) {
- stepHidden = 0;
- curHiddenAdj = nextHiddenAdj;
- nextHiddenAdj += 2;
- if (nextHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
- nextHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+ if (app.curRawAdj == curCachedAdj && app.hasActivities) {
+ // This process was assigned as a cached process... step the
+ // cached level.
+ mNumCachedHiddenProcs++;
+ if (curCachedAdj != nextCachedAdj) {
+ stepCached++;
+ if (stepCached >= cachedFactor) {
+ stepCached = 0;
+ curCachedAdj = nextCachedAdj;
+ nextCachedAdj += 2;
+ if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+ nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
- if (curClientHiddenAdj <= curHiddenAdj) {
- curClientHiddenAdj = curHiddenAdj + 1;
- if (curClientHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
- curClientHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+ if (curClientCachedAdj <= curCachedAdj) {
+ curClientCachedAdj = curCachedAdj + 1;
+ if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+ curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
}
- numHidden++;
- if (numHidden > hiddenProcessLimit) {
+ numCached++;
+ if (numCached > cachedProcessLimit) {
Slog.i(TAG, "No longer want " + app.processName
- + " (pid " + app.pid + "): hidden #" + numHidden);
+ + " (pid " + app.pid + "): cached #" + numCached);
EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
app.processName, app.setAdj, "too many background");
app.killedBackground = true;
Process.killProcessQuiet(app.pid);
}
- } else if (app.curRawAdj == curHiddenAdj && app.hasClientActivities) {
+ } else if (app.curRawAdj == curCachedAdj && app.hasClientActivities) {
// This process has a client that has activities. We will have
- // given it the current hidden adj; here we will just leave it
- // without stepping the hidden adj.
- curClientHiddenAdj++;
- if (curClientHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
- curClientHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+ // given it the current cached adj; here we will just leave it
+ // without stepping the cached adj.
+ curClientCachedAdj++;
+ if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+ curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
} else {
- if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curHiddenAdj) {
+ if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curCachedAdj) {
// This process was assigned as an empty process... step the
// empty level.
if (curEmptyAdj != nextEmptyAdj) {
@@ -14027,15 +14541,15 @@
stepEmpty = 0;
curEmptyAdj = nextEmptyAdj;
nextEmptyAdj += 2;
- if (nextEmptyAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
- nextEmptyAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+ if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+ nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
- } else if (app.curRawAdj < ProcessList.HIDDEN_APP_MIN_ADJ) {
- mNumNonHiddenProcs++;
+ } else if (app.curRawAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+ mNumNonCachedProcs++;
}
- if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
+ if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& !app.hasClientActivities) {
if (numEmpty > ProcessList.TRIM_EMPTY_APPS
&& app.lastActivityTime < oldTime) {
@@ -14090,9 +14604,10 @@
// are managing to keep around is less than half the maximum we desire;
// if we are keeping a good number around, we'll let them use whatever
// memory they want.
- if (numHidden <= ProcessList.TRIM_HIDDEN_APPS
+ int memFactor = ProcessTracker.ADJ_MEM_FACTOR_NORMAL;
+ if (numCached <= ProcessList.TRIM_CACHED_APPS
&& numEmpty <= ProcessList.TRIM_EMPTY_APPS) {
- final int numHiddenAndEmpty = numHidden + numEmpty;
+ final int numCachedAndEmpty = numCached + numEmpty;
final int N = mLruProcesses.size();
int factor = numTrimming/3;
int minFactor = 2;
@@ -14101,12 +14616,15 @@
if (factor < minFactor) factor = minFactor;
int step = 0;
int fgTrimLevel;
- if (numHiddenAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
+ if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
- } else if (numHiddenAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
+ memFactor = ProcessTracker.ADJ_MEM_FACTOR_CRITICAL;
+ } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
+ memFactor = ProcessTracker.ADJ_MEM_FACTOR_LOW;
} else {
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
+ memFactor = ProcessTracker.ADJ_MEM_FACTOR_MODERATE;
}
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
for (i=0; i<N; i++) {
@@ -14129,7 +14647,7 @@
// be in a consistent state at this point.
// For these apps we will also finish their activities
// to help them free memory.
- mMainStack.scheduleDestroyActivities(app, false, "trim");
+ mStackSupervisor.scheduleDestroyAllActivities(app, "trim");
}
}
}
@@ -14203,7 +14721,35 @@
if (mAlwaysFinishActivities) {
// Need to do this on its own message because the stack may not
// be in a consistent state at this point.
- mMainStack.scheduleDestroyActivities(null, false, "always-finish");
+ mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");
+ }
+
+ boolean allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now);
+ if (changed || allChanged) {
+ memFactor = mProcessTracker.getMemFactorLocked();
+ for (i=mLruProcesses.size()-1; i>=0; i--) {
+ ProcessRecord app = mLruProcesses.get(i);
+ if (allChanged || app.setAdjChanged) {
+ app.setProcessTrackerState(TOP_APP, memFactor, now, mProcessList);
+ }
+ }
+ }
+ if (allChanged) {
+ requestPssAllProcsLocked(now, false);
+ }
+
+ if (mProcessTracker.shouldWriteNowLocked(now)) {
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ synchronized (ActivityManagerService.this) {
+ mProcessTracker.writeStateAsyncLocked();
+ }
+ }
+ });
+ }
+
+ if (DEBUG_OOM_ADJ) {
+ Slog.d(TAG, "Did OOM ADJ in " + (SystemClock.uptimeMillis()-now) + "ms");
}
}
@@ -14378,7 +14924,7 @@
}
if (proc == null) {
- HashMap<String, SparseArray<ProcessRecord>> all
+ ArrayMap<String, SparseArray<ProcessRecord>> all
= mProcessNames.getMap();
SparseArray<ProcessRecord> procs = all.get(process);
if (procs != null && procs.size() > 0) {
@@ -14503,7 +15049,6 @@
}
mCurrentUserId = userId;
- mCurrentUserArray = new int[] { userId };
final Integer userIdInt = Integer.valueOf(userId);
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
@@ -14569,7 +15114,7 @@
}
}
- boolean haveActivities = mMainStack.switchUserLocked(userId, uss);
+ boolean haveActivities = mStackSupervisor.switchUserLocked(userId, uss);
if (!haveActivities) {
startHomeActivityLocked(userId);
}
@@ -14884,7 +15429,7 @@
// Clean up all state and processes associated with the user.
// Kill all the processes for the user.
- forceStopUserLocked(userId);
+ forceStopUserLocked(userId, "finish user");
}
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 054d213..2ae9ed6 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -16,12 +16,14 @@
package com.android.server.am;
+import android.os.Trace;
+import com.android.internal.R.styleable;
import com.android.internal.app.ResolverActivity;
import com.android.server.AttributeCache;
import com.android.server.am.ActivityStack.ActivityState;
-import android.app.Activity;
import android.app.ActivityOptions;
+import android.app.ResultInfo;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -54,8 +56,10 @@
* An entry in the history stack, representing an activity.
*/
final class ActivityRecord {
+ static final String TAG = ActivityManagerService.TAG;
+ static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
+
final ActivityManagerService service; // owner
- final ActivityStack stack; // owner
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
final int launchedFromUid; // always the uid who started the activity.
@@ -69,22 +73,29 @@
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity
final boolean stateNotNeeded; // As per ActivityInfo.flags
- final boolean fullscreen; // covers the full screen?
+ boolean fullscreen; // covers the full screen?
final boolean noDisplay; // activity is not displayed?
final boolean componentSpecified; // did caller specifiy an explicit component?
- final boolean isHomeActivity; // do we consider this to be a home activity?
+
+ static final int APPLICATION_ACTIVITY_TYPE = 0;
+ static final int HOME_ACTIVITY_TYPE = 1;
+ static final int RECENTS_ACTIVITY_TYPE = 2;
+ final int mActivityType;
+
final String baseDir; // where activity source (resources etc) located
final String resDir; // where public activity source (public resources etc) located
final String dataDir; // where activity data should go
CharSequence nonLocalizedLabel; // the label information from the package mgr.
int labelRes; // the label information from the package mgr.
int icon; // resource identifier of activity's icon.
+ int logo; // resource identifier of activity's logo.
int theme; // resource identifier of activity's theme.
int realTheme; // actual theme resource we will use, never 0.
int windowFlags; // custom window flags for preview window.
TaskRecord task; // the task this is in.
ThumbnailHolder thumbHolder; // where our thumbnails should go.
- long launchTime; // when we starting launching this activity
+ long displayStartTime; // when we started launching this activity
+ long fullyDrawnStartTime; // when we started launching this activity
long startTime; // last time this activity was started
long lastVisibleTime; // last time this activity became visible
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
@@ -95,9 +106,9 @@
ActivityRecord resultTo; // who started this entry, so will get our reply
final String resultWho; // additional identifier for use by resultTo.
final int requestCode; // code given by requester (resultTo)
- ArrayList results; // pending ActivityResult objs we have received
+ ArrayList<ResultInfo> results; // pending ActivityResult objs we have received
HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
- ArrayList newIntents; // any pending new intents for single-top mode
+ ArrayList<Intent> newIntents; // any pending new intents for single-top mode
ActivityOptions pendingOptions; // most recently given options
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
UriPermissionOwner uriPermissions; // current special URI access perms.
@@ -128,15 +139,19 @@
long lastLaunchTime; // time of last lauch of this activity
String stringName; // for caching of toString().
-
+
private boolean inHistory; // are we in the history stack?
+ final ActivityStackSupervisor mStackSupervisor;
+
+ /** Launch the home activity rather than the activity at the top of stack */
+ boolean mLaunchHomeTaskNext;
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
- pw.print(" launchedFromPackage="); pw.println(launchedFromPackage);
+ pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
pw.print(" userId="); pw.println(userId);
pw.print(prefix); pw.print("app="); pw.println(app);
pw.print(prefix); pw.println(intent.toInsecureStringWithClip());
@@ -152,7 +167,7 @@
pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
pw.print(" componentSpecified="); pw.print(componentSpecified);
- pw.print(" isHomeActivity="); pw.println(isHomeActivity);
+ pw.print(" mActivityType="); pw.println(mActivityType);
pw.print(prefix); pw.print("compat="); pw.print(compat);
pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes));
pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
@@ -182,7 +197,7 @@
if (newIntents != null && newIntents.size() > 0) {
pw.print(prefix); pw.println("Pending New Intents:");
for (int i=0; i<newIntents.size(); i++) {
- Intent intent = (Intent)newIntents.get(i);
+ Intent intent = newIntents.get(i);
pw.print(prefix); pw.print(" - ");
if (intent == null) {
pw.println("null");
@@ -210,7 +225,7 @@
if (lastLaunchTime == 0) pw.print("0");
else TimeUtils.formatDuration(lastLaunchTime, now, pw);
pw.println();
- pw.print(prefix); pw.print(" haveState="); pw.print(haveState);
+ pw.print(prefix); pw.print("haveState="); pw.print(haveState);
pw.print(" icicle="); pw.println(icicle);
pw.print(prefix); pw.print("state="); pw.print(state);
pw.print(" stopped="); pw.print(stopped);
@@ -235,10 +250,10 @@
pw.print(" desc="); pw.print(thumbHolder.lastDescription);
}
pw.println();
- if (launchTime != 0 || startTime != 0) {
- pw.print(prefix); pw.print("launchTime=");
- if (launchTime == 0) pw.print("0");
- else TimeUtils.formatDuration(launchTime, now, pw);
+ if (displayStartTime != 0 || startTime != 0) {
+ pw.print(prefix); pw.print("displayStartTime=");
+ if (displayStartTime == 0) pw.print("0");
+ else TimeUtils.formatDuration(displayStartTime, now, pw);
pw.print(" startTime=");
if (startTime == 0) pw.print("0");
else TimeUtils.formatDuration(startTime, now, pw);
@@ -269,36 +284,33 @@
weakActivity = new WeakReference<ActivityRecord>(activity);
}
- @Override public void windowsDrawn() throws RemoteException {
+ @Override public void windowsDrawn() {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
activity.windowsDrawn();
}
}
- @Override public void windowsVisible() throws RemoteException {
+ @Override public void windowsVisible() {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
activity.windowsVisible();
}
}
- @Override public void windowsGone() throws RemoteException {
+ @Override public void windowsGone() {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
activity.windowsGone();
}
}
- @Override public boolean keyDispatchingTimedOut() throws RemoteException {
+ @Override public boolean keyDispatchingTimedOut() {
ActivityRecord activity = weakActivity.get();
- if (activity != null) {
- return activity.keyDispatchingTimedOut();
- }
- return false;
+ return activity != null && activity.keyDispatchingTimedOut();
}
- @Override public long getKeyDispatchingTimeout() throws RemoteException {
+ @Override public long getKeyDispatchingTimeout() {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
return activity.getKeyDispatchingTimeout();
@@ -306,6 +318,7 @@
return 0;
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("Token{");
@@ -326,13 +339,12 @@
}
}
- ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller,
+ ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
ActivityRecord _resultTo, String _resultWho, int _reqCode,
- boolean _componentSpecified) {
+ boolean _componentSpecified, ActivityStackSupervisor supervisor) {
service = _service;
- stack = _stack;
appToken = new Token(this);
info = aInfo;
launchedFromUid = _launchedFromUid;
@@ -361,6 +373,7 @@
thumbnailNeeded = false;
idle = false;
hasBeenLaunched = false;
+ mStackSupervisor = supervisor;
// This starts out true, since the initial state of an activity
// is that we have everything, and we shouldn't never consider it
@@ -390,6 +403,7 @@
labelRes = app.labelRes;
}
icon = aInfo.getIconResource();
+ logo = aInfo.getLogoResource();
theme = aInfo.getThemeResource();
realTheme = theme;
if (realTheme == 0) {
@@ -413,10 +427,10 @@
if (intent != null && (aInfo.flags & ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS) != 0) {
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
-
+
packageName = aInfo.applicationInfo.packageName;
launchMode = aInfo.launchMode;
-
+
AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
realTheme, com.android.internal.R.styleable.Window);
fullscreen = ent != null && !ent.array.getBoolean(
@@ -425,29 +439,28 @@
com.android.internal.R.styleable.Window_windowIsTranslucent, false);
noDisplay = ent != null && ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
-
- if (!_componentSpecified || _launchedFromUid == Process.myUid()
- || _launchedFromUid == 0) {
- // If we know the system has determined the component, then
- // we can consider this to be a home activity...
- if (Intent.ACTION_MAIN.equals(_intent.getAction()) &&
- _intent.hasCategory(Intent.CATEGORY_HOME) &&
- _intent.getCategories().size() == 1 &&
- _intent.getData() == null &&
- _intent.getType() == null &&
- (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- !ResolverActivity.class.getName().equals(realActivity.getClassName())) {
- // This sure looks like a home activity!
- // Note the last check is so we don't count the resolver
- // activity as being home... really, we don't care about
- // doing anything special with something that comes from
- // the core framework package.
- isHomeActivity = true;
- } else {
- isHomeActivity = false;
- }
+
+ // If we know the system has determined the component, then
+ // we can consider this to be a home activity...
+ // Note the last check is so we don't count the resolver
+ // activity as being home... really, we don't care about
+ // doing anything special with something that comes from
+ // the core framework package.
+ if ((!_componentSpecified || _launchedFromUid == Process.myUid()
+ || _launchedFromUid == 0) &&
+ Intent.ACTION_MAIN.equals(_intent.getAction()) &&
+ _intent.hasCategory(Intent.CATEGORY_HOME) &&
+ _intent.getCategories().size() == 1 &&
+ _intent.getData() == null &&
+ _intent.getType() == null &&
+ (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
+ !ResolverActivity.class.getName().equals(realActivity.getClassName())) {
+ // This sure looks like a home activity!
+ mActivityType = HOME_ACTIVITY_TYPE;
+ } else if (realActivity.getClassName().contains("com.android.systemui.recent")) {
+ mActivityType = RECENTS_ACTIVITY_TYPE;
} else {
- isHomeActivity = false;
+ mActivityType = APPLICATION_ACTIVITY_TYPE;
}
immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
@@ -462,12 +475,15 @@
packageName = null;
fullscreen = true;
noDisplay = false;
- isHomeActivity = false;
+ mActivityType = APPLICATION_ACTIVITY_TYPE;
immersive = false;
}
}
void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
+ if (task != null && task.removeActivity(this)) {
+ mStackSupervisor.removeTask(task);
+ }
if (inHistory && !finishing) {
if (task != null) {
task.numActivities--;
@@ -490,6 +506,25 @@
}
}
+ boolean changeWindowTranslucency(boolean toOpaque) {
+ if (fullscreen == toOpaque) {
+ return false;
+ }
+ AttributeCache.Entry ent =
+ AttributeCache.instance().get(packageName, realTheme, styleable.Window);
+ if (ent == null
+ || !ent.array.getBoolean(styleable.Window_windowIsTranslucent, false)
+ || ent.array.getBoolean(styleable.Window_windowIsFloating, false)) {
+ return false;
+ }
+
+ // Keep track of the number of fullscreen activities in this task.
+ task.numFullscreen += toOpaque ? +1 : -1;
+
+ fullscreen = toOpaque;
+ return true;
+ }
+
void putInHistory() {
if (!inHistory) {
inHistory = true;
@@ -504,6 +539,7 @@
inHistory = false;
if (task != null && !finishing) {
task.numActivities--;
+ task = null;
}
clearOptionsLocked();
}
@@ -513,6 +549,18 @@
return inHistory;
}
+ boolean isHomeActivity() {
+ return mActivityType == HOME_ACTIVITY_TYPE;
+ }
+
+ boolean isRecentsActivity() {
+ return mActivityType == RECENTS_ACTIVITY_TYPE;
+ }
+
+ boolean isApplicationActivity() {
+ return mActivityType == APPLICATION_ACTIVITY_TYPE;
+ }
+
void makeFinishing() {
if (!finishing) {
finishing = true;
@@ -525,6 +573,11 @@
}
}
+ boolean isRootActivity() {
+ ArrayList<ActivityRecord> activities = task.mActivities;
+ return activities.size() == 0 || this == task.mActivities.get(0);
+ }
+
UriPermissionOwner getUriPermissionsLocked() {
if (uriPermissions == null) {
uriPermissions = new UriPermissionOwner(service, this);
@@ -538,7 +591,7 @@
ActivityResult r = new ActivityResult(from, resultWho,
requestCode, resultCode, resultData);
if (results == null) {
- results = new ArrayList();
+ results = new ArrayList<ResultInfo>();
}
results.add(r);
}
@@ -563,17 +616,16 @@
void addNewIntentLocked(Intent intent) {
if (newIntents == null) {
- newIntents = new ArrayList();
+ newIntents = new ArrayList<Intent>();
}
newIntents.add(intent);
}
-
+
/**
* Deliver a new Intent to an existing activity, so that its onNewIntent()
* method will be called at the proper time.
*/
final void deliverNewIntentLocked(int callingUid, Intent intent) {
- boolean sent = false;
// The activity now gets access to the data associated with this Intent.
service.grantUriPermissionFromIntentLocked(callingUid, packageName,
intent, getUriPermissionsLocked());
@@ -582,15 +634,16 @@
// device is sleeping, then all activities are stopped, so in that
// case we will deliver it if this is the current top activity on its
// stack.
+ boolean unsent = true;
if ((state == ActivityState.RESUMED || (service.mSleeping
- && stack.topRunningActivityLocked(null) == this))
+ && task.stack.topRunningActivityLocked(null) == this))
&& app != null && app.thread != null) {
try {
ArrayList<Intent> ar = new ArrayList<Intent>();
intent = new Intent(intent);
ar.add(intent);
app.thread.scheduleNewIntent(ar, appToken);
- sent = true;
+ unsent = false;
} catch (RemoteException e) {
Slog.w(ActivityManagerService.TAG,
"Exception thrown sending new intent to " + this, e);
@@ -599,7 +652,7 @@
"Exception thrown sending new intent to " + this, e);
}
}
- if (!sent) {
+ if (unsent) {
addNewIntentLocked(new Intent(intent));
}
}
@@ -701,9 +754,6 @@
}
void updateThumbnail(Bitmap newThumbnail, CharSequence description) {
- if ((intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
- // This is a logical break in the task; it repre
- }
if (thumbHolder != null) {
if (newThumbnail != null) {
if (ActivityManagerService.DEBUG_THUMBNAILS) Slog.i(ActivityManagerService.TAG,
@@ -727,8 +777,8 @@
boolean continueLaunchTickingLocked() {
if (launchTickTime != 0) {
- Message msg = stack.mHandler.obtainMessage(ActivityStack.LAUNCH_TICK_MSG);
- msg.obj = this;
+ final ActivityStack stack = task.stack;
+ Message msg = stack.mHandler.obtainMessage(ActivityStack.LAUNCH_TICK_MSG, this);
stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
stack.mHandler.sendMessageDelayed(msg, ActivityStack.LAUNCH_TICK);
return true;
@@ -738,7 +788,7 @@
void finishLaunchTickingLocked() {
launchTickTime = 0;
- stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
+ task.stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
}
// IApplicationToken
@@ -750,50 +800,91 @@
// so it is best to leave as-is.
return app != null && !app.crashing && !app.notResponding;
}
-
+
public void startFreezingScreenLocked(ProcessRecord app, int configChanges) {
if (mayFreezeScreenLocked(app)) {
service.mWindowManager.startAppFreezingScreen(appToken, configChanges);
}
}
-
+
public void stopFreezingScreenLocked(boolean force) {
if (force || frozenBeforeDestroy) {
frozenBeforeDestroy = false;
service.mWindowManager.stopAppFreezingScreen(appToken, force);
}
}
-
+
+ public void reportFullyDrawnLocked() {
+ final long curTime = SystemClock.uptimeMillis();
+ if (displayStartTime != 0) {
+ reportLaunchTimeLocked(curTime);
+ }
+ if (fullyDrawnStartTime != 0) {
+ final ActivityStack stack = task.stack;
+ final long thisTime = curTime - fullyDrawnStartTime;
+ final long totalTime = stack.mFullyDrawnStartTime != 0
+ ? (curTime - stack.mFullyDrawnStartTime) : thisTime;
+ if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
+ EventLog.writeEvent(EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME,
+ userId, System.identityHashCode(this), shortComponentName,
+ thisTime, totalTime);
+ StringBuilder sb = service.mStringBuilder;
+ sb.setLength(0);
+ sb.append("Fully drawn ");
+ sb.append(shortComponentName);
+ sb.append(": ");
+ TimeUtils.formatDuration(thisTime, sb);
+ if (thisTime != totalTime) {
+ sb.append(" (total ");
+ TimeUtils.formatDuration(totalTime, sb);
+ sb.append(")");
+ }
+ Log.i(ActivityManagerService.TAG, sb.toString());
+ }
+ if (totalTime > 0) {
+ service.mUsageStatsService.noteFullyDrawnTime(realActivity, (int) totalTime);
+ }
+ fullyDrawnStartTime = 0;
+ stack.mFullyDrawnStartTime = 0;
+ }
+ }
+
+ private void reportLaunchTimeLocked(final long curTime) {
+ final ActivityStack stack = task.stack;
+ final long thisTime = curTime - displayStartTime;
+ final long totalTime = stack.mLaunchStartTime != 0
+ ? (curTime - stack.mLaunchStartTime) : thisTime;
+ if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching", 0);
+ EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME,
+ userId, System.identityHashCode(this), shortComponentName,
+ thisTime, totalTime);
+ StringBuilder sb = service.mStringBuilder;
+ sb.setLength(0);
+ sb.append("Displayed ");
+ sb.append(shortComponentName);
+ sb.append(": ");
+ TimeUtils.formatDuration(thisTime, sb);
+ if (thisTime != totalTime) {
+ sb.append(" (total ");
+ TimeUtils.formatDuration(totalTime, sb);
+ sb.append(")");
+ }
+ Log.i(ActivityManagerService.TAG, sb.toString());
+ }
+ mStackSupervisor.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
+ if (totalTime > 0) {
+ service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
+ }
+ displayStartTime = 0;
+ stack.mLaunchStartTime = 0;
+ }
+
public void windowsDrawn() {
synchronized(service) {
- if (launchTime != 0) {
- final long curTime = SystemClock.uptimeMillis();
- final long thisTime = curTime - launchTime;
- final long totalTime = stack.mInitialStartTime != 0
- ? (curTime - stack.mInitialStartTime) : thisTime;
- if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
- EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME,
- userId, System.identityHashCode(this), shortComponentName,
- thisTime, totalTime);
- StringBuilder sb = service.mStringBuilder;
- sb.setLength(0);
- sb.append("Displayed ");
- sb.append(shortComponentName);
- sb.append(": ");
- TimeUtils.formatDuration(thisTime, sb);
- if (thisTime != totalTime) {
- sb.append(" (total ");
- TimeUtils.formatDuration(totalTime, sb);
- sb.append(")");
- }
- Log.i(ActivityManagerService.TAG, sb.toString());
- }
- stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
- if (totalTime > 0) {
- service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
- }
- launchTime = 0;
- stack.mInitialStartTime = 0;
+ if (displayStartTime != 0) {
+ reportLaunchTimeLocked(SystemClock.uptimeMillis());
}
startTime = 0;
finishLaunchTickingLocked();
@@ -802,7 +893,7 @@
public void windowsVisible() {
synchronized(service) {
- stack.reportActivityVisibleLocked(this);
+ mStackSupervisor.reportActivityVisibleLocked(this);
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG, "windowsVisible(): " + this);
if (!nowVisible) {
@@ -812,27 +903,24 @@
// Instead of doing the full stop routine here, let's just
// hide any activities we now can, and let them stop when
// the normal idle happens.
- stack.processStoppingActivitiesLocked(false);
+ mStackSupervisor.processStoppingActivitiesLocked(false);
} else {
// If this activity was already idle, then we now need to
// make sure we perform the full stop of any activities
// that are waiting to do so. This is because we won't
// do that while they are still waiting for this one to
// become visible.
- final int N = stack.mWaitingVisibleActivities.size();
+ final int N = mStackSupervisor.mWaitingVisibleActivities.size();
if (N > 0) {
for (int i=0; i<N; i++) {
- ActivityRecord r = (ActivityRecord)
- stack.mWaitingVisibleActivities.get(i);
+ ActivityRecord r = mStackSupervisor.mWaitingVisibleActivities.get(i);
r.waitingVisible = false;
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG,
"Was waiting for visible: " + r);
}
- stack.mWaitingVisibleActivities.clear();
- Message msg = Message.obtain();
- msg.what = ActivityStack.IDLE_NOW_MSG;
- stack.mHandler.sendMessage(msg);
+ mStackSupervisor.mWaitingVisibleActivities.clear();
+ mStackSupervisor.scheduleIdleLocked();
}
}
service.scheduleAppGcsLocked();
@@ -845,12 +933,13 @@
ActivityManagerService.TAG, "windowsGone(): " + this);
nowVisible = false;
}
-
+
private ActivityRecord getWaitingHistoryRecordLocked() {
// First find the real culprit... if we are waiting
// for another app to start, then we have paused dispatching
// for this activity.
ActivityRecord r = this;
+ final ActivityStack stack = task.stack;
if (r.waitingVisible) {
// Hmmm, who might we be waiting for?
r = stack.mResumedActivity;
@@ -862,7 +951,7 @@
r = this;
}
}
-
+
return r;
}
@@ -875,7 +964,7 @@
}
return service.inputDispatchingTimedOut(anrApp, r, this, false);
}
-
+
/** Returns the key dispatching timeout for this application token. */
public long getKeyDispatchingTimeout() {
synchronized(service) {
@@ -889,7 +978,7 @@
* currently pausing, or is resumed.
*/
public boolean isInterestingToUserLocked() {
- return visible || nowVisible || state == ActivityState.PAUSING ||
+ return visible || nowVisible || state == ActivityState.PAUSING ||
state == ActivityState.RESUMED;
}
@@ -900,20 +989,57 @@
if (app != null && app.thread != null) {
try {
app.thread.scheduleSleeping(appToken, _sleeping);
- if (sleeping && !stack.mGoingToSleepActivities.contains(this)) {
- stack.mGoingToSleepActivities.add(this);
+ if (_sleeping && !mStackSupervisor.mGoingToSleepActivities.contains(this)) {
+ mStackSupervisor.mGoingToSleepActivities.add(this);
}
sleeping = _sleeping;
} catch (RemoteException e) {
- Slog.w(ActivityStack.TAG, "Exception thrown when sleeping: "
- + intent.getComponent(), e);
+ Slog.w(TAG, "Exception thrown when sleeping: " + intent.getComponent(), e);
}
}
}
-
+
+ static void activityResumedLocked(IBinder token) {
+ final ActivityRecord r = ActivityRecord.forToken(token);
+ if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r);
+ r.icicle = null;
+ r.haveState = false;
+ }
+
+ static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
+ final ActivityRecord r = ActivityRecord.forToken(token);
+ if (r == null) {
+ return -1;
+ }
+ final TaskRecord task = r.task;
+ switch (task.mActivities.indexOf(r)) {
+ case -1: return -1;
+ case 0: return task.taskId;
+ default: return onlyRoot ? -1 : task.taskId;
+ }
+ }
+
+ static ActivityRecord isInStackLocked(IBinder token) {
+ final ActivityRecord r = ActivityRecord.forToken(token);
+ if (r != null) {
+ return r.task.stack.isInStackLocked(token);
+ }
+ return null;
+ }
+
+ static ActivityStack getStackLocked(IBinder token) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ return r.task.stack;
+ }
+ return null;
+ }
+
+ @Override
public String toString() {
if (stringName != null) {
- return stringName;
+ return stringName + " t" + (task == null ? -1 : task.taskId) +
+ (finishing ? " f}" : "}");
}
StringBuilder sb = new StringBuilder(128);
sb.append("ActivityRecord{");
@@ -922,7 +1048,7 @@
sb.append(userId);
sb.append(' ');
sb.append(intent.getComponent().flattenToShortString());
- sb.append('}');
- return stringName = sb.toString();
+ stringName = sb.toString();
+ return toString();
}
}
diff --git a/services/java/com/android/server/am/ActivityResult.java b/services/java/com/android/server/am/ActivityResult.java
index 12eba34..6d5bdeb 100644
--- a/services/java/com/android/server/am/ActivityResult.java
+++ b/services/java/com/android/server/am/ActivityResult.java
@@ -23,7 +23,7 @@
/**
* Pending result information to send back to an activity.
*/
-class ActivityResult extends ResultInfo {
+final class ActivityResult extends ResultInfo {
final ActivityRecord mFrom;
public ActivityResult(ActivityRecord from, String resultWho,
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c344023..be03ee3 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -16,33 +16,48 @@
package com.android.server.am;
-import static android.Manifest.permission.START_ANY_ACTIVITY;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityManagerService.localLOGV;
+import static com.android.server.am.ActivityManagerService.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityManagerService.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerService.DEBUG_PAUSE;
+import static com.android.server.am.ActivityManagerService.DEBUG_RESULTS;
+import static com.android.server.am.ActivityManagerService.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerService.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerService.DEBUG_TRANSITION;
+import static com.android.server.am.ActivityManagerService.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityManagerService.DEBUG_VISBILITY;
+import static com.android.server.am.ActivityManagerService.VALIDATE_TOKENS;
-import com.android.internal.app.HeavyWeightSwitcherActivity;
+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_STATES;
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+
+import android.os.Trace;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
+import com.android.internal.util.Objects;
+import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.wm.AppTransition;
+import com.android.server.wm.TaskGroup;
+import com.android.server.wm.WindowManagerService;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppGlobals;
-import android.app.IActivityManager;
-import android.app.IThumbnailRetriever;
-import android.app.IApplicationThread;
-import android.app.PendingIntent;
+import android.app.IActivityController;
+import android.app.IThumbnailReceiver;
import android.app.ResultInfo;
-import android.app.IActivityManager.WaitResult;
+import android.app.ActivityManager.RunningTaskInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.IIntentSender;
import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -54,17 +69,16 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.EventLog;
-import android.util.Log;
import android.util.Slog;
import android.view.Display;
-import java.io.IOException;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
@@ -74,28 +88,6 @@
* State and management of a single stack of activities.
*/
final class ActivityStack {
- static final String TAG = ActivityManagerService.TAG;
- static final boolean localLOGV = ActivityManagerService.localLOGV;
- static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
- static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE;
- static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY;
- static final boolean DEBUG_USER_LEAVING = ActivityManagerService.DEBUG_USER_LEAVING;
- static final boolean DEBUG_TRANSITION = ActivityManagerService.DEBUG_TRANSITION;
- static final boolean DEBUG_RESULTS = ActivityManagerService.DEBUG_RESULTS;
- static final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION;
- static final boolean DEBUG_TASKS = ActivityManagerService.DEBUG_TASKS;
- static final boolean DEBUG_CLEANUP = ActivityManagerService.DEBUG_CLEANUP;
-
- static final boolean DEBUG_STATES = false;
- static final boolean DEBUG_ADD_REMOVE = false;
- static final boolean DEBUG_SAVED_STATE = false;
- static final boolean DEBUG_APP = false;
-
- static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS;
-
- // How long we wait until giving up on the last activity telling us it
- // is idle.
- static final int IDLE_TIMEOUT = 10*1000;
// Ticks during which we check progress while waiting for an app to launch.
static final int LAUNCH_TICK = 500;
@@ -110,28 +102,26 @@
// from the application in order to get its saved state.
static final int STOP_TIMEOUT = 10*1000;
- // How long we can hold the sleep wake lock before giving up.
- static final int SLEEP_TIMEOUT = 5*1000;
-
- // How long we can hold the launch wake lock before giving up.
- static final int LAUNCH_TIMEOUT = 10*1000;
-
// How long we wait until giving up on an activity telling us it has
// finished destroying itself.
static final int DESTROY_TIMEOUT = 10*1000;
-
+
// How long until we reset a task when the user returns to it. Currently
// disabled.
static final long ACTIVITY_INACTIVE_RESET_TIME = 0;
-
+
// How long between activity launches that we consider safe to not warn
// the user about an unexpected activity being launched on top.
static final long START_WARN_TIME = 5*1000;
-
+
// Set to false to disable the preview that is shown while a new activity
// is being started.
static final boolean SHOW_APP_STARTING_PREVIEW = true;
-
+
+ // How long to wait for all background Activities to redraw following a call to
+ // convertToTranslucent().
+ static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
+
enum ActivityState {
INITIALIZING,
RESUMED,
@@ -145,20 +135,20 @@
}
final ActivityManagerService mService;
- final boolean mMainStack;
-
+ final WindowManagerService mWindowManager;
+
final Context mContext;
-
+
/**
* The back history of all previous (and possibly still
- * running) activities. It contains HistoryRecord objects.
+ * running) activities. It contains #TaskRecord objects.
*/
- final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>();
+ private ArrayList<TaskRecord> mTaskHistory = new ArrayList<TaskRecord>();
/**
* Used for validating app tokens with window manager.
*/
- final ArrayList<IBinder> mValidateAppTokens = new ArrayList<IBinder>();
+ final ArrayList<TaskGroup> mValidateAppTokens = new ArrayList<TaskGroup>();
/**
* List of running activities, sorted by recent usage.
@@ -168,71 +158,10 @@
final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<ActivityRecord>();
/**
- * List of activities that are waiting for a new activity
- * to become visible before completing whatever operation they are
- * supposed to do.
- */
- final ArrayList<ActivityRecord> mWaitingVisibleActivities
- = new ArrayList<ActivityRecord>();
-
- /**
- * List of activities that are ready to be stopped, but waiting
- * for the next activity to settle down before doing so. It contains
- * HistoryRecord objects.
- */
- final ArrayList<ActivityRecord> mStoppingActivities
- = new ArrayList<ActivityRecord>();
-
- /**
- * List of activities that are in the process of going to sleep.
- */
- final ArrayList<ActivityRecord> mGoingToSleepActivities
- = new ArrayList<ActivityRecord>();
-
- /**
* Animations that for the current transition have requested not to
* be considered for the transition animation.
*/
- final ArrayList<ActivityRecord> mNoAnimActivities
- = new ArrayList<ActivityRecord>();
-
- /**
- * List of activities that are ready to be finished, but waiting
- * for the previous activity to settle down before doing so. It contains
- * HistoryRecord objects.
- */
- final ArrayList<ActivityRecord> mFinishingActivities
- = new ArrayList<ActivityRecord>();
-
- /**
- * List of people waiting to find out about the next launched activity.
- */
- final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched
- = new ArrayList<IActivityManager.WaitResult>();
-
- /**
- * List of people waiting to find out about the next visible activity.
- */
- final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
- = new ArrayList<IActivityManager.WaitResult>();
-
- final ArrayList<UserStartedState> mStartingUsers
- = new ArrayList<UserStartedState>();
-
- /**
- * Set when the system is going to sleep, until we have
- * successfully paused the current activity and released our wake lock.
- * At that point the system is allowed to actually sleep.
- */
- final PowerManager.WakeLock mGoingToSleep;
-
- /**
- * We don't want to allow the device to go to sleep while in the process
- * of launching an activity. This is primarily to allow alarm intent
- * receivers to launch an activity and get that to run before the device
- * goes back to sleep.
- */
- final PowerManager.WakeLock mLaunchingActivity;
+ final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<ActivityRecord>();
/**
* When we are in the process of pausing an activity, before starting the
@@ -251,37 +180,32 @@
* Current activity that is resumed, or null if there is none.
*/
ActivityRecord mResumedActivity = null;
-
+
/**
* This is the last activity that has been started. It is only used to
* identify when multiple activities are started at once so that the user
* can be warned they may not be in the activity they think they are.
*/
ActivityRecord mLastStartedActivity = null;
-
+
+ // The topmost Activity passed to convertToTranslucent(). When non-null it means we are
+ // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
+ // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
+ // Activity in mTranslucentActivityWaiting is notified via
+ // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
+ // background activity being drawn then the same call will be made with a true value.
+ ActivityRecord mTranslucentActivityWaiting = null;
+ ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent =
+ new ArrayList<ActivityRecord>();
+
/**
* Set when we know we are going to be calling updateConfiguration()
* soon, so want to skip intermediate config checks.
*/
boolean mConfigWillChange;
- /**
- * Set to indicate whether to issue an onUserLeaving callback when a
- * newly launched activity is being brought in front of us.
- */
- boolean mUserLeaving = false;
-
- long mInitialStartTime = 0;
-
- /**
- * Set when we have taken too long waiting to go to sleep.
- */
- boolean mSleepTimeout = false;
-
- /**
- * Dismiss the keyguard after the next activity is displayed?
- */
- boolean mDismissKeyguardOnNextActivity = false;
+ long mLaunchStartTime = 0;
+ long mFullyDrawnStartTime = 0;
/**
* Save the most recent screenshot for reuse. This keeps Recents from taking two identical
@@ -293,18 +217,19 @@
int mThumbnailWidth = -1;
int mThumbnailHeight = -1;
- private int mCurrentUser;
+ int mCurrentUser;
- static final int SLEEP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG;
+ final int mStackId;
+
+ /** Run all ActivityStacks through this */
+ final ActivityStackSupervisor mStackSupervisor;
+
static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
- static final int IDLE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
- static final int IDLE_NOW_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
- static final int LAUNCH_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
- static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
- static final int RESUME_TOP_ACTIVITY_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
- static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 7;
- static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 8;
- static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 9;
+ static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
+ static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
+ static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
+ static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
+ static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
static class ScheduleDestroyArgs {
final ProcessRecord mOwner;
@@ -330,15 +255,6 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case SLEEP_TIMEOUT_MSG: {
- synchronized (mService) {
- if (mService.isSleeping()) {
- Slog.w(TAG, "Sleep timeout! Sleeping now.");
- mSleepTimeout = true;
- checkReadyForSleepLocked();
- }
- }
- } break;
case PAUSE_TIMEOUT_MSG: {
ActivityRecord r = (ActivityRecord)msg.obj;
// We don't at this point know if the activity is fullscreen,
@@ -346,33 +262,16 @@
Slog.w(TAG, "Activity pause timeout for " + r);
synchronized (mService) {
if (r.app != null) {
- mService.logAppTooSlow(r.app, r.pauseTime,
- "pausing " + r);
+ mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
}
+ activityPausedLocked(r.appToken, true);
}
-
- activityPaused(r != null ? r.appToken : null, true);
- } break;
- case IDLE_TIMEOUT_MSG: {
- if (mService.mDidDexOpt) {
- mService.mDidDexOpt = false;
- Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
- nmsg.obj = msg.obj;
- mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
- return;
- }
- // We don't at this point know if the activity is fullscreen,
- // so we need to be conservative and assume it isn't.
- ActivityRecord r = (ActivityRecord)msg.obj;
- Slog.w(TAG, "Activity idle timeout for " + r);
- activityIdleInternal(r != null ? r.appToken : null, true, null);
} break;
case LAUNCH_TICK_MSG: {
ActivityRecord r = (ActivityRecord)msg.obj;
synchronized (mService) {
if (r.continueLaunchTickingLocked()) {
- mService.logAppTooSlow(r.app, r.launchTickTime,
- "launching " + r);
+ mService.logAppTooSlow(r.app, r.launchTickTime, "launching " + r);
}
}
} break;
@@ -381,29 +280,8 @@
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity destroy timeout for " + r);
- activityDestroyed(r != null ? r.appToken : null);
- } break;
- case IDLE_NOW_MSG: {
- ActivityRecord r = (ActivityRecord)msg.obj;
- activityIdleInternal(r != null ? r.appToken : null, false, null);
- } break;
- case LAUNCH_TIMEOUT_MSG: {
- if (mService.mDidDexOpt) {
- mService.mDidDexOpt = false;
- Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
- return;
- }
synchronized (mService) {
- if (mLaunchingActivity.isHeld()) {
- Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
- mLaunchingActivity.release();
- }
- }
- } break;
- case RESUME_TOP_ACTIVITY_MSG: {
- synchronized (mService) {
- resumeTopActivityLocked(null);
+ activityDestroyedLocked(r != null ? r.appToken : null);
}
} break;
case STOP_TIMEOUT_MSG: {
@@ -422,21 +300,33 @@
synchronized (mService) {
destroyActivitiesLocked(args.mOwner, args.mOomAdj, args.mReason);
}
- }
+ } break;
+ case TRANSLUCENT_TIMEOUT_MSG: {
+ synchronized (mService) {
+ notifyActivityDrawnLocked(null);
+ }
+ } break;
}
}
}
- ActivityStack(ActivityManagerService service, Context context, boolean mainStack, Looper looper) {
+ private int numActivities() {
+ int count = 0;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ count += mTaskHistory.get(taskNdx).mActivities.size();
+ }
+ return count;
+ }
+
+ ActivityStack(ActivityManagerService service, Context context, Looper looper, int stackId) {
mHandler = new ActivityStackHandler(looper);
mService = service;
+ mWindowManager = service.mWindowManager;
+ mStackSupervisor = service.mStackSupervisor;
mContext = context;
- mMainStack = mainStack;
- PowerManager pm =
- (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
- mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
- mLaunchingActivity.setReferenceCounted(false);
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mStackId = stackId;
+ mCurrentUser = service.mCurrentUserId;
}
private boolean okToShow(ActivityRecord r) {
@@ -445,25 +335,29 @@
}
final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- ActivityRecord r = mHistory.get(i);
- if (!r.finishing && r != notTop && okToShow(r)) {
- return r;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = activities.get(activityNdx);
+ if (!r.finishing && r != notTop && okToShow(r)) {
+ return r;
+ }
}
- i--;
}
return null;
}
final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- ActivityRecord r = mHistory.get(i);
- if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) {
- return r;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = activities.get(activityNdx);
+ if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) {
+ return r;
+ }
}
- i--;
}
return null;
}
@@ -471,88 +365,140 @@
/**
* This is a simplified version of topRunningActivityLocked that provides a number of
* optional skip-over modes. It is intended for use with the ActivityController hook only.
- *
+ *
* @param token If non-null, any history records matching this token will be skipped.
* @param taskId If non-zero, we'll attempt to skip over records with the same task ID.
- *
+ *
* @return Returns the HistoryRecord of the next activity on the stack.
*/
final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- ActivityRecord r = mHistory.get(i);
- // Note: the taskId check depends on real taskId fields being non-zero
- if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId)
- && okToShow(r)) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ TaskRecord task = mTaskHistory.get(taskNdx);
+ if (task.taskId == taskId) {
+ continue;
+ }
+ ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int i = activities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = activities.get(i);
+ // Note: the taskId check depends on real taskId fields being non-zero
+ if (!r.finishing && (token != r.appToken) && okToShow(r)) {
+ return r;
+ }
+ }
+ }
+ return null;
+ }
+
+ final ActivityRecord topActivity() {
+ // Iterate to find the first non-empty task stack. Note that this code can
+ // be simplified once we stop storing tasks with empty mActivities lists.
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ return activities.get(activityNdx);
+ }
+ }
+ return null;
+ }
+
+ final TaskRecord topTask() {
+ final int size = mTaskHistory.size();
+ if (size > 0) {
+ return mTaskHistory.get(size - 1);
+ }
+ return null;
+ }
+
+ TaskRecord taskForIdLocked(int id) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ if (task.taskId == id) {
+ return task;
+ }
+ }
+ return null;
+ }
+
+ ActivityRecord isInStackLocked(IBinder token) {
+ final ActivityRecord r = ActivityRecord.forToken(token);
+ if (r != null) {
+ final TaskRecord task = r.task;
+ if (task.mActivities.contains(r) && mTaskHistory.contains(task)) {
+ if (task.stack != this) Slog.w(TAG,
+ "Illegal state! task does not point to stack it is in.");
return r;
}
- i--;
}
return null;
}
- final int indexOfTokenLocked(IBinder token) {
- return mHistory.indexOf(ActivityRecord.forToken(token));
- }
-
- final int indexOfActivityLocked(ActivityRecord r) {
- return mHistory.indexOf(r);
- }
-
- final ActivityRecord isInStackLocked(IBinder token) {
- ActivityRecord r = ActivityRecord.forToken(token);
- if (mHistory.contains(r)) {
- return r;
+ boolean containsApp(ProcessRecord app) {
+ if (app == null) {
+ return false;
}
- return null;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.app == app) {
+ return true;
+ }
+ }
+ }
+ return false;
}
- private final boolean updateLRUListLocked(ActivityRecord r) {
+ final boolean updateLRUListLocked(ActivityRecord r) {
final boolean hadit = mLRUActivities.remove(r);
mLRUActivities.add(r);
return hadit;
}
+ final boolean isHomeStack() {
+ return mStackId == HOME_STACK_ID;
+ }
+
/**
* Returns the top activity in any existing task matching the given
* Intent. Returns null if no such task is found.
*/
- private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+ ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
-
- TaskRecord cp = null;
-
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
- final int N = mHistory.size();
- for (int i=(N-1); i>=0; i--) {
- ActivityRecord r = mHistory.get(i);
- if (!r.finishing && r.task != cp && r.userId == userId
- && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- cp = r.task;
- //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
- // + "/aff=" + r.task.affinity + " to new cls="
- // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
- if (r.task.affinity != null) {
- if (r.task.affinity.equals(info.taskAffinity)) {
- //Slog.i(TAG, "Found matching affinity!");
- return r;
- }
- } else if (r.task.intent != null
- && r.task.intent.getComponent().equals(cls)) {
- //Slog.i(TAG, "Found matching class!");
- //dump();
- //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
- return r;
- } else if (r.task.affinityIntent != null
- && r.task.affinityIntent.getComponent().equals(cls)) {
- //Slog.i(TAG, "Found matching class!");
- //dump();
- //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ final ActivityRecord r = task.getTopActivity();
+ if (r == null || r.finishing || r.userId != userId ||
+ r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ continue;
+ }
+
+ //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
+ // + "/aff=" + r.task.affinity + " to new cls="
+ // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
+ if (task.affinity != null) {
+ if (task.affinity.equals(info.taskAffinity)) {
+ //Slog.i(TAG, "Found matching affinity!");
return r;
}
+ } else if (task.intent != null && task.intent.getComponent().equals(cls)) {
+ //Slog.i(TAG, "Found matching class!");
+ //dump();
+ //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+ return r;
+ } else if (task.affinityIntent != null
+ && task.affinityIntent.getComponent().equals(cls)) {
+ //Slog.i(TAG, "Found matching class!");
+ //dump();
+ //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+ return r;
}
}
@@ -564,18 +510,18 @@
* is the same as the given activity. Returns null if no such activity
* is found.
*/
- private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
+ ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
- final int N = mHistory.size();
- for (int i=(N-1); i>=0; i--) {
- ActivityRecord r = mHistory.get(i);
- if (!r.finishing) {
- if (r.intent.getComponent().equals(cls) && r.userId == userId) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = activities.get(activityNdx);
+ if (!r.finishing && r.intent.getComponent().equals(cls) && r.userId == userId) {
//Slog.i(TAG, "Found matching class!");
//dump();
//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
@@ -587,343 +533,124 @@
return null;
}
- final void showAskCompatModeDialogLocked(ActivityRecord r) {
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.SHOW_COMPAT_MODE_DIALOG_MSG;
- msg.obj = r.task.askedCompatMode ? null : r;
- mService.mHandler.sendMessage(msg);
- }
-
/*
- * Move the activities around in the stack to bring a user to the foreground.
+ * Move the activities around in the stack to bring a user to the foreground. This only
+ * matters on the home stack. All other stacks are single user.
* @return whether there are any activities for the specified user.
*/
- final boolean switchUserLocked(int userId, UserStartedState uss) {
+ final boolean switchUserLocked(int userId) {
+ if (VALIDATE_TOKENS) {
+ validateAppTokensLocked();
+ }
+ if (mCurrentUser == userId) {
+ return true;
+ }
mCurrentUser = userId;
- mStartingUsers.add(uss);
- // Only one activity? Nothing to do...
- if (mHistory.size() < 2)
- return false;
-
+ // Move userId's tasks to the top.
boolean haveActivities = false;
- // Check if the top activity is from the new user.
- ActivityRecord top = mHistory.get(mHistory.size() - 1);
- if (top.userId == userId) return true;
- // Otherwise, move the user's activities to the top.
- int N = mHistory.size();
- int i = 0;
- while (i < N) {
- ActivityRecord r = mHistory.get(i);
- if (r.userId == userId) {
- ActivityRecord moveToTop = mHistory.remove(i);
- mHistory.add(moveToTop);
- // No need to check the top one now
- N--;
+ int index = mTaskHistory.size();
+ for (int i = 0; i < index; ++i) {
+ TaskRecord task = mTaskHistory.get(i);
+ if (task.userId == userId) {
haveActivities = true;
- } else {
- i++;
+ mTaskHistory.remove(i);
+ mTaskHistory.add(task);
+ --index;
}
}
- // Transition from the old top to the new top
- resumeTopActivityLocked(top);
+
return haveActivities;
}
- final boolean realStartActivityLocked(ActivityRecord r,
- ProcessRecord app, boolean andResume, boolean checkConfig)
- throws RemoteException {
-
- r.startFreezingScreenLocked(app, 0);
- mService.mWindowManager.setAppVisibility(r.appToken, true);
-
- // schedule launch ticks to collect information about slow apps.
- r.startLaunchTickingLocked();
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order. Note that
- // as a result of this, it can call back into the activity
- // manager with a new orientation. We don't care about that,
- // because the activity is not currently running so we are
- // just restarting it anyway.
- if (checkConfig) {
- Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
- mService.mConfiguration,
- r.mayFreezeScreenLocked(app) ? r.appToken : null);
- mService.updateConfigurationLocked(config, r, false, false);
- }
-
- r.app = app;
- app.waitingToKill = null;
- r.launchCount++;
- r.lastLaunchTime = SystemClock.uptimeMillis();
-
- if (localLOGV) Slog.v(TAG, "Launching: " + r);
-
- int idx = app.activities.indexOf(r);
- if (idx < 0) {
- app.activities.add(r);
- }
- mService.updateLruProcessLocked(app, true);
-
- try {
- if (app.thread == null) {
- throw new RemoteException();
- }
- List<ResultInfo> results = null;
- List<Intent> newIntents = null;
- if (andResume) {
- results = r.results;
- newIntents = r.newIntents;
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
- + " icicle=" + r.icicle
- + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
- if (andResume) {
- EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
- r.userId, System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
- }
- if (r.isHomeActivity) {
- mService.mHomeProcess = app;
- }
- mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
- r.sleeping = false;
- r.forceNewConfig = false;
- showAskCompatModeDialogLocked(r);
- r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
- String profileFile = null;
- ParcelFileDescriptor profileFd = null;
- boolean profileAutoStop = false;
- if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
- if (mService.mProfileProc == null || mService.mProfileProc == app) {
- mService.mProfileProc = app;
- profileFile = mService.mProfileFile;
- profileFd = mService.mProfileFd;
- profileAutoStop = mService.mAutoStopProfiler;
- }
- }
- app.hasShownUi = true;
- app.pendingUiClean = true;
- if (profileFd != null) {
- try {
- profileFd = profileFd.dup();
- } catch (IOException e) {
- profileFd = null;
- }
- }
- app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
- System.identityHashCode(r), r.info,
- new Configuration(mService.mConfiguration),
- r.compat, r.icicle, results, newIntents, !andResume,
- mService.isNextTransitionForward(), profileFile, profileFd,
- profileAutoStop);
-
- if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
- // This may be a heavy-weight process! Note that the package
- // manager will ensure that only activity can run in the main
- // process of the .apk, which is the only thing that will be
- // considered heavy-weight.
- if (app.processName.equals(app.info.packageName)) {
- if (mService.mHeavyWeightProcess != null
- && mService.mHeavyWeightProcess != app) {
- Log.w(TAG, "Starting new heavy weight process " + app
- + " when already running "
- + mService.mHeavyWeightProcess);
- }
- mService.mHeavyWeightProcess = app;
- Message msg = mService.mHandler.obtainMessage(
- ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
- msg.obj = r;
- mService.mHandler.sendMessage(msg);
- }
- }
-
- } catch (RemoteException e) {
- if (r.launchFailed) {
- // This is the second time we failed -- finish activity
- // and give up.
- Slog.e(TAG, "Second failure launching "
- + r.intent.getComponent().flattenToShortString()
- + ", giving up", e);
- mService.appDiedLocked(app, app.pid, app.thread);
- requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "2nd-crash", false);
- return false;
- }
-
- // This is the first time we failed -- restart process and
- // retry.
- app.activities.remove(r);
- throw e;
- }
-
- r.launchFailed = false;
- if (updateLRUListLocked(r)) {
- Slog.w(TAG, "Activity " + r
- + " being launched, but already in LRU list");
- }
-
- if (andResume) {
- // As part of the process of launching, ActivityThread also performs
- // a resume.
- r.state = ActivityState.RESUMED;
- if (DEBUG_STATES) Slog.v(TAG, "Moving to RESUMED: " + r
- + " (starting new instance)");
- r.stopped = false;
- mResumedActivity = r;
- r.task.touchActiveTime();
- if (mMainStack) {
- mService.addRecentTaskLocked(r.task);
- }
- completeResumeLocked(r);
- checkReadyForSleepLocked();
- if (DEBUG_SAVED_STATE) Slog.i(TAG, "Launch completed; removing icicle of " + r.icicle);
- } else {
- // This activity is not starting in the resumed state... which
- // should look like we asked it to pause+stop (but remain visible),
- // and it has done so and reported back the current icicle and
- // other state.
- if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r
- + " (starting in stopped state)");
- r.state = ActivityState.STOPPED;
- r.stopped = true;
- }
-
- // Launch the new version setup screen if needed. We do this -after-
- // launching the initial activity (that is, home), so that it can have
- // a chance to initialize itself while in the background, making the
- // switch back to it faster and look better.
- if (mMainStack) {
- mService.startSetupActivityLocked();
- }
-
- return true;
+ void minimalResumeActivityLocked(ActivityRecord r) {
+ r.state = ActivityState.RESUMED;
+ if (DEBUG_STATES) Slog.v(TAG, "Moving to RESUMED: " + r
+ + " (starting new instance)");
+ r.stopped = false;
+ mResumedActivity = r;
+ r.task.touchActiveTime();
+ mService.addRecentTaskLocked(r.task);
+ completeResumeLocked(r);
+ mStackSupervisor.checkReadyForSleepLocked();
+ if (DEBUG_SAVED_STATE) Slog.i(TAG, "Launch completed; removing icicle of " + r.icicle);
}
- private final void startSpecificActivityLocked(ActivityRecord r,
- boolean andResume, boolean checkConfig) {
- // Is this activity's application already running?
- ProcessRecord app = mService.getProcessRecordLocked(r.processName,
- r.info.applicationInfo.uid);
-
- if (r.launchTime == 0) {
- r.launchTime = SystemClock.uptimeMillis();
- if (mInitialStartTime == 0) {
- mInitialStartTime = r.launchTime;
- }
- } else if (mInitialStartTime == 0) {
- mInitialStartTime = SystemClock.uptimeMillis();
+ private void startLaunchTraces() {
+ if (mFullyDrawnStartTime != 0) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
}
-
- if (app != null && app.thread != null) {
- try {
- app.addPackage(r.info.packageName);
- realStartActivityLocked(r, app, andResume, checkConfig);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when starting activity "
- + r.intent.getComponent().flattenToShortString(), e);
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
-
- mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- "activity", r.intent.getComponent(), false, false);
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching", 0);
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
}
-
- void stopIfSleepingLocked() {
- if (mService.isSleeping()) {
- if (!mGoingToSleep.isHeld()) {
- mGoingToSleep.acquire();
- if (mLaunchingActivity.isHeld()) {
- mLaunchingActivity.release();
- mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- }
- }
- mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
- Message msg = mHandler.obtainMessage(SLEEP_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(msg, SLEEP_TIMEOUT);
- checkReadyForSleepLocked();
+
+ private void stopFullyDrawnTraceIfNeeded() {
+ if (mFullyDrawnStartTime != 0 && mLaunchStartTime == 0) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
+ mFullyDrawnStartTime = 0;
}
}
+ void setLaunchTime(ActivityRecord r) {
+ if (r.displayStartTime == 0) {
+ r.fullyDrawnStartTime = r.displayStartTime = SystemClock.uptimeMillis();
+ if (mLaunchStartTime == 0) {
+ startLaunchTraces();
+ mLaunchStartTime = mFullyDrawnStartTime = r.displayStartTime;
+ }
+ } else if (mLaunchStartTime == 0) {
+ startLaunchTraces();
+ mLaunchStartTime = mFullyDrawnStartTime = SystemClock.uptimeMillis();
+ }
+ }
+
+ void clearLaunchTime(ActivityRecord r) {
+ r.displayStartTime = r.fullyDrawnStartTime = 0;
+ }
+
void awakeFromSleepingLocked() {
- mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
- mSleepTimeout = false;
- if (mGoingToSleep.isHeld()) {
- mGoingToSleep.release();
- }
// Ensure activities are no longer sleeping.
- for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = mHistory.get(i);
- r.setSleeping(false);
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ activities.get(activityNdx).setSleeping(false);
+ }
}
- mGoingToSleepActivities.clear();
}
- void activitySleptLocked(ActivityRecord r) {
- mGoingToSleepActivities.remove(r);
- checkReadyForSleepLocked();
- }
-
- void checkReadyForSleepLocked() {
- if (!mService.isSleeping()) {
- // Do not care.
- return;
+ /**
+ * @return true if something must be done before going to sleep.
+ */
+ boolean checkReadyForSleepLocked() {
+ if (mResumedActivity != null) {
+ // Still have something resumed; can't sleep until it is paused.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause " + mResumedActivity);
+ if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
+ startPausingLocked(false, true);
+ return true;
+ }
+ if (mPausingActivity != null) {
+ // Still waiting for something to pause; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivity);
+ return true;
}
- if (!mSleepTimeout) {
- if (mResumedActivity != null) {
- // Still have something resumed; can't sleep until it is paused.
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause " + mResumedActivity);
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
- startPausingLocked(false, true);
- return;
- }
- if (mPausingActivity != null) {
- // Still waiting for something to pause; can't sleep yet.
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivity);
- return;
- }
+ return false;
+ }
- if (mStoppingActivities.size() > 0) {
- // Still need to tell some activities to stop; can't sleep yet.
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to stop "
- + mStoppingActivities.size() + " activities");
- scheduleIdleLocked();
- return;
- }
+ void goToSleep() {
+ ensureActivitiesVisibleLocked(null, 0);
- ensureActivitiesVisibleLocked(null, 0);
-
- // Make sure any stopped but visible activities are now sleeping.
- // This ensures that the activity's onStop() is called.
- for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = mHistory.get(i);
+ // Make sure any stopped but visible activities are now sleeping.
+ // This ensures that the activity's onStop() is called.
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
r.setSleeping(true);
}
}
-
- if (mGoingToSleepActivities.size() > 0) {
- // Still need to tell some activities to sleep; can't sleep yet.
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to sleep "
- + mGoingToSleepActivities.size() + " activities");
- return;
- }
- }
-
- mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
-
- if (mGoingToSleep.isHeld()) {
- mGoingToSleep.release();
- }
- if (mService.mShuttingDown) {
- mService.notifyAll();
}
}
@@ -931,7 +658,7 @@
if (who.noDisplay) {
return null;
}
-
+
Resources res = mService.mContext.getResources();
int w = mThumbnailWidth;
int h = mThumbnailHeight;
@@ -947,7 +674,7 @@
|| mLastScreenshotBitmap.getWidth() != w
|| mLastScreenshotBitmap.getHeight() != h) {
mLastScreenshotActivity = who;
- mLastScreenshotBitmap = mService.mWindowManager.screenshotApplications(
+ mLastScreenshotBitmap = mWindowManager.screenshotApplications(
who.appToken, Display.DEFAULT_DISPLAY, w, h);
}
if (mLastScreenshotBitmap != null) {
@@ -957,17 +684,16 @@
return null;
}
- private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+ final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
if (mPausingActivity != null) {
- RuntimeException e = new RuntimeException();
Slog.e(TAG, "Trying to pause when pause is already pending for "
- + mPausingActivity, e);
+ + mPausingActivity, new RuntimeException("here").fillInStackTrace());
}
ActivityRecord prev = mResumedActivity;
if (prev == null) {
- RuntimeException e = new RuntimeException();
- Slog.e(TAG, "Trying to pause when nothing is resumed", e);
- resumeTopActivityLocked(null);
+ Slog.e(TAG, "Trying to pause when nothing is resumed",
+ new RuntimeException("here").fillInStackTrace());
+ mStackSupervisor.resumeTopActivitiesLocked();
return;
}
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
@@ -977,21 +703,21 @@
mLastPausedActivity = prev;
prev.state = ActivityState.PAUSING;
prev.task.touchActiveTime();
+ clearLaunchTime(prev);
prev.updateThumbnail(screenshotActivities(prev), null);
+ stopFullyDrawnTraceIfNeeded();
mService.updateCpuStats();
-
+
if (prev.app != null && prev.app.thread != null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
try {
EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
prev.userId, System.identityHashCode(prev),
prev.shortComponentName);
+ mService.updateUsageStats(prev, false);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags);
- if (mMainStack) {
- mService.updateUsageStats(prev, false);
- }
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
@@ -1005,16 +731,10 @@
// If we are not going to sleep, we want to ensure the device is
// awake until the next activity is started.
- if (!mService.mSleeping && !mService.mShuttingDown) {
- mLaunchingActivity.acquire();
- if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
- // To be safe, don't allow the wake lock to be held for too long.
- Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
- }
+ if (!mService.isSleepingOrShuttingDown()) {
+ mStackSupervisor.acquireLaunchWakelock();
}
-
if (mPausingActivity != null) {
// Have the window manager pause its key dispatching until the new
// activity has started. If we're pausing the activity just because
@@ -1038,46 +758,27 @@
// This activity failed to schedule the
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
- resumeTopActivityLocked(null);
+ mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
}
}
- final void activityResumed(IBinder token) {
- ActivityRecord r = null;
-
- synchronized (mService) {
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- r = mHistory.get(index);
- if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r);
- r.icicle = null;
- r.haveState = false;
- }
- }
- }
-
- final void activityPaused(IBinder token, boolean timeout) {
+ final void activityPausedLocked(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(
TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
- ActivityRecord r = null;
-
- synchronized (mService) {
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- r = mHistory.get(index);
- mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- if (mPausingActivity == r) {
- if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
- + (timeout ? " (due to timeout)" : " (pause complete)"));
- r.state = ActivityState.PAUSED;
- completePauseLocked();
- } else {
- EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
- r.userId, System.identityHashCode(r), r.shortComponentName,
- mPausingActivity != null
- ? mPausingActivity.shortComponentName : "(none)");
- }
+ final ActivityRecord r = isInStackLocked(token);
+ if (r != null) {
+ mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+ if (mPausingActivity == r) {
+ if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
+ + (timeout ? " (due to timeout)" : " (pause complete)"));
+ r.state = ActivityState.PAUSED;
+ completePauseLocked();
+ } else {
+ EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
+ r.userId, System.identityHashCode(r), r.shortComponentName,
+ mPausingActivity != null
+ ? mPausingActivity.shortComponentName : "(none)");
}
}
}
@@ -1108,7 +809,7 @@
} else {
if (r.configDestroy) {
destroyActivityLocked(r, true, false, "stop-config");
- resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
} else {
// Now that this process has stopped, we may want to consider
// it to be the previous app to try to keep around in case
@@ -1133,7 +834,7 @@
private final void completePauseLocked() {
ActivityRecord prev = mPausingActivity;
if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
-
+
if (prev != null) {
if (prev.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
@@ -1142,7 +843,7 @@
if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
if (prev.waitingVisible) {
prev.waitingVisible = false;
- mWaitingVisibleActivities.remove(prev);
+ mStackSupervisor.mWaitingVisibleActivities.remove(prev);
if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
TAG, "Complete pause, no longer waiting: " + prev);
}
@@ -1155,15 +856,17 @@
if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
destroyActivityLocked(prev, true, false, "pause-config");
} else {
- mStoppingActivities.add(prev);
- if (mStoppingActivities.size() > 3) {
+ mStackSupervisor.mStoppingActivities.add(prev);
+ if (mStackSupervisor.mStoppingActivities.size() > 3 ||
+ prev.frontOfTask && mTaskHistory.size() <= 1) {
// If we already have a few activities waiting to stop,
// then give up on things going idle and start clearing
- // them out.
+ // them out. Or if r is the last of activity of the last task the stack
+ // will be empty and must be cleared immediately.
if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
- scheduleIdleLocked();
+ mStackSupervisor.scheduleIdleLocked();
} else {
- checkReadyForSleepLocked();
+ mStackSupervisor.checkReadyForSleepLocked();
}
}
} else {
@@ -1173,45 +876,46 @@
mPausingActivity = null;
}
- if (!mService.isSleeping()) {
- resumeTopActivityLocked(prev);
+ final ActivityStack topStack = mStackSupervisor.getFocusedStack();
+ if (!mService.isSleepingOrShuttingDown()) {
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
} else {
- checkReadyForSleepLocked();
- ActivityRecord top = topRunningActivityLocked(null);
+ mStackSupervisor.checkReadyForSleepLocked();
+ ActivityRecord top = topStack.topRunningActivityLocked(null);
if (top == null || (prev != null && top != prev)) {
// If there are no more activities available to run,
// do resume anyway to start something. Also if the top
// activity on the stack is not the just paused activity,
// we need to go ahead and resume it to ensure we complete
// an in-flight app switch.
- resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
}
}
-
- if (prev != null) {
- prev.resumeKeyDispatchingLocked();
- }
- if (prev.app != null && prev.cpuTimeAtResume > 0
- && mService.mBatteryStatsService.isOnBattery()) {
- long diff = 0;
- synchronized (mService.mProcessStatsThread) {
- diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid)
- - prev.cpuTimeAtResume;
- }
- if (diff > 0) {
- BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
- synchronized (bsi) {
- BatteryStatsImpl.Uid.Proc ps =
- bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
- prev.info.packageName);
- if (ps != null) {
- ps.addForegroundTimeLocked(diff);
+ if (prev != null) {
+ prev.resumeKeyDispatchingLocked();
+
+ if (prev.app != null && prev.cpuTimeAtResume > 0
+ && mService.mBatteryStatsService.isOnBattery()) {
+ long diff;
+ synchronized (mService.mProcessStatsThread) {
+ diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid)
+ - prev.cpuTimeAtResume;
+ }
+ if (diff > 0) {
+ BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
+ synchronized (bsi) {
+ BatteryStatsImpl.Uid.Proc ps =
+ bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
+ prev.info.packageName);
+ if (ps != null) {
+ ps.addForegroundTimeLocked(diff);
+ }
}
}
}
+ prev.cpuTimeAtResume = 0; // reset it
}
- prev.cpuTimeAtResume = 0; // reset it
}
/**
@@ -1219,36 +923,17 @@
* the resumed state (either by launching it or explicitly telling it),
* this function updates the rest of our state to match that fact.
*/
- private final void completeResumeLocked(ActivityRecord next) {
+ private void completeResumeLocked(ActivityRecord next) {
next.idle = false;
next.results = null;
next.newIntents = null;
// schedule an idle timeout in case the app doesn't do it for us.
- Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
- msg.obj = next;
- mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
+ mStackSupervisor.scheduleIdleTimeoutLocked(next);
- if (false) {
- // The activity was never told to pause, so just keep
- // things going as-is. To maintain our own state,
- // we need to emulate it coming back and saying it is
- // idle.
- msg = mHandler.obtainMessage(IDLE_NOW_MSG);
- msg.obj = next;
- mHandler.sendMessage(msg);
- }
+ mStackSupervisor.reportResumedActivityLocked(next);
- if (mMainStack) {
- mService.reportResumedActivityLocked(next);
- }
-
- if (mMainStack) {
- mService.setFocusedActivityLocked(next);
- }
next.resumeKeyDispatchingLocked();
- ensureActivitiesVisibleLocked(null, 0);
- mService.mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
// Mark the point when the activity is resuming
@@ -1264,129 +949,165 @@
}
/**
+ * Version of ensureActivitiesVisible that can easily be called anywhere.
+ */
+ final boolean ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
+ return ensureActivitiesVisibleLocked(starting, configChanges, false);
+ }
+
+ final boolean ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+ boolean forceHomeShown) {
+ ActivityRecord r = topRunningActivityLocked(null);
+ return r != null &&
+ ensureActivitiesVisibleLocked(r, starting, null, configChanges, forceHomeShown);
+ }
+
+ /**
* Make sure that all activities that need to be visible (that is, they
* currently can be seen by the user) actually are.
*/
- final void ensureActivitiesVisibleLocked(ActivityRecord top,
- ActivityRecord starting, String onlyThisProcess, int configChanges) {
+ final boolean ensureActivitiesVisibleLocked(ActivityRecord top, ActivityRecord starting,
+ String onlyThisProcess, int configChanges, boolean forceHomeShown) {
if (DEBUG_VISBILITY) Slog.v(
TAG, "ensureActivitiesVisible behind " + top
+ " configChanges=0x" + Integer.toHexString(configChanges));
+ if (mTranslucentActivityWaiting != top) {
+ mUndrawnActivitiesBelowTopTranslucent.clear();
+ if (mTranslucentActivityWaiting != null) {
+ // Call the callback with a timeout indication.
+ notifyActivityDrawnLocked(null);
+ mTranslucentActivityWaiting = null;
+ }
+ mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+ }
+
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
- final int count = mHistory.size();
- int i = count-1;
- while (mHistory.get(i) != top) {
- i--;
- }
- ActivityRecord r;
- boolean behindFullscreen = false;
- for (; i>=0; i--) {
- r = mHistory.get(i);
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Make visible? " + r + " finishing=" + r.finishing
- + " state=" + r.state);
- if (r.finishing) {
- continue;
- }
-
- final boolean doThisProcess = onlyThisProcess == null
- || onlyThisProcess.equals(r.processName);
-
- // First: if this is not the current activity being started, make
- // sure it matches the current configuration.
- if (r != starting && doThisProcess) {
- ensureActivityConfigurationLocked(r, 0);
- }
-
- if (r.app == null || r.app.thread == null) {
- if (onlyThisProcess == null
- || onlyThisProcess.equals(r.processName)) {
- // This activity needs to be visible, but isn't even
- // running... get it started, but don't resume it
- // at this point.
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Start and freeze screen for " + r);
- if (r != starting) {
- r.startFreezingScreenLocked(r.app, configChanges);
- }
- if (!r.visible) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Starting and making visible: " + r);
- mService.mWindowManager.setAppVisibility(r.appToken, true);
- }
- if (r != starting) {
- startSpecificActivityLocked(r, false, false);
- }
+ boolean aboveTop = true;
+ boolean showHomeBehindStack = false;
+ boolean behindFullscreen = !mStackSupervisor.isFrontStack(this) &&
+ !(forceHomeShown && isHomeStack());
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.finishing) {
+ continue;
}
-
- } else if (r.visible) {
- // If this activity is already visible, then there is nothing
- // else to do here.
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Skipping: already visible at " + r);
- r.stopFreezingScreenLocked(false);
-
- } else if (onlyThisProcess == null) {
- // This activity is not currently visible, but is running.
- // Tell it to become visible.
- r.visible = true;
- if (r.state != ActivityState.RESUMED && r != starting) {
- // If this activity is paused, tell it
- // to now show its window.
+ if (aboveTop && r != top) {
+ continue;
+ }
+ aboveTop = false;
+ if (!behindFullscreen) {
if (DEBUG_VISBILITY) Slog.v(
- TAG, "Making visible and scheduling visibility: " + r);
- try {
- mService.mWindowManager.setAppVisibility(r.appToken, true);
- r.sleeping = false;
- r.app.pendingUiClean = true;
- r.app.thread.scheduleWindowVisibility(r.appToken, true);
+ TAG, "Make visible? " + r + " finishing=" + r.finishing
+ + " state=" + r.state);
+
+ final boolean doThisProcess = onlyThisProcess == null
+ || onlyThisProcess.equals(r.processName);
+
+ // First: if this is not the current activity being started, make
+ // sure it matches the current configuration.
+ if (r != starting && doThisProcess) {
+ ensureActivityConfigurationLocked(r, 0);
+ }
+
+ if (r.app == null || r.app.thread == null) {
+ if (onlyThisProcess == null || onlyThisProcess.equals(r.processName)) {
+ // This activity needs to be visible, but isn't even
+ // running... get it started, but don't resume it
+ // at this point.
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Start and freeze screen for " + r);
+ if (r != starting) {
+ r.startFreezingScreenLocked(r.app, configChanges);
+ }
+ if (!r.visible) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Starting and making visible: " + r);
+ mWindowManager.setAppVisibility(r.appToken, true);
+ }
+ if (r != starting) {
+ mStackSupervisor.startSpecificActivityLocked(r, false, false);
+ }
+ }
+
+ } else if (r.visible) {
+ // If this activity is already visible, then there is nothing
+ // else to do here.
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Skipping: already visible at " + r);
r.stopFreezingScreenLocked(false);
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making visibile: "
- + r.intent.getComponent(), e);
+
+ } else if (onlyThisProcess == null) {
+ // This activity is not currently visible, but is running.
+ // Tell it to become visible.
+ r.visible = true;
+ if (r.state != ActivityState.RESUMED && r != starting) {
+ // If this activity is paused, tell it
+ // to now show its window.
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Making visible and scheduling visibility: " + r);
+ try {
+ if (mTranslucentActivityWaiting != null) {
+ mUndrawnActivitiesBelowTopTranslucent.add(r);
+ }
+ mWindowManager.setAppVisibility(r.appToken, true);
+ r.sleeping = false;
+ r.app.pendingUiClean = true;
+ r.app.thread.scheduleWindowVisibility(r.appToken, true);
+ r.stopFreezingScreenLocked(false);
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it
+ // visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making visibile: "
+ + r.intent.getComponent(), e);
+ }
+ }
}
- }
- }
- // Aggregate current change flags.
- configChanges |= r.configChangeFlags;
+ // Aggregate current change flags.
+ configChanges |= r.configChangeFlags;
- if (r.fullscreen) {
- // At this point, nothing else needs to be shown
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Stopping: fullscreen at " + r);
- behindFullscreen = true;
- i--;
- break;
- }
- }
-
- // Now for any activities that aren't visible to the user, make
- // sure they no longer are keeping the screen frozen.
- while (i >= 0) {
- r = mHistory.get(i);
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Make invisible? " + r + " finishing=" + r.finishing
- + " state=" + r.state
- + " behindFullscreen=" + behindFullscreen);
- if (!r.finishing) {
- if (behindFullscreen) {
+ if (r.fullscreen) {
+ // At this point, nothing else needs to be shown
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r);
+ behindFullscreen = true;
+ } else if (r.mLaunchHomeTaskNext) {
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
+ showHomeBehindStack = true;
+ behindFullscreen = true;
+ }
+ } else {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Make invisible? " + r + " finishing=" + r.finishing
+ + " state=" + r.state
+ + " behindFullscreen=" + behindFullscreen);
+ // Now for any activities that aren't visible to the user, make
+ // sure they no longer are keeping the screen frozen.
if (r.visible) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Making invisible: " + r);
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Making invisible: " + r);
r.visible = false;
try {
- mService.mWindowManager.setAppVisibility(r.appToken, false);
- if ((r.state == ActivityState.STOPPING
- || r.state == ActivityState.STOPPED)
- && r.app != null && r.app.thread != null) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Scheduling invisibility: " + r);
- r.app.thread.scheduleWindowVisibility(r.appToken, false);
+ mWindowManager.setAppVisibility(r.appToken, false);
+ switch (r.state) {
+ case STOPPING:
+ case STOPPED:
+ if (r.app != null && r.app.thread != null) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Scheduling invisibility: " + r);
+ r.app.thread.scheduleWindowVisibility(r.appToken, false);
+ }
+ break;
+
+ case INITIALIZING:
+ case RESUMED:
+ case PAUSING:
+ case PAUSED:
+ stopActivityLocked(r);
+ break;
+
+ default:
+ break;
}
} catch (Exception e) {
// Just skip on any failure; we'll make it
@@ -1395,30 +1116,50 @@
+ r.intent.getComponent(), e);
}
} else {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Already invisible: " + r);
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Already invisible: " + r);
}
- } else if (r.fullscreen) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Now behindFullscreen: " + r);
- behindFullscreen = true;
}
}
- i--;
}
+ return showHomeBehindStack;
+ }
+
+ void convertToTranslucent(ActivityRecord r) {
+ mTranslucentActivityWaiting = r;
+ mUndrawnActivitiesBelowTopTranslucent.clear();
+ mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
}
/**
- * Version of ensureActivitiesVisible that can easily be called anywhere.
+ * Called as activities below the top translucent activity are redrawn. When the last one is
+ * redrawn notify the top activity by calling
+ * {@link Activity#onTranslucentConversionComplete}.
+ *
+ * @param r The most recent background activity to be drawn. Or, if r is null then a timeout
+ * occurred and the activity will be notified immediately.
*/
- final void ensureActivitiesVisibleLocked(ActivityRecord starting,
- int configChanges) {
- ActivityRecord r = topRunningActivityLocked(null);
- if (r != null) {
- ensureActivitiesVisibleLocked(r, starting, null, configChanges);
+ void notifyActivityDrawnLocked(ActivityRecord r) {
+ if ((r == null)
+ || (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
+ mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
+ // The last undrawn activity below the top has just been drawn. If there is an
+ // opaque activity at the top, notify it that it can become translucent safely now.
+ final ActivityRecord waitingActivity = mTranslucentActivityWaiting;
+ mTranslucentActivityWaiting = null;
+ mUndrawnActivitiesBelowTopTranslucent.clear();
+ mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+
+ if (waitingActivity != null && waitingActivity.app != null &&
+ waitingActivity.app.thread != null) {
+ try {
+ waitingActivity.app.thread.scheduleTranslucentConversionComplete(
+ waitingActivity.appToken, r != null);
+ } catch (RemoteException e) {
+ }
+ }
}
}
-
+
/**
* Ensure that the top activity in the stack is resumed.
*
@@ -1438,42 +1179,69 @@
// Remember how we'll process this pause/resume situation, and ensure
// that the state is reset however we wind up proceeding.
- final boolean userLeaving = mUserLeaving;
- mUserLeaving = false;
+ final boolean userLeaving = mStackSupervisor.mUserLeaving;
+ mStackSupervisor.mUserLeaving = false;
if (next == null) {
// There are no more activities! Let's just start up the
// Launcher...
- if (mMainStack) {
- ActivityOptions.abort(options);
- return mService.startHomeActivityLocked(mCurrentUser);
- }
+ ActivityOptions.abort(options);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return mStackSupervisor.resumeHomeActivity(prev);
}
next.delayedResume = false;
-
+
// If the top activity is the resumed one, nothing to do.
- if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
+ if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
+ mStackSupervisor.allResumedActivitiesComplete()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
- mService.mWindowManager.executeAppTransition();
+ mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
ActivityOptions.abort(options);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
+ final TaskRecord nextTask = next.task;
+ final TaskRecord prevTask = prev != null ? prev.task : null;
+ if (prevTask != null && prev.mLaunchHomeTaskNext && prev.finishing && prev.frontOfTask) {
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ if (prevTask == nextTask) {
+ ArrayList<ActivityRecord> activities = prevTask.mActivities;
+ final int numActivities = activities.size();
+ for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ // r is usually the same as next, but what if two activities were launched
+ // before prev finished?
+ if (!r.finishing) {
+ r.mLaunchHomeTaskNext = true;
+ r.frontOfTask = true;
+ break;
+ }
+ }
+ } else if (prevTask != topTask()) {
+ // This task is going away but it was supposed to return to the home task.
+ // Now the task above it has to return to the home task instead.
+ final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
+ mTaskHistory.get(taskNdx).mActivities.get(0).mLaunchHomeTaskNext = true;
+ } else {
+ return mStackSupervisor.resumeHomeActivity(prev);
+ }
+ }
+
// If we are sleeping, and there is no resumed activity, and the top
// activity is paused, well that is the state we want.
- if ((mService.mSleeping || mService.mShuttingDown)
+ if ((mService.isSleepingOrShuttingDown())
&& mLastPausedActivity == next
- && (next.state == ActivityState.PAUSED
- || next.state == ActivityState.STOPPED
- || next.state == ActivityState.STOPPING)) {
+ && mStackSupervisor.allPausedActivitiesComplete()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
- mService.mWindowManager.executeAppTransition();
+ mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
ActivityOptions.abort(options);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -1483,15 +1251,16 @@
if (mService.mStartedUsers.get(next.userId) == null) {
Slog.w(TAG, "Skipping resume of top activity " + next
+ ": user " + next.userId + " is stopped");
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
// The activity may be waiting for stop, but that is no longer
// appropriate for it.
- mStoppingActivities.remove(next);
- mGoingToSleepActivities.remove(next);
+ mStackSupervisor.mStoppingActivities.remove(next);
+ mStackSupervisor.mGoingToSleepActivities.remove(next);
next.sleeping = false;
- mWaitingVisibleActivities.remove(next);
+ mStackSupervisor.mWaitingVisibleActivities.remove(next);
next.updateOptionsLocked(options);
@@ -1499,9 +1268,9 @@
// If we are currently pausing an activity, then don't do anything
// until that is done.
- if (mPausingActivity != null) {
- if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG,
- "Skip resume: pausing=" + mPausingActivity);
+ if (!mStackSupervisor.allPausedActivitiesComplete()) {
+ if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG, "Skip resume: some activity pausing");
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -1533,10 +1302,15 @@
mLastStartedActivity = next;
}
}
-
+
// We need to start pausing the current activity so the top one
// can be resumed...
+ boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving);
if (mResumedActivity != null) {
+ pausing = true;
+ startPausingLocked(userLeaving, false);
+ }
+ if (pausing) {
if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
// At this point we want to put the upcoming activity's process
// at the top of the LRU list, since we know we will be needing it
@@ -1547,7 +1321,7 @@
// happen whenever it needs to later.
mService.updateLruProcessLocked(next.app, false);
}
- startPausingLocked(userLeaving, false);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -1569,7 +1343,7 @@
if (prev != null && prev != next) {
if (!prev.waitingVisible && next != null && !next.nowVisible) {
prev.waitingVisible = true;
- mWaitingVisibleActivities.add(prev);
+ mStackSupervisor.mWaitingVisibleActivities.add(prev);
if (DEBUG_SWITCH) Slog.v(
TAG, "Resuming top, waiting visible to hide: " + prev);
} else {
@@ -1582,7 +1356,7 @@
// previous should actually be hidden depending on whether the
// new one is found to be full-screen or not.
if (prev.finishing) {
- mService.mWindowManager.setAppVisibility(prev.appToken, false);
+ mWindowManager.setAppVisibility(prev.appToken, false);
if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
+ prev + ", waitingVisible="
+ (prev != null ? prev.waitingVisible : null)
@@ -1610,95 +1384,89 @@
// We are starting up the next activity, so tell the window manager
// that the previous one will be hidden soon. This way it can know
// to ignore it when computing the desired screen orientation.
- boolean noAnim = false;
+ boolean anim = true;
if (prev != null) {
if (prev.finishing) {
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare close transition: prev=" + prev);
if (mNoAnimActivities.contains(prev)) {
- mService.mWindowManager.prepareAppTransition(
- AppTransition.TRANSIT_NONE, false);
+ anim = false;
+ mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
} else {
- mService.mWindowManager.prepareAppTransition(prev.task == next.task
+ mWindowManager.prepareAppTransition(prev.task == next.task
? AppTransition.TRANSIT_ACTIVITY_CLOSE
: AppTransition.TRANSIT_TASK_CLOSE, false);
}
- mService.mWindowManager.setAppWillBeHidden(prev.appToken);
- mService.mWindowManager.setAppVisibility(prev.appToken, false);
+ mWindowManager.setAppWillBeHidden(prev.appToken);
+ mWindowManager.setAppVisibility(prev.appToken, false);
} else {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: prev=" + prev);
+ if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: prev=" + prev);
if (mNoAnimActivities.contains(next)) {
- noAnim = true;
- mService.mWindowManager.prepareAppTransition(
- AppTransition.TRANSIT_NONE, false);
+ anim = false;
+ mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
} else {
- mService.mWindowManager.prepareAppTransition(prev.task == next.task
+ mWindowManager.prepareAppTransition(prev.task == next.task
? AppTransition.TRANSIT_ACTIVITY_OPEN
: AppTransition.TRANSIT_TASK_OPEN, false);
}
}
if (false) {
- mService.mWindowManager.setAppWillBeHidden(prev.appToken);
- mService.mWindowManager.setAppVisibility(prev.appToken, false);
+ mWindowManager.setAppWillBeHidden(prev.appToken);
+ mWindowManager.setAppVisibility(prev.appToken, false);
}
- } else if (mHistory.size() > 1) {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: no previous");
+ } else {
+ if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: no previous");
if (mNoAnimActivities.contains(next)) {
- noAnim = true;
- mService.mWindowManager.prepareAppTransition(
- AppTransition.TRANSIT_NONE, false);
+ anim = false;
+ mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
} else {
- mService.mWindowManager.prepareAppTransition(
- AppTransition.TRANSIT_ACTIVITY_OPEN, false);
+ mWindowManager.prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_OPEN, false);
}
}
- if (!noAnim) {
+ if (anim) {
next.applyOptionsLocked();
} else {
next.clearOptionsLocked();
}
+ ActivityStack lastStack = mStackSupervisor.getLastStack();
if (next.app != null && next.app.thread != null) {
if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
// This activity is now becoming visible.
- mService.mWindowManager.setAppVisibility(next.appToken, true);
+ mWindowManager.setAppVisibility(next.appToken, true);
// schedule launch ticks to collect information about slow apps.
next.startLaunchTickingLocked();
- ActivityRecord lastResumedActivity = mResumedActivity;
+ ActivityRecord lastResumedActivity =
+ lastStack == null ? null :lastStack.mResumedActivity;
ActivityState lastState = next.state;
mService.updateCpuStats();
-
+
if (DEBUG_STATES) Slog.v(TAG, "Moving to RESUMED: " + next + " (in existing)");
next.state = ActivityState.RESUMED;
mResumedActivity = next;
next.task.touchActiveTime();
- if (mMainStack) {
- mService.addRecentTaskLocked(next.task);
- }
+ mService.addRecentTaskLocked(next.task);
mService.updateLruProcessLocked(next.app, true);
updateLRUListLocked(next);
// Have the window manager re-evaluate the orientation of
// the screen based on the new activity order.
- boolean updated = false;
- if (mMainStack) {
- synchronized (mService) {
- Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
- mService.mConfiguration,
- next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- updated = mService.updateConfigurationLocked(config, next, false, false);
+ boolean notUpdated = true;
+ if (mStackSupervisor.isFrontStack(this)) {
+ Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ mService.mConfiguration,
+ next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
+ if (config != null) {
+ next.frozenBeforeDestroy = true;
}
+ notUpdated = !mService.updateConfigurationLocked(config, next, false, false);
}
- if (!updated) {
+
+ if (notUpdated) {
// The configuration update wasn't able to keep the existing
// instance of the activity, and instead started a new one.
// We should be all done, but let's just make sure our activity
@@ -1710,20 +1478,20 @@
+ ", new next: " + nextNext);
if (nextNext != next) {
// Do over!
- mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
+ mStackSupervisor.scheduleResumeTopActivities();
}
- if (mMainStack) {
- mService.setFocusedActivityLocked(next);
+ if (mStackSupervisor.reportResumedActivityLocked(next)) {
+ mNoAnimActivities.clear();
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return true;
}
- ensureActivitiesVisibleLocked(null, 0);
- mService.mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return true;
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return false;
}
-
+
try {
// Deliver all pending results.
- ArrayList a = next.results;
+ ArrayList<ResultInfo> a = next.results;
if (a != null) {
final int N = a.size();
if (!next.finishing && N > 0) {
@@ -1741,36 +1509,36 @@
EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
next.userId, System.identityHashCode(next),
next.task.taskId, next.shortComponentName);
-
+
next.sleeping = false;
- showAskCompatModeDialogLocked(next);
+ mService.showAskCompatModeDialogLocked(next);
next.app.pendingUiClean = true;
next.app.thread.scheduleResumeActivity(next.appToken,
mService.isNextTransitionForward());
-
- checkReadyForSleepLocked();
+
+ mStackSupervisor.checkReadyForSleepLocked();
} catch (Exception e) {
// Whoops, need to restart this activity!
if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to "
+ lastState + ": " + next);
next.state = lastState;
- mResumedActivity = lastResumedActivity;
+ if (lastStack != null) {
+ lastStack.mResumedActivity = lastResumedActivity;
+ }
Slog.i(TAG, "Restarting because process died: " + next);
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
- mService.mWindowManager.setAppStartingWindow(
- next.appToken, next.packageName, next.theme,
- mService.compatibilityInfoForPackageLocked(
- next.info.applicationInfo),
- next.nonLocalizedLabel,
- next.labelRes, next.icon, next.windowFlags,
- null, true);
- }
+ } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
+ mStackSupervisor.isFrontStack(lastStack)) {
+ mWindowManager.setAppStartingWindow(
+ next.appToken, next.packageName, next.theme,
+ mService.compatibilityInfoForPackageLocked(next.info.applicationInfo),
+ next.nonLocalizedLabel, next.labelRes, next.icon, next.logo,
+ next.windowFlags, null, true);
}
- startSpecificActivityLocked(next, true, false);
+ mStackSupervisor.startSpecificActivityLocked(next, true, false);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -1785,6 +1553,7 @@
Slog.w(TAG, "Exception thrown during resume of " + next, e);
requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
"resume-exception", true);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
next.stopped = false;
@@ -1795,52 +1564,54 @@
next.hasBeenLaunched = true;
} else {
if (SHOW_APP_STARTING_PREVIEW) {
- mService.mWindowManager.setAppStartingWindow(
+ mWindowManager.setAppStartingWindow(
next.appToken, next.packageName, next.theme,
mService.compatibilityInfoForPackageLocked(
next.info.applicationInfo),
next.nonLocalizedLabel,
- next.labelRes, next.icon, next.windowFlags,
+ next.labelRes, next.icon, next.logo, next.windowFlags,
null, true);
}
if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
}
- startSpecificActivityLocked(next, true, true);
+ mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
- private final void startActivityLocked(ActivityRecord r, boolean newTask,
- boolean doResume, boolean keepCurTransition, Bundle options) {
- final int NH = mHistory.size();
- int addPos = -1;
-
+ final void startActivityLocked(ActivityRecord r, boolean newTask,
+ boolean doResume, boolean keepCurTransition, Bundle options) {
+ TaskRecord rTask = r.task;
+ final int taskId = rTask.taskId;
+ if (taskForIdLocked(taskId) == null || newTask) {
+ // Last activity in task had been removed or ActivityManagerService is reusing task.
+ // Insert or replace.
+ // Might not even be in.
+ mTaskHistory.remove(rTask);
+ // Now put task at top.
+ mTaskHistory.add(rTask);
+ mWindowManager.moveTaskToTop(taskId);
+ }
+ TaskRecord task = null;
if (!newTask) {
// If starting in an existing task, find where that is...
boolean startIt = true;
- for (int i = NH-1; i >= 0; i--) {
- ActivityRecord p = mHistory.get(i);
- if (p.finishing) {
- continue;
- }
- if (p.task == r.task) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ task = mTaskHistory.get(taskNdx);
+ if (task == r.task) {
// Here it is! Now, if this is not yet visible to the
// user, then just add it without starting; it will
// get started when the user navigates back to it.
- addPos = i+1;
if (!startIt) {
- if (DEBUG_ADD_REMOVE) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos,
- here);
- }
- mHistory.add(addPos, r);
+ if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
+ + task, new RuntimeException("here").fillInStackTrace());
+ task.addActivityToTop(r);
r.putInHistory();
- mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
- r.info.screenOrientation, r.fullscreen,
+ mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
+ r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
@@ -1849,8 +1620,7 @@
return;
}
break;
- }
- if (p.fullscreen) {
+ } else if (task.numFullscreen > 0) {
startIt = false;
}
}
@@ -1858,28 +1628,26 @@
// Place a new activity at top of stack, so it is next to interact
// with the user.
- if (addPos < 0) {
- addPos = NH;
- }
-
+
// If we are not placing the new activity frontmost, we do not want
// to deliver the onUserLeaving callback to the actual frontmost
// activity
- if (addPos < NH) {
- mUserLeaving = false;
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false");
+ if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
+ mStackSupervisor.mUserLeaving = false;
+ if (DEBUG_USER_LEAVING) Slog.v(TAG,
+ "startActivity() behind front, mUserLeaving=false");
}
-
+
+ task = r.task;
+
// Slot the activity into the history stack and proceed
- if (DEBUG_ADD_REMOVE) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos, here);
- }
- mHistory.add(addPos, r);
+ if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
+ new RuntimeException("here").fillInStackTrace());
+ task.addActivityToTop(r);
+
r.putInHistory();
r.frontOfTask = newTask;
- if (NH > 0) {
+ if (!isHomeStack() || numActivities() > 0) {
// We want to show the starting preview window if we are
// switching to a new task, or the next activity's process is
// not currently running.
@@ -1894,18 +1662,17 @@
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare open transition: starting " + r);
if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mService.mWindowManager.prepareAppTransition(
- AppTransition.TRANSIT_NONE, keepCurTransition);
+ mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition);
mNoAnimActivities.add(r);
} else {
- mService.mWindowManager.prepareAppTransition(newTask
+ mWindowManager.prepareAppTransition(newTask
? AppTransition.TRANSIT_TASK_OPEN
: AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
mNoAnimActivities.remove(r);
}
r.updateOptionsLocked(options);
- mService.mWindowManager.addAppToken(
- addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen,
+ mWindowManager.addAppToken(task.mActivities.indexOf(r),
+ r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
boolean doShow = true;
if (newTask) {
@@ -1929,22 +1696,26 @@
if (prev != null) {
// We don't want to reuse the previous starting preview if:
// (1) The current activity is in a different task.
- if (prev.task != r.task) prev = null;
+ if (prev.task != r.task) {
+ prev = null;
+ }
// (2) The current activity is already displayed.
- else if (prev.nowVisible) prev = null;
+ else if (prev.nowVisible) {
+ prev = null;
+ }
}
- mService.mWindowManager.setAppStartingWindow(
+ mWindowManager.setAppStartingWindow(
r.appToken, r.packageName, r.theme,
mService.compatibilityInfoForPackageLocked(
r.info.applicationInfo), r.nonLocalizedLabel,
- r.labelRes, r.icon, r.windowFlags,
+ r.labelRes, r.icon, r.logo, r.windowFlags,
prev != null ? prev.appToken : null, showStartingIcon);
}
} else {
// If this is the first activity, don't do any fancy animations,
// because there is nothing for it to animate on top of.
- mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
- r.info.screenOrientation, r.fullscreen,
+ mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
+ r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
ActivityOptions.abort(options);
}
@@ -1953,233 +1724,68 @@
}
if (doResume) {
- resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
}
}
final void validateAppTokensLocked() {
mValidateAppTokens.clear();
- mValidateAppTokens.ensureCapacity(mHistory.size());
- for (int i=0; i<mHistory.size(); i++) {
- mValidateAppTokens.add(mHistory.get(i).appToken);
+ mValidateAppTokens.ensureCapacity(numActivities());
+ final int numTasks = mTaskHistory.size();
+ for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
+ TaskRecord task = mTaskHistory.get(taskNdx);
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ if (activities.size() == 0) {
+ continue;
+ }
+ TaskGroup group = new TaskGroup();
+ group.taskId = task.taskId;
+ mValidateAppTokens.add(group);
+ final int numActivities = activities.size();
+ for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ group.tokens.add(r.appToken);
+ }
}
- mService.mWindowManager.validateAppTokens(mValidateAppTokens);
+ mWindowManager.validateAppTokens(mStackId, mValidateAppTokens);
}
/**
* Perform a reset of the given task, if needed as part of launching it.
* Returns the new HistoryRecord at the top of the task.
*/
- private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
- ActivityRecord newActivity) {
- boolean forceReset = (newActivity.info.flags
- &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
- if (ACTIVITY_INACTIVE_RESET_TIME > 0
- && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
- if ((newActivity.info.flags
- &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
- forceReset = true;
- }
- }
-
- final TaskRecord task = taskTop.task;
-
- // We are going to move through the history list so that we can look
- // at each activity 'target' with 'below' either the interesting
- // activity immediately below it in the stack or null.
- ActivityRecord target = null;
- int targetI = 0;
- int taskTopI = -1;
- int replyChainEnd = -1;
- int lastReparentPos = -1;
+ /**
+ * Helper method for #resetTaskIfNeededLocked.
+ * We are inside of the task being reset... we'll either finish this activity, push it out
+ * for another task, or leave it as-is.
+ * @param task The task containing the Activity (taskTop) that might be reset.
+ * @param forceReset
+ * @return An ActivityOptions that needs to be processed.
+ */
+ final ActivityOptions resetTargetTaskIfNeededLocked(TaskRecord task, boolean forceReset) {
ActivityOptions topOptions = null;
- boolean canMoveOptions = true;
- for (int i=mHistory.size()-1; i>=-1; i--) {
- ActivityRecord below = i >= 0 ? mHistory.get(i) : null;
-
- if (below != null && below.finishing) {
- continue;
- }
- // Don't check any lower in the stack if we're crossing a user boundary.
- if (below != null && below.userId != taskTop.userId) {
- break;
- }
- if (target == null) {
- target = below;
- targetI = i;
- // If we were in the middle of a reply chain before this
- // task, it doesn't appear like the root of the chain wants
- // anything interesting, so drop it.
- replyChainEnd = -1;
- continue;
- }
-
- final int flags = target.info.flags;
-
- final boolean finishOnTaskLaunch =
- (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
- final boolean allowTaskReparenting =
- (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
-
- if (target.task == task) {
- // We are inside of the task being reset... we'll either
- // finish this activity, push it out for another task,
- // or leave it as-is. We only do this
- // for activities that are not the root of the task (since
- // if we finish the root, we may no longer have the task!).
- if (taskTopI < 0) {
- taskTopI = targetI;
- }
- if (below != null && below.task == task) {
- final boolean clearWhenTaskReset =
- (target.intent.getFlags()
- &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
- if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) {
- // If this activity is sending a reply to a previous
- // activity, we can't do anything with it now until
- // we reach the start of the reply chain.
- // XXX note that we are assuming the result is always
- // to the previous activity, which is almost always
- // the case but we really shouldn't count on.
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting
- && target.taskAffinity != null
- && !target.taskAffinity.equals(task.affinity)) {
- // If this activity has an affinity for another
- // task, then we need to move it out of here. We will
- // move it as far out of the way as possible, to the
- // bottom of the activity stack. This also keeps it
- // correctly ordered with any activities we previously
- // moved.
- ActivityRecord p = mHistory.get(0);
- if (target.taskAffinity != null
- && target.taskAffinity.equals(p.task.affinity)) {
- // If the activity currently at the bottom has the
- // same task affinity as the one we are moving,
- // then merge it into the same task.
- target.setTask(p.task, p.thumbHolder, false);
- if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
- + " out to bottom task " + p.task);
- } else {
- mService.mCurTask++;
- if (mService.mCurTask <= 0) {
- mService.mCurTask = 1;
- }
- target.setTask(new TaskRecord(mService.mCurTask, target.info, null),
- null, false);
- target.task.affinityIntent = target.intent;
- if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
- + " out to new task " + target.task);
- }
- mService.mWindowManager.setAppGroupId(target.appToken, task.taskId);
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- int dstPos = 0;
- ThumbnailHolder curThumbHolder = target.thumbHolder;
- boolean gotOptions = !canMoveOptions;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
- + " out to target's task " + target.task);
- p.setTask(target.task, curThumbHolder, false);
- curThumbHolder = p.thumbHolder;
- canMoveOptions = false;
- if (!gotOptions && topOptions == null) {
- topOptions = p.takeOptionsLocked();
- if (topOptions != null) {
- gotOptions = true;
- }
- }
- if (DEBUG_ADD_REMOVE) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "Removing and adding activity " + p + " to stack at "
- + dstPos, here);
- }
- mHistory.remove(srcPos);
- mHistory.add(dstPos, p);
- mService.mWindowManager.moveAppToken(dstPos, p.appToken);
- mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId);
- dstPos++;
- if (VALIDATE_TOKENS) {
- validateAppTokensLocked();
- }
- i++;
- }
- if (taskTop == p) {
- taskTop = below;
- }
- if (taskTopI == replyChainEnd) {
- taskTopI = -1;
- }
- replyChainEnd = -1;
- } else if (forceReset || finishOnTaskLaunch
- || clearWhenTaskReset) {
- // If the activity should just be removed -- either
- // because it asks for it, or the task should be
- // cleared -- then finish it and anything that is
- // part of its reply chain.
- if (clearWhenTaskReset) {
- // In this case, we want to finish this activity
- // and everything above it, so be sneaky and pretend
- // like these are all in the reply chain.
- replyChainEnd = targetI+1;
- while (replyChainEnd < mHistory.size() &&
- (mHistory.get(
- replyChainEnd)).task == task) {
- replyChainEnd++;
- }
- replyChainEnd--;
- } else if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- ActivityRecord p = null;
- boolean gotOptions = !canMoveOptions;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- canMoveOptions = false;
- if (!gotOptions && topOptions == null) {
- topOptions = p.takeOptionsLocked();
- if (topOptions != null) {
- gotOptions = true;
- }
- }
- if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset", false)) {
- replyChainEnd--;
- srcPos--;
- }
- }
- if (taskTop == p) {
- taskTop = below;
- }
- if (taskTopI == replyChainEnd) {
- taskTopI = -1;
- }
- replyChainEnd = -1;
- } else {
- // If we were in the middle of a chain, well the
- // activity that started it all doesn't want anything
- // special, so leave it all as-is.
- replyChainEnd = -1;
- }
- } else {
- // Reached the bottom of the task -- any reply chain
- // should be left as-is.
- replyChainEnd = -1;
- }
- } else if (target.resultTo != null && (below == null
- || below.task == target.task)) {
+ int replyChainEnd = -1;
+ boolean canMoveOptions = true;
+
+ // We only do this for activities that are not the root of the task (since if we finish
+ // the root, we may no longer have the task!).
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ final int numActivities = activities.size();
+ for (int i = numActivities - 1; i > 0; --i ) {
+ ActivityRecord target = activities.get(i);
+
+ final int flags = target.info.flags;
+ final boolean finishOnTaskLaunch =
+ (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
+ final boolean allowTaskReparenting =
+ (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
+ final boolean clearWhenTaskReset =
+ (target.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
+
+ if (!finishOnTaskLaunch
+ && !clearWhenTaskReset
+ && target.resultTo != null) {
// If this activity is sending a reply to a previous
// activity, we can't do anything with it now until
// we reach the start of the reply chain.
@@ -2187,14 +1793,161 @@
// to the previous activity, which is almost always
// the case but we really shouldn't count on.
if (replyChainEnd < 0) {
- replyChainEnd = targetI;
+ replyChainEnd = i;
+ }
+ } else if (!finishOnTaskLaunch
+ && !clearWhenTaskReset
+ && allowTaskReparenting
+ && target.taskAffinity != null
+ && !target.taskAffinity.equals(task.affinity)) {
+ // If this activity has an affinity for another
+ // task, then we need to move it out of here. We will
+ // move it as far out of the way as possible, to the
+ // bottom of the activity stack. This also keeps it
+ // correctly ordered with any activities we previously
+ // moved.
+ TaskRecord bottomTask = mTaskHistory.get(0);
+ ActivityRecord p = bottomTask.mActivities.get(0);
+ if (target.taskAffinity != null
+ && target.taskAffinity.equals(p.task.affinity)) {
+ // If the activity currently at the bottom has the
+ // same task affinity as the one we are moving,
+ // then merge it into the same task.
+ target.setTask(p.task, p.thumbHolder, false);
+ if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ + " out to bottom task " + p.task);
+ } else {
+ target.setTask(createTaskRecord(mStackSupervisor.getNextTaskId(), target.info,
+ null, false), null, false);
+ target.task.affinityIntent = target.intent;
+ if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ + " out to new task " + target.task);
}
- } else if (taskTopI >= 0 && allowTaskReparenting
- && task.affinity != null
- && task.affinity.equals(target.taskAffinity)) {
- // We are inside of another task... if this activity has
- // an affinity for our task, then either remove it if we are
+ final TaskRecord targetTask = target.task;
+ final int targetTaskId = targetTask.taskId;
+ mWindowManager.setAppGroupId(target.appToken, targetTaskId);
+
+ boolean noOptions = canMoveOptions;
+ final int start = replyChainEnd < 0 ? i : replyChainEnd;
+ for (int srcPos = start; srcPos >= i; --srcPos) {
+ p = activities.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+
+ ThumbnailHolder curThumbHolder = p.thumbHolder;
+ canMoveOptions = false;
+ if (noOptions && topOptions == null) {
+ topOptions = p.takeOptionsLocked();
+ if (topOptions != null) {
+ noOptions = false;
+ }
+ }
+ if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing activity " + p + " from task="
+ + task + " adding to task=" + targetTask,
+ new RuntimeException("here").fillInStackTrace());
+ if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
+ + " out to target's task " + target.task);
+ p.setTask(targetTask, curThumbHolder, false);
+ targetTask.addActivityAtBottom(p);
+
+ mWindowManager.setAppGroupId(p.appToken, targetTaskId);
+ }
+
+ mWindowManager.moveTaskToBottom(targetTaskId);
+ if (VALIDATE_TOKENS) {
+ validateAppTokensLocked();
+ }
+
+ replyChainEnd = -1;
+ } else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
+ // If the activity should just be removed -- either
+ // because it asks for it, or the task should be
+ // cleared -- then finish it and anything that is
+ // part of its reply chain.
+ int end;
+ if (clearWhenTaskReset) {
+ // In this case, we want to finish this activity
+ // and everything above it, so be sneaky and pretend
+ // like these are all in the reply chain.
+ end = numActivities - 1;
+ } else if (replyChainEnd < 0) {
+ end = i;
+ } else {
+ end = replyChainEnd;
+ }
+ boolean noOptions = canMoveOptions;
+ for (int srcPos = i; srcPos <= end; srcPos++) {
+ ActivityRecord p = activities.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ canMoveOptions = false;
+ if (noOptions && topOptions == null) {
+ topOptions = p.takeOptionsLocked();
+ if (topOptions != null) {
+ noOptions = false;
+ }
+ }
+ if (DEBUG_TASKS) Slog.w(TAG,
+ "resetTaskIntendedTask: calling finishActivity on " + p);
+ if (finishActivityLocked(p, Activity.RESULT_CANCELED, null, "reset", false)) {
+ end--;
+ srcPos--;
+ }
+ }
+ replyChainEnd = -1;
+ } else {
+ // If we were in the middle of a chain, well the
+ // activity that started it all doesn't want anything
+ // special, so leave it all as-is.
+ replyChainEnd = -1;
+ }
+ }
+
+ return topOptions;
+ }
+
+ /**
+ * Helper method for #resetTaskIfNeededLocked. Processes all of the activities in a given
+ * TaskRecord looking for an affinity with the task of resetTaskIfNeededLocked.taskTop.
+ * @param affinityTask The task we are looking for an affinity to.
+ * @param task Task that resetTaskIfNeededLocked.taskTop belongs to.
+ * @param topTaskIsHigher True if #task has already been processed by resetTaskIfNeededLocked.
+ * @param forceReset Flag passed in to resetTaskIfNeededLocked.
+ */
+ private int resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task,
+ boolean topTaskIsHigher, boolean forceReset, int taskInsertionPoint) {
+ int replyChainEnd = -1;
+ final int taskId = task.taskId;
+ final String taskAffinity = task.affinity;
+
+ final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
+ final int numActivities = activities.size();
+ // Do not operate on the root Activity.
+ for (int i = numActivities - 1; i > 0; --i) {
+ ActivityRecord target = activities.get(i);
+
+ final int flags = target.info.flags;
+ boolean finishOnTaskLaunch = (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
+ boolean allowTaskReparenting = (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
+
+ if (target.resultTo != null) {
+ // If this activity is sending a reply to a previous
+ // activity, we can't do anything with it now until
+ // we reach the start of the reply chain.
+ // XXX note that we are assuming the result is always
+ // to the previous activity, which is almost always
+ // the case but we really shouldn't count on.
+ if (replyChainEnd < 0) {
+ replyChainEnd = i;
+ }
+ } else if (topTaskIsHigher
+ && allowTaskReparenting
+ && taskAffinity != null
+ && taskAffinity.equals(target.taskAffinity)) {
+ // This activity has an affinity for our task. Either remove it if we are
// clearing or move it over to our task. Note that
// we currently punt on the case where we are resetting a
// task that is not at the top but who has activities above
@@ -2205,93 +1958,103 @@
// someone starts an activity in a new task from an activity
// in a task that is not currently on top.)
if (forceReset || finishOnTaskLaunch) {
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- ActivityRecord p = null;
- if (DEBUG_TASKS) Slog.v(TAG, "Finishing task at index "
- + targetI + " to " + replyChainEnd);
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = mHistory.get(srcPos);
+ final int start = replyChainEnd >= 0 ? replyChainEnd : i;
+ if (DEBUG_TASKS) Slog.v(TAG, "Finishing task at index " + start + " to " + i);
+ for (int srcPos = start; srcPos >= i; --srcPos) {
+ final ActivityRecord p = activities.get(srcPos);
if (p.finishing) {
continue;
}
- if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset", false)) {
- taskTopI--;
- lastReparentPos--;
- replyChainEnd--;
- srcPos--;
- }
+ finishActivityLocked(p, Activity.RESULT_CANCELED, null, "reset", false);
}
- replyChainEnd = -1;
} else {
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
+ if (taskInsertionPoint < 0) {
+ taskInsertionPoint = task.mActivities.size();
+
}
- if (DEBUG_TASKS) Slog.v(TAG, "Reparenting task at index "
- + targetI + " to " + replyChainEnd);
- for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
- ActivityRecord p = mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (lastReparentPos < 0) {
- lastReparentPos = taskTopI;
- taskTop = p;
- } else {
- lastReparentPos--;
- }
- if (DEBUG_ADD_REMOVE) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "Removing and adding activity " + p + " to stack at "
- + lastReparentPos, here);
- }
- mHistory.remove(srcPos);
+
+ final int start = replyChainEnd >= 0 ? replyChainEnd : i;
+ if (DEBUG_TASKS) Slog.v(TAG, "Reparenting from task=" + affinityTask + ":"
+ + start + "-" + i + " to task=" + task + ":" + taskInsertionPoint);
+ for (int srcPos = start; srcPos >= i; --srcPos) {
+ final ActivityRecord p = activities.get(srcPos);
p.setTask(task, null, false);
- mHistory.add(lastReparentPos, p);
- if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
- + " from " + srcPos + " to " + lastReparentPos
+ task.addActivityAtIndex(taskInsertionPoint, p);
+
+ if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + p
+ + " to stack at " + task,
+ new RuntimeException("here").fillInStackTrace());
+ if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + " from " + srcPos
+ " in to resetting task " + task);
- mService.mWindowManager.moveAppToken(lastReparentPos, p.appToken);
- mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId);
- if (VALIDATE_TOKENS) {
- validateAppTokensLocked();
- }
+ mWindowManager.setAppGroupId(p.appToken, taskId);
}
- replyChainEnd = -1;
-
+ mWindowManager.moveTaskToTop(taskId);
+ if (VALIDATE_TOKENS) {
+ validateAppTokensLocked();
+ }
+
// Now we've moved it in to place... but what if this is
// a singleTop activity and we have put it on top of another
// instance of the same activity? Then we drop the instance
// below so it remains singleTop.
if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
- for (int j=lastReparentPos-1; j>=0; j--) {
- ActivityRecord p = mHistory.get(j);
- if (p.finishing) {
- continue;
- }
+ ArrayList<ActivityRecord> taskActivities = task.mActivities;
+ int targetNdx = taskActivities.indexOf(target);
+ if (targetNdx > 0) {
+ ActivityRecord p = taskActivities.get(targetNdx - 1);
if (p.intent.getComponent().equals(target.intent.getComponent())) {
- if (finishActivityLocked(p, j,
- Activity.RESULT_CANCELED, null, "replace", false)) {
- taskTopI--;
- lastReparentPos--;
- }
+ finishActivityLocked(p, Activity.RESULT_CANCELED, null, "replace",
+ false);
}
}
}
}
- } else if (below != null && below.task != target.task) {
- // We hit the botton of a task; the reply chain can't
- // pass through it.
replyChainEnd = -1;
}
-
- target = below;
- targetI = i;
}
+ return taskInsertionPoint;
+ }
+
+ final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
+ ActivityRecord newActivity) {
+ boolean forceReset =
+ (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
+ if (ACTIVITY_INACTIVE_RESET_TIME > 0
+ && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
+ if ((newActivity.info.flags & ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
+ forceReset = true;
+ }
+ }
+
+ final TaskRecord task = taskTop.task;
+
+ /** False until we evaluate the TaskRecord associated with taskTop. Switches to true
+ * for remaining tasks. Used for later tasks to reparent to task. */
+ boolean taskFound = false;
+
+ /** If ActivityOptions are moved out and need to be aborted or moved to taskTop. */
+ ActivityOptions topOptions = null;
+
+ // Preserve the location for reparenting in the new task.
+ int reparentInsertionPoint = -1;
+
+ for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
+ final TaskRecord targetTask = mTaskHistory.get(i);
+
+ if (targetTask == task) {
+ topOptions = resetTargetTaskIfNeededLocked(task, forceReset);
+ taskFound = true;
+ } else {
+ reparentInsertionPoint = resetAffinityTaskIfNeededLocked(targetTask, task,
+ taskFound, forceReset, reparentInsertionPoint);
+ }
+ }
+
+ int taskNdx = mTaskHistory.indexOf(task);
+ do {
+ taskTop = mTaskHistory.get(taskNdx--).getTopActivity();
+ } while (taskTop == null && taskNdx >= 0);
if (topOptions != null) {
// If we got some ActivityOptions from an activity on top that
@@ -2305,1068 +2068,6 @@
return taskTop;
}
-
- /**
- * Perform clear operation as requested by
- * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
- * stack to the given task, then look for
- * an instance of that activity in the stack and, if found, finish all
- * activities on top of it and return the instance.
- *
- * @param newR Description of the new activity being started.
- * @return Returns the old activity that should be continued to be used,
- * or null if none was found.
- */
- private final ActivityRecord performClearTaskLocked(int taskId,
- ActivityRecord newR, int launchFlags) {
- int i = mHistory.size();
-
- // First find the requested task.
- while (i > 0) {
- i--;
- ActivityRecord r = mHistory.get(i);
- if (r.task.taskId == taskId) {
- i++;
- break;
- }
- }
-
- // Now clear it.
- while (i > 0) {
- i--;
- ActivityRecord r = mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (r.task.taskId != taskId) {
- return null;
- }
- if (r.realActivity.equals(newR.realActivity)) {
- // Here it is! Now finish everything in front...
- ActivityRecord ret = r;
- while (i < (mHistory.size()-1)) {
- i++;
- r = mHistory.get(i);
- if (r.task.taskId != taskId) {
- break;
- }
- if (r.finishing) {
- continue;
- }
- ActivityOptions opts = r.takeOptionsLocked();
- if (opts != null) {
- ret.updateOptionsLocked(opts);
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear", false)) {
- i--;
- }
- }
-
- // Finally, if this is a normal launch mode (that is, not
- // expecting onNewIntent()), then we will finish the current
- // instance of the activity so a new fresh one can be started.
- if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
- && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
- if (!ret.finishing) {
- int index = indexOfTokenLocked(ret.appToken);
- if (index >= 0) {
- finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
- null, "clear", false);
- }
- return null;
- }
- }
-
- return ret;
- }
- }
-
- return null;
- }
-
- /**
- * Completely remove all activities associated with an existing
- * task starting at a specified index.
- */
- private final void performClearTaskAtIndexLocked(int taskId, int i) {
- while (i < mHistory.size()) {
- ActivityRecord r = mHistory.get(i);
- if (r.task.taskId != taskId) {
- // Whoops hit the end.
- return;
- }
- if (r.finishing) {
- i++;
- continue;
- }
- if (!finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear", false)) {
- i++;
- }
- }
- }
-
- /**
- * Completely remove all activities associated with an existing task.
- */
- private final void performClearTaskLocked(int taskId) {
- int i = mHistory.size();
-
- // First find the requested task.
- while (i > 0) {
- i--;
- ActivityRecord r = mHistory.get(i);
- if (r.task.taskId == taskId) {
- i++;
- break;
- }
- }
-
- // Now find the start and clear it.
- while (i > 0) {
- i--;
- ActivityRecord r = mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (r.task.taskId != taskId) {
- // We hit the bottom. Now finish it all...
- performClearTaskAtIndexLocked(taskId, i+1);
- return;
- }
- }
- }
-
- /**
- * Find the activity in the history stack within the given task. Returns
- * the index within the history at which it's found, or < 0 if not found.
- */
- private final int findActivityInHistoryLocked(ActivityRecord r, int task) {
- int i = mHistory.size();
- while (i > 0) {
- i--;
- ActivityRecord candidate = mHistory.get(i);
- if (candidate.finishing) {
- continue;
- }
- if (candidate.task.taskId != task) {
- break;
- }
- if (candidate.realActivity.equals(r.realActivity)) {
- return i;
- }
- }
-
- return -1;
- }
-
- /**
- * Reorder the history stack so that the activity at the given index is
- * brought to the front.
- */
- private final ActivityRecord moveActivityToFrontLocked(int where) {
- ActivityRecord newTop = mHistory.remove(where);
- int top = mHistory.size();
- ActivityRecord oldTop = mHistory.get(top-1);
- if (DEBUG_ADD_REMOVE) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "Removing and adding activity " + newTop + " to stack at "
- + top, here);
- }
- mHistory.add(top, newTop);
- oldTop.frontOfTask = false;
- newTop.frontOfTask = true;
- return newTop;
- }
-
- final int startActivityLocked(IApplicationThread caller,
- Intent intent, String resolvedType, ActivityInfo aInfo, IBinder resultTo,
- String resultWho, int requestCode,
- int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
- boolean componentSpecified, ActivityRecord[] outActivity) {
-
- int err = ActivityManager.START_SUCCESS;
-
- ProcessRecord callerApp = null;
-
- if (caller != null) {
- callerApp = mService.getRecordForAppLocked(caller);
- if (callerApp != null) {
- callingPid = callerApp.pid;
- callingUid = callerApp.info.uid;
- } else {
- Slog.w(TAG, "Unable to find app for caller " + caller
- + " (pid=" + callingPid + ") when starting: "
- + intent.toString());
- err = ActivityManager.START_PERMISSION_DENIED;
- }
- }
-
- if (err == ActivityManager.START_SUCCESS) {
- final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
- Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
- + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
- }
-
- ActivityRecord sourceRecord = null;
- ActivityRecord resultRecord = null;
- if (resultTo != null) {
- int index = indexOfTokenLocked(resultTo);
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Will send result to " + resultTo + " (index " + index + ")");
- if (index >= 0) {
- sourceRecord = mHistory.get(index);
- if (requestCode >= 0 && !sourceRecord.finishing) {
- resultRecord = sourceRecord;
- }
- }
- }
-
- int launchFlags = intent.getFlags();
-
- if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
- && sourceRecord != null) {
- // Transfer the result target from the source activity to the new
- // one being started, including any failures.
- if (requestCode >= 0) {
- ActivityOptions.abort(options);
- return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
- }
- resultRecord = sourceRecord.resultTo;
- resultWho = sourceRecord.resultWho;
- requestCode = sourceRecord.requestCode;
- sourceRecord.resultTo = null;
- if (resultRecord != null) {
- resultRecord.removeResultsLocked(
- sourceRecord, resultWho, requestCode);
- }
- }
-
- if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
- // We couldn't find a class that can handle the given Intent.
- // That's the end of that!
- err = ActivityManager.START_INTENT_NOT_RESOLVED;
- }
-
- if (err == ActivityManager.START_SUCCESS && aInfo == null) {
- // We couldn't find the specific class specified in the Intent.
- // Also the end of the line.
- err = ActivityManager.START_CLASS_NOT_FOUND;
- }
-
- if (err != ActivityManager.START_SUCCESS) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- mDismissKeyguardOnNextActivity = false;
- ActivityOptions.abort(options);
- return err;
- }
-
- final int startAnyPerm = mService.checkPermission(
- START_ANY_ACTIVITY, callingPid, callingUid);
- final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
- callingUid, aInfo.applicationInfo.uid, aInfo.exported);
- if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- mDismissKeyguardOnNextActivity = false;
- String msg;
- if (!aInfo.exported) {
- msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " not exported from uid " + aInfo.applicationInfo.uid;
- } else {
- msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires " + aInfo.permission;
- }
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
- callerApp==null?null:callerApp.info, callingUid, callingPid, resolvedType, aInfo);
-
- if (mMainStack) {
- if (mService.mController != null) {
- try {
- // The Intent we give to the watcher has the extra data
- // stripped off, since it can contain private information.
- Intent watchIntent = intent.cloneFilter();
- abort |= !mService.mController.activityStarting(watchIntent,
- aInfo.applicationInfo.packageName);
- } catch (RemoteException e) {
- mService.mController = null;
- }
- }
- }
-
- if (abort) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- // We pretend to the caller that it was really started, but
- // they will just get a cancel result.
- mDismissKeyguardOnNextActivity = false;
- ActivityOptions.abort(options);
- return ActivityManager.START_SUCCESS;
- }
-
- ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid, callingPackage,
- intent, resolvedType, aInfo, mService.mConfiguration,
- resultRecord, resultWho, requestCode, componentSpecified);
- if (outActivity != null) {
- outActivity[0] = r;
- }
-
- if (mMainStack) {
- if (mResumedActivity == null
- || mResumedActivity.info.applicationInfo.uid != callingUid) {
- if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
- PendingActivityLaunch pal = new PendingActivityLaunch();
- pal.r = r;
- pal.sourceRecord = sourceRecord;
- pal.startFlags = startFlags;
- mService.mPendingActivityLaunches.add(pal);
- mDismissKeyguardOnNextActivity = false;
- ActivityOptions.abort(options);
- return ActivityManager.START_SWITCHES_CANCELED;
- }
- }
-
- if (mService.mDidAppSwitch) {
- // This is the second allowed switch since we stopped switches,
- // so now just generally allow switches. Use case: user presses
- // home (switches disabled, switch to home, mDidAppSwitch now true);
- // user taps a home icon (coming from home so allowed, we hit here
- // and now allow anyone to switch again).
- mService.mAppSwitchesAllowedTime = 0;
- } else {
- mService.mDidAppSwitch = true;
- }
-
- mService.doPendingActivityLaunchesLocked(false);
- }
-
- err = startActivityUncheckedLocked(r, sourceRecord,
- startFlags, true, options);
- if (mDismissKeyguardOnNextActivity && mPausingActivity == null) {
- // Someone asked to have the keyguard dismissed on the next
- // activity start, but we are not actually doing an activity
- // switch... just dismiss the keyguard now, because we
- // probably want to see whatever is behind it.
- mDismissKeyguardOnNextActivity = false;
- mService.mWindowManager.dismissKeyguard();
- }
- return err;
- }
-
- final void moveHomeToFrontFromLaunchLocked(int launchFlags) {
- 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.
- moveHomeToFrontLocked();
- }
- }
-
- final int startActivityUncheckedLocked(ActivityRecord r,
- ActivityRecord sourceRecord, int startFlags, boolean doResume,
- Bundle options) {
- final Intent intent = r.intent;
- final int callingUid = r.launchedFromUid;
-
- int launchFlags = intent.getFlags();
-
- // 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;
- if (DEBUG_USER_LEAVING) Slog.v(TAG,
- "startActivity() => mUserLeaving=" + mUserLeaving);
-
- // If the caller has asked not to resume at this point, we make note
- // of this in the record so that we can skip it when trying to find
- // the top running activity.
- if (!doResume) {
- r.delayedResume = true;
- }
-
- 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
- // a special case, if we do not know the caller then we count the
- // current top activity as the caller.
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
- ActivityRecord checkedCaller = sourceRecord;
- if (checkedCaller == null) {
- checkedCaller = topRunningNonDelayedActivityLocked(notTop);
- }
- if (!checkedCaller.realActivity.equals(r.realActivity)) {
- // Caller is not the same as launcher, so always needed.
- startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
- }
- }
-
- if (sourceRecord == null) {
- // This activity is not being started from another... in this
- // case we -always- start a new task.
- if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
- + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // The original activity who is starting us is running as a single
- // instance... this new activity it is starting must go on its
- // own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
-
- if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // For whatever reason this activity is being launched into a new
- // task... yet the caller has requested a result back. Well, that
- // is pretty messed up, so instead immediately send back a cancel
- // and let the new task continue launched as normal without a
- // dependency on its originator.
- Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
- sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- r.resultTo = null;
- }
-
- boolean addingToTask = false;
- boolean movedHome = false;
- TaskRecord reuseTask = null;
- if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // If bring to front is requested, and no result is requested, and
- // we can find a task that was started with this same
- // component, then instead of launching bring that one to the front.
- if (r.resultTo == null) {
- // See if there is a task to bring to the front. If this is
- // a SINGLE_INSTANCE activity, there can be one and only one
- // instance of it in the history, and it is always in its own
- // unique task, so we do a special search.
- ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
- ? findTaskLocked(intent, r.info)
- : findActivityLocked(intent, r.info);
- if (taskTop != null) {
- if (taskTop.task.intent == null) {
- // This task was started because of movement of
- // the activity based on affinity... now that we
- // are actually launching it, we can assign the
- // base intent.
- taskTop.task.setIntent(intent, r.info);
- }
- // If the target task is not in the front, then we need
- // to bring it to the front... except... well, with
- // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
- // to have the same behavior as if a new instance was
- // being started, which means not bringing it to the front
- // if the caller is not itself in the front.
- ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
- if (curTop != null && curTop.task != taskTop.task) {
- r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- boolean callerAtFront = sourceRecord == null
- || curTop.task == sourceRecord.task;
- if (callerAtFront) {
- // We really do want to push this one into the
- // user's face, right now.
- movedHome = true;
- moveHomeToFrontFromLaunchLocked(launchFlags);
- moveTaskToFrontLocked(taskTop.task, r, options);
- options = null;
- }
- }
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- taskTop = resetTaskIfNeededLocked(taskTop, r);
- }
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it! And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null, options);
- } else {
- ActivityOptions.abort(options);
- }
- return ActivityManager.START_RETURN_INTENT_TO_CALLER;
- }
- if ((launchFlags &
- (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
- == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
- // The caller has requested to completely replace any
- // existing task with its new activity. Well that should
- // not be too hard...
- reuseTask = taskTop.task;
- performClearTaskLocked(taskTop.task.taskId);
- reuseTask.setIntent(r.intent, r.info);
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // In this situation we want to remove all activities
- // from the task up to the one being started. In most
- // cases this means we are resetting the task to its
- // initial state.
- ActivityRecord top = performClearTaskLocked(
- taskTop.task.taskId, r, launchFlags);
- if (top != null) {
- if (top.frontOfTask) {
- // Activity aliases may mean we use different
- // intents for the top activity, so make sure
- // the task now has the identity of the new
- // intent.
- top.task.setIntent(r.intent, r.info);
- }
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- top.deliverNewIntentLocked(callingUid, r.intent);
- } else {
- // A special case: we need to
- // start the activity because it is not currently
- // running, and the caller has asked to clear the
- // current task to have this activity at the top.
- addingToTask = true;
- // Now pretend like this activity is being started
- // by the top of its task, so it is put in the
- // right place.
- sourceRecord = taskTop;
- }
- } else if (r.realActivity.equals(taskTop.task.realActivity)) {
- // In this case the top activity on the task is the
- // same as the one being launched, so we take that
- // as a request to bring the task to the foreground.
- // If the top activity in the task is the root
- // activity, deliver this new intent to it if it
- // desires.
- if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
- && taskTop.realActivity.equals(r.realActivity)) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
- if (taskTop.frontOfTask) {
- taskTop.task.setIntent(r.intent, r.info);
- }
- taskTop.deliverNewIntentLocked(callingUid, r.intent);
- } else if (!r.intent.filterEquals(taskTop.task.intent)) {
- // In this case we are launching the root activity
- // of the task, but with a different intent. We
- // should start a new instance on top.
- addingToTask = true;
- sourceRecord = taskTop;
- }
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
- // In this case an activity is being launched in to an
- // existing task, without resetting that task. This
- // is typically the situation of launching an activity
- // from a notification or shortcut. We want to place
- // the new activity on top of the current task.
- addingToTask = true;
- sourceRecord = taskTop;
- } else if (!taskTop.task.rootWasReset) {
- // In this case we are launching in to an existing task
- // that has not yet been started from its front door.
- // The current task has been brought to the front.
- // Ideally, we'd probably like to place this new task
- // at the bottom of its stack, but that's a little hard
- // to do with the current organization of the code so
- // for now we'll just drop it.
- taskTop.task.setIntent(r.intent, r.info);
- }
- if (!addingToTask && reuseTask == null) {
- // We didn't do anything... but it was needed (a.k.a., client
- // don't use that intent!) And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null, options);
- } else {
- ActivityOptions.abort(options);
- }
- return ActivityManager.START_TASK_TO_FRONT;
- }
- }
- }
- }
-
- //String uri = r.intent.toURI();
- //Intent intent2 = new Intent(uri);
- //Slog.i(TAG, "Given intent: " + r.intent);
- //Slog.i(TAG, "URI is: " + uri);
- //Slog.i(TAG, "To intent: " + intent2);
-
- if (r.packageName != null) {
- // If the activity being launched is the same as the one currently
- // at the top, then we need to check if it should only be launched
- // once.
- ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
- if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
- if (top.app != null && top.app.thread != null) {
- if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- ActivityOptions.abort(options);
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it!
- return ActivityManager.START_RETURN_INTENT_TO_CALLER;
- }
- top.deliverNewIntentLocked(callingUid, r.intent);
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
- }
- }
- }
-
- } else {
- if (r.resultTo != null) {
- sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- }
- ActivityOptions.abort(options);
- return ActivityManager.START_CLASS_NOT_FOUND;
- }
-
- boolean newTask = false;
- boolean keepCurTransition = false;
-
- // Should this be considered a new task?
- if (r.resultTo == null && !addingToTask
- && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- if (reuseTask == null) {
- // todo: should do better management of integers.
- mService.mCurTask++;
- if (mService.mCurTask <= 0) {
- mService.mCurTask = 1;
- }
- r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
- 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) {
- moveHomeToFrontFromLaunchLocked(launchFlags);
- }
-
- } else if (sourceRecord != null) {
- if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- // In this case, we are adding the activity to an existing
- // task, but the caller has asked to clear that task if the
- // activity is already running.
- ActivityRecord top = performClearTaskLocked(
- sourceRecord.task.taskId, r, launchFlags);
- keepCurTransition = true;
- if (top != null) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- top.deliverNewIntentLocked(callingUid, r.intent);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- ActivityOptions.abort(options);
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
- } else if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
- // In this case, we are launching an activity in our own task
- // that may already be running somewhere in the history, and
- // we want to shuffle it to the front of the stack if so.
- int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
- if (where >= 0) {
- ActivityRecord top = moveActivityToFrontLocked(where);
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- top.updateOptionsLocked(options);
- top.deliverNewIntentLocked(callingUid, r.intent);
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
- }
- // An existing activity is starting this new activity, so we want
- // to keep the new one in the same task as the one that is starting
- // it.
- r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in existing task " + r.task);
-
- } else {
- // 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.
- final int N = mHistory.size();
- ActivityRecord prev =
- N > 0 ? mHistory.get(N-1) : null;
- r.setTask(prev != null
- ? prev.task
- : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new guessed " + r.task);
- }
-
- mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- intent, r.getUriPermissionsLocked());
-
- if (newTask) {
- EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
- }
- logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
- startActivityLocked(r, newTask, doResume, keepCurTransition, options);
- return ActivityManager.START_SUCCESS;
- }
-
- ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
- String profileFile, ParcelFileDescriptor profileFd, int userId) {
- // Collect information about the target of the Intent.
- ActivityInfo aInfo;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
- }
-
- if (aInfo != null) {
- // Store the found target back into the intent, because now that
- // we have it we never want to do this again. For example, if the
- // user navigates back to this point in the history, we should
- // always restart the exact same activity.
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
-
- // Don't debug things in the system process
- if ((startFlags&ActivityManager.START_FLAG_DEBUG) != 0) {
- if (!aInfo.processName.equals("system")) {
- mService.setDebugApp(aInfo.processName, true, false);
- }
- }
-
- if ((startFlags&ActivityManager.START_FLAG_OPENGL_TRACES) != 0) {
- if (!aInfo.processName.equals("system")) {
- mService.setOpenGlTraceApp(aInfo.applicationInfo, aInfo.processName);
- }
- }
-
- if (profileFile != null) {
- if (!aInfo.processName.equals("system")) {
- mService.setProfileApp(aInfo.applicationInfo, aInfo.processName,
- profileFile, profileFd,
- (startFlags&ActivityManager.START_FLAG_AUTO_STOP_PROFILER) != 0);
- }
- }
- }
- return aInfo;
- }
-
- final int startActivityMayWait(IApplicationThread caller, int callingUid,
- String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, String profileFile,
- ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
- Bundle options, int userId) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
- boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
-
- // Collect information about the target of the Intent.
- ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
- profileFile, profileFd, userId);
-
- synchronized (mService) {
- int callingPid;
- if (callingUid >= 0) {
- callingPid = -1;
- } else if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
- } else {
- callingPid = callingUid = -1;
- }
-
- mConfigWillChange = config != null
- && mService.mConfiguration.diff(config) != 0;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Starting activity when config will change = " + mConfigWillChange);
-
- final long origId = Binder.clearCallingIdentity();
-
- if (mMainStack && aInfo != null &&
- (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
- // This may be a heavy-weight process! Check to see if we already
- // have another, different heavy-weight process running.
- if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
- if (mService.mHeavyWeightProcess != null &&
- (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
- !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
- int realCallingPid = callingPid;
- int realCallingUid = callingUid;
- if (caller != null) {
- ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
- if (callerApp != null) {
- realCallingPid = callerApp.pid;
- realCallingUid = callerApp.info.uid;
- } else {
- Slog.w(TAG, "Unable to find app for caller " + caller
- + " (pid=" + realCallingPid + ") when starting: "
- + intent.toString());
- ActivityOptions.abort(options);
- return ActivityManager.START_PERMISSION_DENIED;
- }
- }
-
- IIntentSender target = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, userId, null, null, 0, new Intent[] { intent },
- new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
- | PendingIntent.FLAG_ONE_SHOT, null);
-
- Intent newIntent = new Intent();
- if (requestCode >= 0) {
- // Caller is requesting a result.
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
- }
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
- new IntentSender(target));
- if (mService.mHeavyWeightProcess.activities.size() > 0) {
- ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
- hist.packageName);
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
- hist.task.taskId);
- }
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
- aInfo.packageName);
- newIntent.setFlags(intent.getFlags());
- newIntent.setClassName("android",
- HeavyWeightSwitcherActivity.class.getName());
- intent = newIntent;
- resolvedType = null;
- caller = null;
- callingUid = Binder.getCallingUid();
- callingPid = Binder.getCallingPid();
- componentSpecified = true;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, null,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- aInfo = mService.getActivityInfoForUser(aInfo, userId);
- } catch (RemoteException e) {
- aInfo = null;
- }
- }
- }
- }
-
- int res = startActivityLocked(caller, intent, resolvedType,
- aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,
- callingPackage, startFlags, options, componentSpecified, null);
-
- if (mConfigWillChange && mMainStack) {
- // If the caller also wants to switch to a new configuration,
- // do so now. This allows a clean switch, as we are waiting
- // for the current activity to pause (so we will not destroy
- // it), and have not yet started the next activity.
- mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
- "updateConfiguration()");
- mConfigWillChange = false;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Updating to new configuration after starting activity.");
- mService.updateConfigurationLocked(config, null, false, false);
- }
-
- Binder.restoreCallingIdentity(origId);
-
- if (outResult != null) {
- outResult.result = res;
- if (res == ActivityManager.START_SUCCESS) {
- mWaitingActivityLaunched.add(outResult);
- do {
- try {
- mService.wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- } else if (res == ActivityManager.START_TASK_TO_FRONT) {
- ActivityRecord r = this.topRunningActivityLocked(null);
- if (r.nowVisible) {
- outResult.timeout = false;
- outResult.who = new ComponentName(r.info.packageName, r.info.name);
- outResult.totalTime = 0;
- outResult.thisTime = 0;
- } else {
- outResult.thisTime = SystemClock.uptimeMillis();
- mWaitingActivityVisible.add(outResult);
- do {
- try {
- mService.wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- }
- }
- }
-
- return res;
- }
- }
-
- final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options, int userId) {
- if (intents == null) {
- throw new NullPointerException("intents is null");
- }
- if (resolvedTypes == null) {
- throw new NullPointerException("resolvedTypes is null");
- }
- if (intents.length != resolvedTypes.length) {
- throw new IllegalArgumentException("intents are length different than resolvedTypes");
- }
-
- ActivityRecord[] outActivity = new ActivityRecord[1];
-
- int callingPid;
- if (callingUid >= 0) {
- callingPid = -1;
- } else if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
- } else {
- callingPid = callingUid = -1;
- }
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mService) {
-
- for (int i=0; i<intents.length; i++) {
- Intent intent = intents[i];
- if (intent == null) {
- continue;
- }
-
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
-
- // Collect information about the target of the Intent.
- ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i],
- 0, null, null, userId);
- // TODO: New, check if this is correct
- aInfo = mService.getActivityInfoForUser(aInfo, userId);
-
- if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
- & ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
- throw new IllegalArgumentException(
- "FLAG_CANT_SAVE_STATE not supported here");
- }
-
- Bundle theseOptions;
- if (options != null && i == intents.length-1) {
- theseOptions = options;
- } else {
- theseOptions = null;
- }
- int res = startActivityLocked(caller, intent, resolvedTypes[i],
- aInfo, resultTo, null, -1, callingPid, callingUid, callingPackage,
- 0, theseOptions, componentSpecified, outActivity);
- if (res < 0) {
- return res;
- }
-
- resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
-
- return ActivityManager.START_SUCCESS;
- }
-
- void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
- long thisTime, long totalTime) {
- for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
- WaitResult w = mWaitingActivityLaunched.get(i);
- w.timeout = timeout;
- if (r != null) {
- w.who = new ComponentName(r.info.packageName, r.info.name);
- }
- w.thisTime = thisTime;
- w.totalTime = totalTime;
- }
- mService.notifyAll();
- }
-
- void reportActivityVisibleLocked(ActivityRecord r) {
- for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
- WaitResult w = mWaitingActivityVisible.get(i);
- w.timeout = false;
- if (r != null) {
- w.who = new ComponentName(r.info.packageName, r.info.name);
- }
- w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
- w.thisTime = w.totalTime;
- }
- mService.notifyAll();
-
- if (mDismissKeyguardOnNextActivity) {
- mDismissKeyguardOnNextActivity = false;
- mService.mWindowManager.dismissKeyguard();
- }
- }
void sendActivityResultLocked(int callingUid, ActivityRecord r,
String resultWho, int requestCode, int resultCode, Intent data) {
@@ -3394,7 +2095,7 @@
r.addResultLocked(null, resultWho, requestCode, resultCode, data);
}
- private final void stopActivityLocked(ActivityRecord r) {
+ final void stopActivityLocked(ActivityRecord r) {
if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r);
if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
@@ -3413,7 +2114,7 @@
}
if (r.app != null && r.app.thread != null) {
- if (mMainStack) {
+ if (mStackSupervisor.isFrontStack(this)) {
if (mService.mFocusedActivity == r) {
mService.setFocusedActivityLocked(topRunningActivityLocked(null));
}
@@ -3427,14 +2128,13 @@
if (DEBUG_VISBILITY) Slog.v(
TAG, "Stopping visible=" + r.visible + " for " + r);
if (!r.visible) {
- mService.mWindowManager.setAppVisibility(r.appToken, false);
+ mWindowManager.setAppVisibility(r.appToken, false);
}
r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
- if (mService.isSleeping()) {
+ if (mService.isSleepingOrShuttingDown()) {
r.setSleeping(true);
}
- Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG);
- msg.obj = r;
+ Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
} catch (Exception e) {
// Maybe just ignore exceptions here... if the process
@@ -3451,217 +2151,6 @@
}
}
}
-
- final ArrayList<ActivityRecord> processStoppingActivitiesLocked(
- boolean remove) {
- int N = mStoppingActivities.size();
- if (N <= 0) return null;
-
- ArrayList<ActivityRecord> stops = null;
-
- final boolean nowVisible = mResumedActivity != null
- && mResumedActivity.nowVisible
- && !mResumedActivity.waitingVisible;
- for (int i=0; i<N; i++) {
- ActivityRecord s = mStoppingActivities.get(i);
- if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
- + nowVisible + " waitingVisible=" + s.waitingVisible
- + " finishing=" + s.finishing);
- if (s.waitingVisible && nowVisible) {
- mWaitingVisibleActivities.remove(s);
- s.waitingVisible = false;
- if (s.finishing) {
- // If this activity is finishing, it is sitting on top of
- // everyone else but we now know it is no longer needed...
- // so get rid of it. Otherwise, we need to go through the
- // normal flow and hide it once we determine that it is
- // hidden by the activities in front of it.
- if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
- mService.mWindowManager.setAppVisibility(s.appToken, false);
- }
- }
- if ((!s.waitingVisible || mService.isSleeping()) && remove) {
- if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
- if (stops == null) {
- stops = new ArrayList<ActivityRecord>();
- }
- stops.add(s);
- mStoppingActivities.remove(i);
- N--;
- i--;
- }
- }
-
- return stops;
- }
-
- final void scheduleIdleLocked() {
- Message msg = Message.obtain();
- msg.what = IDLE_NOW_MSG;
- mHandler.sendMessage(msg);
- }
-
- final ActivityRecord activityIdleInternal(IBinder token, boolean fromTimeout,
- Configuration config) {
- if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
-
- ActivityRecord res = null;
-
- ArrayList<ActivityRecord> stops = null;
- ArrayList<ActivityRecord> finishes = null;
- ArrayList<ActivityRecord> thumbnails = null;
- ArrayList<UserStartedState> startingUsers = null;
- int NS = 0;
- int NF = 0;
- int NT = 0;
- IApplicationThread sendThumbnail = null;
- boolean booting = false;
- boolean enableScreen = false;
- boolean activityRemoved = false;
-
- synchronized (mService) {
- ActivityRecord r = ActivityRecord.forToken(token);
- if (r != null) {
- mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
- r.finishLaunchTickingLocked();
- }
-
- // Get the activity record.
- int index = indexOfActivityLocked(r);
- if (index >= 0) {
- res = r;
-
- if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
- }
-
- // This is a hack to semi-deal with a race condition
- // in the client where it can be constructed with a
- // newer configuration from when we asked it to launch.
- // We'll update with whatever configuration it now says
- // it used to launch.
- if (config != null) {
- r.configuration = config;
- }
-
- // No longer need to keep the device awake.
- if (mResumedActivity == r && mLaunchingActivity.isHeld()) {
- mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- mLaunchingActivity.release();
- }
-
- // We are now idle. If someone is waiting for a thumbnail from
- // us, we can now deliver.
- r.idle = true;
- mService.scheduleAppGcsLocked();
- if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
- sendThumbnail = r.app.thread;
- r.thumbnailNeeded = false;
- }
-
- // If this activity is fullscreen, set up to hide those under it.
-
- if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r);
- ensureActivitiesVisibleLocked(null, 0);
-
- //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
- if (mMainStack) {
- if (!mService.mBooted) {
- mService.mBooted = true;
- enableScreen = true;
- }
- }
-
- } else if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
- }
-
- // Atomically retrieve all of the other things to do.
- stops = processStoppingActivitiesLocked(true);
- NS = stops != null ? stops.size() : 0;
- if ((NF=mFinishingActivities.size()) > 0) {
- finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
- mFinishingActivities.clear();
- }
- if ((NT=mService.mCancelledThumbnails.size()) > 0) {
- thumbnails = new ArrayList<ActivityRecord>(mService.mCancelledThumbnails);
- mService.mCancelledThumbnails.clear();
- }
-
- if (mMainStack) {
- booting = mService.mBooting;
- mService.mBooting = false;
- }
- if (mStartingUsers.size() > 0) {
- startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
- mStartingUsers.clear();
- }
- }
-
- int i;
-
- // Send thumbnail if requested.
- if (sendThumbnail != null) {
- try {
- sendThumbnail.requestThumbnail(token);
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
- mService.sendPendingThumbnail(null, token, null, null, true);
- }
- }
-
- // Stop any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (i=0; i<NS; i++) {
- ActivityRecord r = (ActivityRecord)stops.get(i);
- synchronized (mService) {
- if (r.finishing) {
- finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
- } else {
- stopActivityLocked(r);
- }
- }
- }
-
- // Finish any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (i=0; i<NF; i++) {
- ActivityRecord r = (ActivityRecord)finishes.get(i);
- synchronized (mService) {
- activityRemoved = destroyActivityLocked(r, true, false, "finish-idle");
- }
- }
-
- // Report back to any thumbnail receivers.
- for (i=0; i<NT; i++) {
- ActivityRecord r = (ActivityRecord)thumbnails.get(i);
- mService.sendPendingThumbnail(r, null, null, null, true);
- }
-
- if (booting) {
- mService.finishBooting();
- } else if (startingUsers != null) {
- for (i=0; i<startingUsers.size(); i++) {
- mService.finishUserSwitch(startingUsers.get(i));
- }
- }
-
- mService.trimApplications();
- //dump();
- //mWindowManager.dump();
-
- if (enableScreen) {
- mService.enableScreenAfterBoot();
- }
-
- if (activityRemoved) {
- synchronized (mService) {
- resumeTopActivityLocked(null);
- }
- }
-
- return res;
- }
/**
* @return Returns true if the activity is being finished, false if for
@@ -3669,63 +2158,82 @@
*/
final boolean requestFinishActivityLocked(IBinder token, int resultCode,
Intent resultData, String reason, boolean oomAdj) {
- int index = indexOfTokenLocked(token);
+ ActivityRecord r = isInStackLocked(token);
if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(
- TAG, "Finishing activity @" + index + ": token=" + token
+ TAG, "Finishing activity token=" + token + " r="
+ ", result=" + resultCode + ", data=" + resultData
+ ", reason=" + reason);
- if (index < 0) {
+ if (r == null) {
return false;
}
- ActivityRecord r = mHistory.get(index);
- finishActivityLocked(r, index, resultCode, resultData, reason, oomAdj);
+ finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
return true;
}
- final void finishSubActivityLocked(IBinder token, String resultWho, int requestCode) {
- ActivityRecord self = isInStackLocked(token);
- if (self == null) {
- return;
- }
-
- int i;
- for (i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (r.resultTo == self && r.requestCode == requestCode) {
- if ((r.resultWho == null && resultWho == null) ||
- (r.resultWho != null && r.resultWho.equals(resultWho))) {
- finishActivityLocked(r, i,
- Activity.RESULT_CANCELED, null, "request-sub", false);
+ final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = activities.get(activityNdx);
+ if (r.resultTo == self && r.requestCode == requestCode) {
+ if ((r.resultWho == null && resultWho == null) ||
+ (r.resultWho != null && r.resultWho.equals(resultWho))) {
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, "request-sub",
+ false);
+ }
}
}
}
mService.updateOomAdjLocked();
}
- final boolean finishActivityAffinityLocked(IBinder token) {
- int index = indexOfTokenLocked(token);
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Finishing activity affinity @" + index + ": token=" + token);
- if (index < 0) {
- return false;
+ final void finishTopRunningActivityLocked(ProcessRecord app) {
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null && r.app == app) {
+ // If the top running activity is from this crashing
+ // process, then terminate it to avoid getting in a loop.
+ Slog.w(TAG, " Force finishing activity "
+ + r.intent.getComponent().flattenToShortString());
+ int taskNdx = mTaskHistory.indexOf(r.task);
+ int activityNdx = r.task.mActivities.indexOf(r);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, "crashed", false);
+ // Also terminate any activities below it that aren't yet
+ // stopped, to avoid a situation where one will get
+ // re-start our crashing activity once it gets resumed again.
+ --activityNdx;
+ if (activityNdx < 0) {
+ do {
+ --taskNdx;
+ if (taskNdx < 0) {
+ break;
+ }
+ activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
+ } while (activityNdx < 0);
+ }
+ if (activityNdx >= 0) {
+ r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
+ if (r.state == ActivityState.RESUMED
+ || r.state == ActivityState.PAUSING
+ || r.state == ActivityState.PAUSED) {
+ if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
+ Slog.w(TAG, " Force finishing activity "
+ + r.intent.getComponent().flattenToShortString());
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, "crashed", false);
+ }
+ }
+ }
}
- ActivityRecord r = mHistory.get(index);
+ }
- while (index >= 0) {
- ActivityRecord cur = mHistory.get(index);
- if (cur.task != r.task) {
+ final boolean finishActivityAffinityLocked(ActivityRecord r) {
+ ArrayList<ActivityRecord> activities = r.task.mActivities;
+ for (int index = activities.indexOf(r); index >= 0; --index) {
+ ActivityRecord cur = activities.get(index);
+ if (!Objects.equal(cur.taskAffinity, r.taskAffinity)) {
break;
}
- if (cur.taskAffinity == null && r.taskAffinity != null) {
- break;
- }
- if (cur.taskAffinity != null && !cur.taskAffinity.equals(r.taskAffinity)) {
- break;
- }
- finishActivityLocked(cur, index, Activity.RESULT_CANCELED, null,
- "request-affinity", true);
- index--;
+ finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", true);
}
return true;
}
@@ -3761,17 +2269,8 @@
* @return Returns true if this activity has been removed from the history
* list, or false if it is still in the list and will be removed later.
*/
- final boolean finishActivityLocked(ActivityRecord r, int index,
- int resultCode, Intent resultData, String reason, boolean oomAdj) {
- return finishActivityLocked(r, index, resultCode, resultData, reason, false, oomAdj);
- }
-
- /**
- * @return Returns true if this activity has been removed from the history
- * list, or false if it is still in the list and will be removed later.
- */
- final boolean finishActivityLocked(ActivityRecord r, int index, int resultCode,
- Intent resultData, String reason, boolean immediate, boolean oomAdj) {
+ final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
+ String reason, boolean oomAdj) {
if (r.finishing) {
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
@@ -3781,53 +2280,49 @@
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
r.userId, System.identityHashCode(r),
r.task.taskId, r.shortComponentName, reason);
- if (index < (mHistory.size()-1)) {
- ActivityRecord next = mHistory.get(index+1);
- if (next.task == r.task) {
- if (r.frontOfTask) {
- // The next activity is now the front of the task.
- next.frontOfTask = true;
- }
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
- // If the caller asked that this activity (and all above it)
- // be cleared when the task is reset, don't lose that information,
- // but propagate it up to the next activity.
- next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
- }
+ final ArrayList<ActivityRecord> activities = r.task.mActivities;
+ final int index = activities.indexOf(r);
+ if (index < (activities.size() - 1)) {
+ ActivityRecord next = activities.get(index+1);
+ if (r.frontOfTask) {
+ // The next activity is now the front of the task.
+ next.frontOfTask = true;
+ }
+ if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // If the caller asked that this activity (and all above it)
+ // be cleared when the task is reset, don't lose that information,
+ // but propagate it up to the next activity.
+ next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}
r.pauseKeyDispatchingLocked();
- if (mMainStack) {
+ if (mStackSupervisor.isFrontStack(this)) {
if (mService.mFocusedActivity == r) {
- mService.setFocusedActivityLocked(topRunningActivityLocked(null));
+ mService.setFocusedActivityLocked(mStackSupervisor.topRunningActivityLocked());
}
}
finishActivityResultsLocked(r, resultCode, resultData);
-
+
if (mService.mPendingThumbnails.size() > 0) {
// There are clients waiting to receive thumbnails so, in case
// this is an activity that someone is waiting for, add it
// to the pending list so we can correctly update the clients.
- mService.mCancelledThumbnails.add(r);
+ mStackSupervisor.mCancelledThumbnails.add(r);
}
- if (immediate) {
- return finishCurrentActivityLocked(r, index,
- FINISH_IMMEDIATELY, oomAdj) == null;
- } else if (mResumedActivity == r) {
- boolean endTask = index <= 0
- || (mHistory.get(index-1)).task != r.task;
+ if (mResumedActivity == r) {
+ boolean endTask = index <= 0;
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare close transition: finishing " + r);
- mService.mWindowManager.prepareAppTransition(endTask
+ mWindowManager.prepareAppTransition(endTask
? AppTransition.TRANSIT_TASK_CLOSE
: AppTransition.TRANSIT_ACTIVITY_CLOSE, false);
-
+
// Tell window manager to prepare for this one to be removed.
- mService.mWindowManager.setAppVisibility(r.appToken, false);
-
+ mWindowManager.setAppVisibility(r.appToken, false);
+
if (mPausingActivity == null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
@@ -3838,8 +2333,7 @@
// If the activity is PAUSING, we will complete the finish once
// it is done pausing; else we can just directly finish it here.
if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
- return finishCurrentActivityLocked(r, index,
- FINISH_AFTER_PAUSE, oomAdj) == null;
+ return finishCurrentActivityLocked(r, FINISH_AFTER_PAUSE, oomAdj) == null;
} else {
if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
}
@@ -3847,35 +2341,26 @@
return false;
}
- private static final int FINISH_IMMEDIATELY = 0;
- private static final int FINISH_AFTER_PAUSE = 1;
- private static final int FINISH_AFTER_VISIBLE = 2;
+ static final int FINISH_IMMEDIATELY = 0;
+ static final int FINISH_AFTER_PAUSE = 1;
+ static final int FINISH_AFTER_VISIBLE = 2;
- private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
- int mode, boolean oomAdj) {
- final int index = indexOfActivityLocked(r);
- if (index < 0) {
- return null;
- }
-
- return finishCurrentActivityLocked(r, index, mode, oomAdj);
- }
-
- private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
- int index, int mode, boolean oomAdj) {
+ final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj) {
// First things first: if this activity is currently visible,
// and the resumed activity is not yet visible, then hold off on
// finishing until the resumed one becomes visible.
if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
- if (!mStoppingActivities.contains(r)) {
- mStoppingActivities.add(r);
- if (mStoppingActivities.size() > 3) {
+ if (!mStackSupervisor.mStoppingActivities.contains(r)) {
+ mStackSupervisor.mStoppingActivities.add(r);
+ if (mStackSupervisor.mStoppingActivities.size() > 3
+ || r.frontOfTask && mTaskHistory.size() <= 1) {
// If we already have a few activities waiting to stop,
// then give up on things going idle and start clearing
- // them out.
- scheduleIdleLocked();
+ // them out. Or if r is the last of activity of the last task the stack
+ // will be empty and must be cleared immediately.
+ mStackSupervisor.scheduleIdleLocked();
} else {
- checkReadyForSleepLocked();
+ mStackSupervisor.checkReadyForSleepLocked();
}
}
if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r
@@ -3888,9 +2373,9 @@
}
// make sure the record is cleaned out of other places.
- mStoppingActivities.remove(r);
- mGoingToSleepActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
+ mStackSupervisor.mStoppingActivities.remove(r);
+ mStackSupervisor.mGoingToSleepActivities.remove(r);
+ mStackSupervisor.mWaitingVisibleActivities.remove(r);
if (mResumedActivity == r) {
mResumedActivity = null;
}
@@ -3906,19 +2391,99 @@
boolean activityRemoved = destroyActivityLocked(r, true,
oomAdj, "finish-imm");
if (activityRemoved) {
- resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
}
return activityRemoved ? null : r;
- } else {
- // Need to go through the full pause cycle to get this
- // activity into the stopped state and then finish it.
- if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
- mFinishingActivities.add(r);
- resumeTopActivityLocked(null);
}
+
+ // Need to go through the full pause cycle to get this
+ // activity into the stopped state and then finish it.
+ if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
+ mStackSupervisor.mFinishingActivities.add(r);
+ mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
return r;
}
+ final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
+ Intent resultData) {
+ final ActivityRecord srec = ActivityRecord.forToken(token);
+ final TaskRecord task = srec.task;
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ final int start = activities.indexOf(srec);
+ if (!mTaskHistory.contains(task) || (start < 0)) {
+ return false;
+ }
+ int finishTo = start - 1;
+ ActivityRecord parent = finishTo < 0 ? null : activities.get(finishTo);
+ boolean foundParentInTask = false;
+ final ComponentName dest = destIntent.getComponent();
+ if (start > 0 && dest != null) {
+ for (int i = finishTo; i >= 0; i--) {
+ ActivityRecord r = activities.get(i);
+ if (r.info.packageName.equals(dest.getPackageName()) &&
+ r.info.name.equals(dest.getClassName())) {
+ finishTo = i;
+ parent = r;
+ foundParentInTask = true;
+ break;
+ }
+ }
+ }
+
+ IActivityController controller = mService.mController;
+ if (controller != null) {
+ ActivityRecord next = topRunningActivityLocked(srec.appToken, 0);
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean resumeOK = true;
+ try {
+ resumeOK = controller.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
+ }
+
+ if (!resumeOK) {
+ return false;
+ }
+ }
+ }
+ final long origId = Binder.clearCallingIdentity();
+ for (int i = start; i > finishTo; i--) {
+ ActivityRecord r = activities.get(i);
+ requestFinishActivityLocked(r.appToken, resultCode, resultData, "navigate-up", true);
+ // Only return the supplied result for the first activity finished
+ resultCode = Activity.RESULT_CANCELED;
+ resultData = null;
+ }
+
+ if (parent != null && foundParentInTask) {
+ final int parentLaunchMode = parent.info.launchMode;
+ final int destIntentFlags = destIntent.getFlags();
+ if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
+ (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+ parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent);
+ } else {
+ try {
+ ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
+ destIntent.getComponent(), 0, srec.userId);
+ int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent,
+ null, aInfo, parent.appToken, null,
+ 0, -1, parent.launchedFromUid, parent.launchedFromPackage,
+ 0, null, true, null);
+ foundParentInTask = res == ActivityManager.START_SUCCESS;
+ } catch (RemoteException e) {
+ foundParentInTask = false;
+ }
+ requestFinishActivityLocked(parent.appToken, resultCode,
+ resultData, "navigate-up", true);
+ }
+ }
+ Binder.restoreCallingIdentity(origId);
+ return foundParentInTask;
+ }
/**
* Perform the common clean-up of an activity record. This is called both
* as part of destroyActivityLocked() (when destroying the client-side
@@ -3948,9 +2513,9 @@
// Make sure this record is no longer in the pending finishes list.
// This could happen, for example, if we are trimming activities
// down to the max limit while they are still waiting to finish.
- mFinishingActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
-
+ mStackSupervisor.mFinishingActivities.remove(r);
+ mStackSupervisor.mWaitingVisibleActivities.remove(r);
+
// Remove any pending results.
if (r.finishing && r.pendingResults != null) {
for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
@@ -3963,14 +2528,14 @@
}
if (cleanServices) {
- cleanUpActivityServicesLocked(r);
+ cleanUpActivityServicesLocked(r);
}
if (mService.mPendingThumbnails.size() > 0) {
// There are clients waiting to receive thumbnails so, in case
// this is an activity that someone is waiting for, add it
// to the pending list so we can correctly update the clients.
- mService.mCancelledThumbnails.add(r);
+ mStackSupervisor.mCancelledThumbnails.add(r);
}
// Get rid of any pending idle timeouts.
@@ -3978,9 +2543,9 @@
}
private void removeTimeoutsForActivityLocked(ActivityRecord r) {
+ mStackSupervisor.removeTimeoutsForActivityLocked(r);
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
- mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
r.finishLaunchTickingLocked();
}
@@ -3993,22 +2558,26 @@
here.fillInStackTrace();
Slog.i(TAG, "Removing activity " + r + " from stack");
}
- mHistory.remove(r);
+ final TaskRecord task = r.task;
+ if (task != null && task.removeActivity(r)) {
+ if (DEBUG_STACK) Slog.i(TAG,
+ "removeActivityFromHistoryLocked: last activity removed from " + this);
+ mStackSupervisor.removeTask(task);
+ }
r.takeFromHistory();
removeTimeoutsForActivityLocked(r);
- if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r
- + " (removed from history)");
+ if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (removed from history)");
r.state = ActivityState.DESTROYED;
if (DEBUG_APP) Slog.v(TAG, "Clearing app during remove for activity " + r);
r.app = null;
- mService.mWindowManager.removeAppToken(r.appToken);
+ mWindowManager.removeAppToken(r.appToken);
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
cleanUpActivityServicesLocked(r);
r.removeUriPermissionsLocked();
}
-
+
/**
* Perform clean-up of service connections in an activity record.
*/
@@ -4033,36 +2602,40 @@
final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj, String reason) {
boolean lastIsOpaque = false;
boolean activityRemoved = false;
- for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (r.fullscreen) {
- lastIsOpaque = true;
- }
- if (owner != null && r.app != owner) {
- continue;
- }
- if (!lastIsOpaque) {
- continue;
- }
- // We can destroy this one if we have its icicle saved and
- // it is not in the process of pausing/stopping/finishing.
- if (r.app != null && r != mResumedActivity && r != mPausingActivity
- && r.haveState && !r.visible && r.stopped
- && r.state != ActivityState.DESTROYING
- && r.state != ActivityState.DESTROYED) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state
- + " resumed=" + mResumedActivity
- + " pausing=" + mPausingActivity);
- if (destroyActivityLocked(r, true, oomAdj, reason)) {
- activityRemoved = true;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.fullscreen) {
+ lastIsOpaque = true;
+ }
+ if (owner != null && r.app != owner) {
+ continue;
+ }
+ if (!lastIsOpaque) {
+ continue;
+ }
+ // We can destroy this one if we have its icicle saved and
+ // it is not in the process of pausing/stopping/finishing.
+ if (r.app != null && r != mResumedActivity && r != mPausingActivity
+ && r.haveState && !r.visible && r.stopped
+ && r.state != ActivityState.DESTROYING
+ && r.state != ActivityState.DESTROYED) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state
+ + " resumed=" + mResumedActivity
+ + " pausing=" + mPausingActivity);
+ if (destroyActivityLocked(r, true, oomAdj, reason)) {
+ activityRemoved = true;
+ }
}
}
}
if (activityRemoved) {
- resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
+
}
}
@@ -4089,10 +2662,7 @@
if (hadApp) {
if (removeFromApp) {
- int idx = r.app.activities.indexOf(r);
- if (idx >= 0) {
- r.app.activities.remove(idx);
- }
+ r.app.activities.remove(r);
if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
mService.mHeavyWeightProcess = null;
mService.mHandler.sendEmptyMessage(
@@ -4105,7 +2675,7 @@
}
boolean skipDestroy = false;
-
+
try {
if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
@@ -4123,7 +2693,7 @@
}
r.nowVisible = false;
-
+
// If the activity is finishing, we need to wait on removing it
// from the list to give it a chance to do its cleanup. During
// that time it may make calls back with its token so we need to
@@ -4135,12 +2705,10 @@
if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYING: " + r
+ " (destroy requested)");
r.state = ActivityState.DESTROYING;
- Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
- msg.obj = r;
+ Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
} else {
- if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r
- + " (destroy skipped)");
+ if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (destroy skipped)");
r.state = ActivityState.DESTROYED;
if (DEBUG_APP) Slog.v(TAG, "Clearing app during destroy for activity " + r);
r.app = null;
@@ -4151,8 +2719,7 @@
removeActivityFromHistoryLocked(r);
removedFromHistory = true;
} else {
- if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r
- + " (no app)");
+ if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (no app)");
r.state = ActivityState.DESTROYED;
if (DEBUG_APP) Slog.v(TAG, "Clearing app during destroy for activity " + r);
r.app = null;
@@ -4160,46 +2727,43 @@
}
r.configChangeFlags = 0;
-
+
if (!mLRUActivities.remove(r) && hadApp) {
Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
}
-
+
return removedFromHistory;
}
- final void activityDestroyed(IBinder token) {
- synchronized (mService) {
- final long origId = Binder.clearCallingIdentity();
- try {
- ActivityRecord r = ActivityRecord.forToken(token);
- if (r != null) {
- mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
- }
-
- int index = indexOfActivityLocked(r);
- if (index >= 0) {
- if (r.state == ActivityState.DESTROYING) {
- cleanUpActivityLocked(r, true, false);
- removeActivityFromHistoryLocked(r);
- }
- }
- resumeTopActivityLocked(null);
- } finally {
- Binder.restoreCallingIdentity(origId);
+ final void activityDestroyedLocked(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ ActivityRecord r = ActivityRecord.forToken(token);
+ if (r != null) {
+ mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
}
+
+ if (isInStackLocked(token) != null) {
+ if (r.state == ActivityState.DESTROYING) {
+ cleanUpActivityLocked(r, true, false);
+ removeActivityFromHistoryLocked(r);
+ }
+ }
+ mStackSupervisor.resumeTopActivitiesLocked();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
}
-
- private void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app,
- String listName) {
+
+ private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
+ ProcessRecord app, String listName) {
int i = list.size();
if (DEBUG_CLEANUP) Slog.v(
TAG, "Removing app " + app + " from list " + listName
+ " with " + i + " entries");
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)list.get(i);
+ ActivityRecord r = list.get(i);
if (DEBUG_CLEANUP) Slog.v(TAG, "Record #" + i + " " + r);
if (r.app == app) {
if (DEBUG_CLEANUP) Slog.v(TAG, "---> REMOVING this entry!");
@@ -4211,100 +2775,91 @@
boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
- removeHistoryRecordsForAppLocked(mStoppingActivities, app, "mStoppingActivities");
- removeHistoryRecordsForAppLocked(mGoingToSleepActivities, app, "mGoingToSleepActivities");
- removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app,
+ removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
+ "mStoppingActivities");
+ removeHistoryRecordsForAppLocked(mStackSupervisor.mGoingToSleepActivities, app,
+ "mGoingToSleepActivities");
+ removeHistoryRecordsForAppLocked(mStackSupervisor.mWaitingVisibleActivities, app,
"mWaitingVisibleActivities");
- removeHistoryRecordsForAppLocked(mFinishingActivities, app, "mFinishingActivities");
+ removeHistoryRecordsForAppLocked(mStackSupervisor.mFinishingActivities, app,
+ "mFinishingActivities");
boolean hasVisibleActivities = false;
// Clean out the history list.
- int i = mHistory.size();
+ int i = numActivities();
if (DEBUG_CLEANUP) Slog.v(
TAG, "Removing app " + app + " from history with " + i + " entries");
- while (i > 0) {
- i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (DEBUG_CLEANUP) Slog.v(
- TAG, "Record #" + i + " " + r + ": app=" + r.app);
- if (r.app == app) {
- boolean remove;
- if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
- // Don't currently have state for the activity, or
- // it is finishing -- always remove it.
- remove = true;
- } else if (r.launchCount > 2 &&
- r.lastLaunchTime > (SystemClock.uptimeMillis()-60000)) {
- // We have launched this activity too many times since it was
- // able to run, so give up and remove it.
- remove = true;
- } else {
- // The process may be gone, but the activity lives on!
- remove = false;
- }
- if (remove) {
- if (ActivityStack.DEBUG_ADD_REMOVE || DEBUG_CLEANUP) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "Removing activity " + r + " from stack at " + i
- + ": haveState=" + r.haveState
- + " stateNotNeeded=" + r.stateNotNeeded
- + " finishing=" + r.finishing
- + " state=" + r.state, here);
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ --i;
+ if (DEBUG_CLEANUP) Slog.v(
+ TAG, "Record #" + i + " " + r + ": app=" + r.app);
+ if (r.app == app) {
+ boolean remove;
+ if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
+ // Don't currently have state for the activity, or
+ // it is finishing -- always remove it.
+ remove = true;
+ } else if (r.launchCount > 2 &&
+ r.lastLaunchTime > (SystemClock.uptimeMillis()-60000)) {
+ // We have launched this activity too many times since it was
+ // able to run, so give up and remove it.
+ remove = true;
+ } else {
+ // The process may be gone, but the activity lives on!
+ remove = false;
}
- if (!r.finishing) {
- Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
- EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
- r.userId, System.identityHashCode(r),
- r.task.taskId, r.shortComponentName,
- "proc died without state saved");
- }
- removeActivityFromHistoryLocked(r);
+ if (remove) {
+ if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.i(TAG, "Removing activity " + r + " from stack at " + i
+ + ": haveState=" + r.haveState
+ + " stateNotNeeded=" + r.stateNotNeeded
+ + " finishing=" + r.finishing
+ + " state=" + r.state, here);
+ }
+ if (!r.finishing) {
+ Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
+ EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
+ r.userId, System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName,
+ "proc died without state saved");
+ if (r.state == ActivityState.RESUMED) {
+ mService.updateUsageStats(r, false);
+ }
+ }
+ removeActivityFromHistoryLocked(r);
- } else {
- // We have the current state for this activity, so
- // it can be restarted later when needed.
- if (localLOGV) Slog.v(
- TAG, "Keeping entry, setting app to null");
- if (r.visible) {
- hasVisibleActivities = true;
+ } else {
+ // We have the current state for this activity, so
+ // it can be restarted later when needed.
+ if (localLOGV) Slog.v(
+ TAG, "Keeping entry, setting app to null");
+ if (r.visible) {
+ hasVisibleActivities = true;
+ }
+ if (DEBUG_APP) Slog.v(TAG, "Clearing app during removeHistory for activity "
+ + r);
+ r.app = null;
+ r.nowVisible = false;
+ if (!r.haveState) {
+ if (DEBUG_SAVED_STATE) Slog.i(TAG,
+ "App died, clearing saved state of " + r);
+ r.icicle = null;
+ }
}
- if (DEBUG_APP) Slog.v(TAG, "Clearing app during removeHistory for activity "
- + r);
- r.app = null;
- r.nowVisible = false;
- if (!r.haveState) {
- if (ActivityStack.DEBUG_SAVED_STATE) Slog.i(TAG,
- "App died, clearing saved state of " + r);
- r.icicle = null;
- }
- }
- r.stack.cleanUpActivityLocked(r, true, true);
+ cleanUpActivityLocked(r, true, true);
+ }
}
}
return hasVisibleActivities;
}
-
- /**
- * Move the current home activity's task (if one exists) to the front
- * of the stack.
- */
- final void moveHomeToFrontLocked() {
- TaskRecord homeTask = null;
- for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord hr = mHistory.get(i);
- if (hr.isHomeActivity) {
- homeTask = hr.task;
- break;
- }
- }
- if (homeTask != null) {
- moveTaskToFrontLocked(homeTask, null, null);
- }
- }
final void updateTransitLocked(int transit, Bundle options) {
if (options != null) {
@@ -4315,16 +2870,32 @@
ActivityOptions.abort(options);
}
}
- mService.mWindowManager.prepareAppTransition(transit, false);
+ mWindowManager.prepareAppTransition(transit, false);
+ }
+
+ final boolean findTaskToMoveToFrontLocked(int taskId, int flags, Bundle options) {
+ final TaskRecord task = taskForIdLocked(taskId);
+ if (task != null) {
+ if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
+ mStackSupervisor.mUserLeaving = true;
+ }
+ 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.mActivities.get(0).mLaunchHomeTaskNext = true;
+ }
+ moveTaskToFrontLocked(task, null, options);
+ return true;
+ }
+ return false;
}
final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) {
if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
- final int task = tr.taskId;
- int top = mHistory.size()-1;
-
- if (top < 0 || (mHistory.get(top)).task.taskId == task) {
+ final int numTasks = mTaskHistory.size();
+ final int index = mTaskHistory.indexOf(tr);
+ if (numTasks == 0 || index < 0) {
// nothing to do!
if (reason != null &&
(reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
@@ -4335,40 +2906,17 @@
return;
}
- ArrayList<IBinder> moved = new ArrayList<IBinder>();
-
- // Applying the affinities may have removed entries from the history,
- // so get the size again.
- top = mHistory.size()-1;
- int pos = top;
+ mStackSupervisor.moveHomeStack(isHomeStack());
// Shift all activities with this task up to the top
// of the stack, keeping them in the same internal order.
- while (pos >= 0) {
- ActivityRecord r = mHistory.get(pos);
- if (localLOGV) Slog.v(
- TAG, "At " + pos + " ckp " + r.task + ": " + r);
- if (r.task.taskId == task) {
- if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
- if (DEBUG_ADD_REMOVE) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "Removing and adding activity " + r + " to stack at " + top, here);
- }
- mHistory.remove(pos);
- mHistory.add(top, r);
- moved.add(0, r.appToken);
- top--;
- }
- pos--;
- }
+ mTaskHistory.remove(tr);
+ mTaskHistory.add(tr);
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare to front transition: task=" + tr);
+ if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr);
if (reason != null &&
(reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mService.mWindowManager.prepareAppTransition(
- AppTransition.TRANSIT_NONE, false);
+ mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
ActivityRecord r = topRunningActivityLocked(null);
if (r != null) {
mNoAnimActivities.add(r);
@@ -4377,38 +2925,35 @@
} else {
updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
}
-
- mService.mWindowManager.moveAppTokensToTop(moved);
+
+ mWindowManager.moveTaskToTop(tr.taskId);
+
+ mStackSupervisor.resumeTopActivitiesLocked();
+ EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
+
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
-
- finishTaskMoveLocked(task);
- EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, task);
- }
-
- private final void finishTaskMoveLocked(int task) {
- resumeTopActivityLocked(null);
}
/**
- * Worker method for rearranging history stack. Implements the function of moving all
- * activities for a specific task (gathering them if disjoint) into a single group at the
+ * Worker method for rearranging history stack. Implements the function of moving all
+ * activities for a specific task (gathering them if disjoint) into a single group at the
* bottom of the stack.
- *
+ *
* If a watcher is installed, the action is preflighted and the watcher has an opportunity
* to premeptively cancel the move.
- *
+ *
* @param task The taskId to collect and move to the bottom.
* @return Returns true if the move completed, false if not.
*/
final boolean moveTaskToBackLocked(int task, ActivityRecord reason) {
Slog.i(TAG, "moveTaskToBack: " + task);
-
+
// If we have a watcher, preflight the move before committing to it. First check
// for *other* available tasks, but if none are available, then try again allowing the
// current task to be selected.
- if (mMainStack && mService.mController != null) {
+ if (mStackSupervisor.isFrontStack(this) && mService.mController != null) {
ActivityRecord next = topRunningActivityLocked(null, task);
if (next == null) {
next = topRunningActivityLocked(null, 0);
@@ -4420,6 +2965,7 @@
moveOK = mService.mController.activityResuming(next.packageName);
} catch (RemoteException e) {
mService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
}
if (!moveOK) {
return false;
@@ -4427,181 +2973,45 @@
}
}
- ArrayList<IBinder> moved = new ArrayList<IBinder>();
-
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare to back transition: task=" + task);
-
- final int N = mHistory.size();
- int bottom = 0;
- int pos = 0;
- // Shift all activities with this task down to the bottom
- // of the stack, keeping them in the same internal order.
- while (pos < N) {
- ActivityRecord r = mHistory.get(pos);
- if (localLOGV) Slog.v(
- TAG, "At " + pos + " ckp " + r.task + ": " + r);
- if (r.task.taskId == task) {
- if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1));
- if (DEBUG_ADD_REMOVE) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "Removing and adding activity " + r + " to stack at "
- + bottom, here);
- }
- mHistory.remove(pos);
- mHistory.add(bottom, r);
- moved.add(r.appToken);
- bottom++;
- }
- pos++;
+ final TaskRecord tr = taskForIdLocked(task);
+ if (tr == null) {
+ return false;
}
+ mTaskHistory.remove(tr);
+ mTaskHistory.add(0, tr);
if (reason != null &&
- (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mService.mWindowManager.prepareAppTransition(
- AppTransition.TRANSIT_NONE, false);
+ (reason.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
ActivityRecord r = topRunningActivityLocked(null);
if (r != null) {
mNoAnimActivities.add(r);
}
} else {
- mService.mWindowManager.prepareAppTransition(
+ mWindowManager.prepareAppTransition(
AppTransition.TRANSIT_TASK_TO_BACK, false);
}
- mService.mWindowManager.moveAppTokensToBottom(moved);
+ mWindowManager.moveTaskToBottom(task);
+
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
- finishTaskMoveLocked(task);
+ if (mResumedActivity != null && mResumedActivity.task == tr &&
+ mResumedActivity.mLaunchHomeTaskNext) {
+ // TODO: Can we skip the next line and just pass mResumedAct. to resumeHomeAct.()?
+ mResumedActivity.mLaunchHomeTaskNext = false;
+ return mStackSupervisor.resumeHomeActivity(null);
+ }
+
+ mStackSupervisor.resumeTopActivitiesLocked();
return true;
}
- public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) {
- TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true);
- ActivityRecord resumed = mResumedActivity;
- if (resumed != null && resumed.thumbHolder == tr) {
- info.mainThumbnail = resumed.stack.screenshotActivities(resumed);
- }
- if (info.mainThumbnail == null) {
- info.mainThumbnail = tr.lastThumbnail;
- }
- return info;
- }
-
- public Bitmap getTaskTopThumbnailLocked(TaskRecord tr) {
- ActivityRecord resumed = mResumedActivity;
- if (resumed != null && resumed.task == tr) {
- // This task is the current resumed task, we just need to take
- // a screenshot of it and return that.
- return resumed.stack.screenshotActivities(resumed);
- }
- // Return the information about the task, to figure out the top
- // thumbnail to return.
- TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true);
- if (info.numSubThumbbails <= 0) {
- return info.mainThumbnail != null ? info.mainThumbnail : tr.lastThumbnail;
- } else {
- return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail;
- }
- }
-
- public ActivityRecord removeTaskActivitiesLocked(int taskId, int subTaskIndex,
- boolean taskRequired) {
- TaskAccessInfo info = getTaskAccessInfoLocked(taskId, false);
- if (info.root == null) {
- if (taskRequired) {
- Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
- }
- return null;
- }
-
- if (subTaskIndex < 0) {
- // Just remove the entire task.
- performClearTaskAtIndexLocked(taskId, info.rootIndex);
- return info.root;
- }
-
- if (subTaskIndex >= info.subtasks.size()) {
- if (taskRequired) {
- Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex);
- }
- return null;
- }
-
- // Remove all of this task's activities starting at the sub task.
- TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex);
- performClearTaskAtIndexLocked(taskId, subtask.index);
- return subtask.activity;
- }
-
- public TaskAccessInfo getTaskAccessInfoLocked(int taskId, boolean inclThumbs) {
- final TaskAccessInfo thumbs = new TaskAccessInfo();
- // How many different sub-thumbnails?
- final int NA = mHistory.size();
- int j = 0;
- ThumbnailHolder holder = null;
- while (j < NA) {
- ActivityRecord ar = mHistory.get(j);
- if (!ar.finishing && ar.task.taskId == taskId) {
- thumbs.root = ar;
- thumbs.rootIndex = j;
- holder = ar.thumbHolder;
- if (holder != null) {
- thumbs.mainThumbnail = holder.lastThumbnail;
- }
- j++;
- break;
- }
- j++;
- }
-
- if (j >= NA) {
- return thumbs;
- }
-
- ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>();
- thumbs.subtasks = subtasks;
- while (j < NA) {
- ActivityRecord ar = mHistory.get(j);
- j++;
- if (ar.finishing) {
- continue;
- }
- if (ar.task.taskId != taskId) {
- break;
- }
- if (ar.thumbHolder != holder && holder != null) {
- thumbs.numSubThumbbails++;
- holder = ar.thumbHolder;
- TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask();
- sub.holder = holder;
- sub.activity = ar;
- sub.index = j-1;
- subtasks.add(sub);
- }
- }
- if (thumbs.numSubThumbbails > 0) {
- thumbs.retriever = new IThumbnailRetriever.Stub() {
- public Bitmap getThumbnail(int index) {
- if (index < 0 || index >= thumbs.subtasks.size()) {
- return null;
- }
- TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index);
- ActivityRecord resumed = mResumedActivity;
- if (resumed != null && resumed.thumbHolder == sub.holder) {
- return resumed.stack.screenshotActivities(resumed);
- }
- return sub.holder.lastThumbnail;
- }
- };
- }
- return thumbs;
- }
-
- private final void logStartActivity(int tag, ActivityRecord r,
+ static final void logStartActivity(int tag, ActivityRecord r,
TaskRecord task) {
final Uri data = r.intent.getData();
final String strData = data != null ? data.toSafeString() : null;
@@ -4626,10 +3036,10 @@
"Skipping config check (will change): " + r);
return true;
}
-
+
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Ensuring correct configuration: " + r);
-
+
// Short circuit: if the two configurations are the exact same
// object (the common case), then there is nothing to do.
Configuration newConfig = mService.mConfiguration;
@@ -4638,7 +3048,7 @@
"Configuration unchanged in " + r);
return true;
}
-
+
// We don't worry about activities that are finishing.
if (r.finishing) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
@@ -4646,7 +3056,7 @@
r.stopFreezingScreenLocked(false);
return true;
}
-
+
// Okay we now are going to make this activity have the new config.
// But then we need to figure out how it needs to deal with that.
Configuration oldConfig = r.configuration;
@@ -4672,7 +3082,7 @@
r.forceNewConfig = false;
return true;
}
-
+
// Figure out how to handle the changes between the configurations.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
@@ -4712,12 +3122,12 @@
relaunchActivityLocked(r, r.configChangeFlags, false);
r.configChangeFlags = 0;
}
-
+
// All done... tell the caller we weren't able to keep this
// activity around.
return false;
}
-
+
// Default case: the activity can handle this new configuration, so
// hand it over. Note that we don't need to give it the new
// configuration, since we always send configuration changes to all
@@ -4732,7 +3142,7 @@
}
}
r.stopFreezingScreenLocked(false);
-
+
return true;
}
@@ -4750,9 +3160,9 @@
EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
: EventLogTags.AM_RELAUNCH_ACTIVITY, r.userId, System.identityHashCode(r),
r.task.taskId, r.shortComponentName);
-
+
r.startFreezingScreenLocked(r.app, 0);
-
+
try {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG,
(andResume ? "Relaunching to RESUMED " : "Relaunching to PAUSED ")
@@ -4770,9 +3180,6 @@
if (andResume) {
r.results = null;
r.newIntents = null;
- if (mMainStack) {
- mService.reportResumedActivityLocked(r);
- }
r.state = ActivityState.RESUMED;
} else {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
@@ -4781,8 +3188,307 @@
return true;
}
-
- public void dismissKeyguardOnNextActivityLocked() {
- mDismissKeyguardOnNextActivity = true;
+
+ boolean willActivityBeVisibleLocked(IBinder token) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.appToken == token) {
+ return true;
+ }
+ if (r.fullscreen && !r.finishing) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ void closeSystemDialogsLocked() {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, "close-sys", true);
+ }
+ }
+ }
+ }
+
+ boolean forceStopPackageLocked(String name, boolean doit, boolean evenPersistent, int userId) {
+ boolean didSomething = false;
+ TaskRecord lastTask = null;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ int numActivities = activities.size();
+ for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+ ActivityRecord r = activities.get(activityNdx);
+ final boolean samePackage = r.packageName.equals(name)
+ || (name == null && r.userId == userId);
+ if ((userId == UserHandle.USER_ALL || r.userId == userId)
+ && (samePackage || r.task == lastTask)
+ && (r.app == null || evenPersistent || !r.app.persistent)) {
+ if (!doit) {
+ if (r.finishing) {
+ // If this activity is just finishing, then it is not
+ // interesting as far as something to stop.
+ continue;
+ }
+ return true;
+ }
+ didSomething = true;
+ Slog.i(TAG, " Force finishing activity " + r);
+ if (samePackage) {
+ if (r.app != null) {
+ r.app.removed = true;
+ }
+ r.app = null;
+ }
+ lastTask = r.task;
+ if (finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop",
+ true)) {
+ // r has been deleted from mActivities, accommodate.
+ --numActivities;
+ --activityNdx;
+ }
+ }
+ }
+ }
+ return didSomething;
+ }
+
+ ActivityRecord getTasksLocked(int maxNum, IThumbnailReceiver receiver,
+ PendingThumbnailsRecord pending, List<RunningTaskInfo> list) {
+ ActivityRecord topRecord = null;
+ for (int taskNdx = mTaskHistory.size() - 1; maxNum > 0 && taskNdx >= 0;
+ --maxNum, --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ ActivityRecord r = null;
+ ActivityRecord top = null;
+ int numActivities = 0;
+ int numRunning = 0;
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ r = activities.get(activityNdx);
+
+ // Initialize state for next task if needed.
+ if (top == null || (top.state == ActivityState.INITIALIZING)) {
+ top = r;
+ numActivities = numRunning = 0;
+ }
+
+ // Add 'r' into the current task.
+ numActivities++;
+ if (r.app != null && r.app.thread != null) {
+ numRunning++;
+ }
+
+ if (localLOGV) Slog.v(
+ TAG, r.intent.getComponent().flattenToShortString()
+ + ": task=" + r.task);
+ }
+
+ RunningTaskInfo ci = new RunningTaskInfo();
+ ci.id = task.taskId;
+ ci.baseActivity = r.intent.getComponent();
+ ci.topActivity = top.intent.getComponent();
+ if (top.thumbHolder != null) {
+ ci.description = top.thumbHolder.lastDescription;
+ }
+ ci.numActivities = numActivities;
+ ci.numRunning = numRunning;
+ //System.out.println(
+ // "#" + maxNum + ": " + " descr=" + ci.description);
+ if (receiver != null) {
+ if (localLOGV) Slog.v(
+ TAG, "State=" + top.state + "Idle=" + top.idle
+ + " app=" + top.app
+ + " thr=" + (top.app != null ? top.app.thread : null));
+ if (top.state == ActivityState.RESUMED || top.state == ActivityState.PAUSING) {
+ if (top.idle && top.app != null && top.app.thread != null) {
+ topRecord = top;
+ } else {
+ top.thumbnailNeeded = true;
+ }
+ }
+ pending.pendingRecords.add(top);
+ }
+ list.add(ci);
+ }
+ return topRecord;
+ }
+
+ public void unhandledBackLocked() {
+ final int top = mTaskHistory.size() - 1;
+ if (DEBUG_SWITCH) Slog.d(
+ TAG, "Performing unhandledBack(): top activity at " + top);
+ if (top >= 0) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
+ int activityTop = activities.size() - 1;
+ if (activityTop > 0) {
+ finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED, null,
+ "unhandled-back", true);
+ }
+ }
+ }
+
+ void handleAppDiedLocked(ProcessRecord app, boolean restarting) {
+ if (!containsApp(app)) {
+ return;
+ }
+ // TODO: handle the case where an app spans multiple stacks.
+ if (mPausingActivity != null && mPausingActivity.app == app) {
+ if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG,
+ "App died while pausing: " + mPausingActivity);
+ mPausingActivity = null;
+ }
+ if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+ mLastPausedActivity = null;
+ }
+ final ActivityRecord top = topRunningActivityLocked(null);
+ final boolean launchHomeTaskNext =
+ top != null && top.app == app && top.mLaunchHomeTaskNext;
+
+ // Remove this application's activities from active lists.
+ boolean hasVisibleActivities = removeHistoryRecordsForAppLocked(app);
+
+ if (!restarting) {
+ ActivityStack stack = mStackSupervisor.getFocusedStack();
+ if (stack == null || launchHomeTaskNext) {
+ mStackSupervisor.resumeHomeActivity(null);
+ } else if (!mStackSupervisor.resumeTopActivitiesLocked(stack, null, null)) {
+ // If there was nothing to resume, and we are not already
+ // restarting this process, but there is a visible activity that
+ // is hosted by the process... then make sure all visible
+ // activities are running, taking care of restarting this
+ // process.
+ if (hasVisibleActivities) {
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ }
+ }
+ }
+ }
+
+ void handleAppCrashLocked(ProcessRecord app) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.app == app) {
+ Slog.w(TAG, " Force finishing activity "
+ + r.intent.getComponent().flattenToShortString());
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, "crashed", false);
+ }
+ }
+ }
+ }
+
+ boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ boolean dumpClient, String dumpPackage, boolean needSep, String header) {
+ boolean printed = false;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ printed |= ActivityStackSupervisor.dumpHistoryList(fd, pw,
+ mTaskHistory.get(taskNdx).mActivities, " ", "Hist", true, !dumpAll,
+ dumpClient, dumpPackage, needSep, header,
+ " Task " + taskNdx + ": id #" + task.taskId);
+ if (printed) {
+ header = null;
+ }
+ }
+ return printed;
+ }
+
+ ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
+ ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+
+ if ("all".equals(name)) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ activities.addAll(mTaskHistory.get(taskNdx).mActivities);
+ }
+ } else if ("top".equals(name)) {
+ final int top = mTaskHistory.size() - 1;
+ if (top >= 0) {
+ final ArrayList<ActivityRecord> list = mTaskHistory.get(top).mActivities;
+ int listTop = list.size() - 1;
+ if (listTop >= 0) {
+ activities.add(list.get(listTop));
+ }
+ }
+ } else {
+ ItemMatcher matcher = new ItemMatcher();
+ matcher.build(name);
+
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ for (ActivityRecord r1 : mTaskHistory.get(taskNdx).mActivities) {
+ if (matcher.match(r1, r1.intent.getComponent())) {
+ activities.add(r1);
+ }
+ }
+ }
+ }
+
+ return activities;
+ }
+
+ ActivityRecord restartPackage(String packageName) {
+ ActivityRecord starting = topRunningActivityLocked(null);
+
+ // All activities that came from the package must be
+ // restarted as if there was a config change.
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord a = activities.get(activityNdx);
+ if (a.info.packageName.equals(packageName)) {
+ a.forceNewConfig = true;
+ if (starting != null && a == starting && a.visible) {
+ a.startFreezingScreenLocked(starting.app,
+ ActivityInfo.CONFIG_SCREEN_LAYOUT);
+ }
+ }
+ }
+ }
+
+ return starting;
+ }
+
+ boolean removeTask(TaskRecord task) {
+ mTaskHistory.remove(task);
+ return mTaskHistory.size() == 0;
+ }
+
+ TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, boolean toTop) {
+ TaskRecord task = new TaskRecord(taskId, info, intent, this);
+ if (toTop) {
+ mTaskHistory.add(task);
+ } else {
+ mTaskHistory.add(0, task);
+ }
+ return task;
+ }
+
+ ArrayList<TaskRecord> getAllTasks() {
+ return new ArrayList<TaskRecord>(mTaskHistory);
+ }
+
+ void addTask(final TaskRecord task, final boolean toTop) {
+ task.stack = this;
+ if (toTop) {
+ mTaskHistory.add(task);
+ } else {
+ mTaskHistory.add(0, task);
+ }
+ }
+
+ public int getStackId() {
+ return mStackId;
+ }
+
+ @Override
+ public String toString() {
+ return "stackId=" + mStackId + " tasks=" + mTaskHistory;
}
}
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
new file mode 100644
index 0000000..d39798d
--- /dev/null
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -0,0 +1,2547 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.server.am.ActivityManagerService.localLOGV;
+import static com.android.server.am.ActivityManagerService.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerService.DEBUG_FOCUS;
+import static com.android.server.am.ActivityManagerService.DEBUG_PAUSE;
+import static com.android.server.am.ActivityManagerService.DEBUG_RESULTS;
+import static com.android.server.am.ActivityManagerService.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerService.DEBUG_TASKS;
+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 android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import android.app.IApplicationThread;
+import android.app.IThumbnailReceiver;
+import android.app.PendingIntent;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.IActivityManager.WaitResult;
+import android.app.ResultInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.EventLog;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.os.TransferPipe;
+import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
+import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.wm.StackBox;
+import com.android.server.wm.WindowManagerService;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class ActivityStackSupervisor {
+ static final boolean DEBUG = ActivityManagerService.DEBUG || false;
+ static final boolean DEBUG_ADD_REMOVE = DEBUG || false;
+ static final boolean DEBUG_APP = DEBUG || false;
+ static final boolean DEBUG_SAVED_STATE = DEBUG || false;
+ static final boolean DEBUG_STATES = DEBUG || false;
+ static final boolean DEBUG_IDLE = DEBUG || false;
+
+ public static final int HOME_STACK_ID = 0;
+
+ /** How long we wait until giving up on the last activity telling us it is idle. */
+ static final int IDLE_TIMEOUT = 10*1000;
+
+ /** How long we can hold the sleep wake lock before giving up. */
+ static final int SLEEP_TIMEOUT = 5*1000;
+
+ // How long we can hold the launch wake lock before giving up.
+ static final int LAUNCH_TIMEOUT = 10*1000;
+
+ static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG;
+ static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1;
+ static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
+ static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
+ static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
+
+ // 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;
+
+ final ActivityManagerService mService;
+ final Context mContext;
+ final Looper mLooper;
+
+ final ActivityStackSupervisorHandler mHandler;
+
+ /** Short cut */
+ WindowManagerService mWindowManager;
+
+ /** Dismiss the keyguard after the next activity is displayed? */
+ private boolean mDismissKeyguardOnNextActivity = false;
+
+ /** Identifier counter for all ActivityStacks */
+ private int mLastStackId = HOME_STACK_ID;
+
+ /** Task identifier that activities are currently being started in. Incremented each time a
+ * new task is created. */
+ private int mCurTaskId = 0;
+
+ /** The current user */
+ private int mCurrentUser;
+
+ /** The stack containing the launcher app */
+ private ActivityStack mHomeStack;
+
+ /** The non-home stack currently receiving input or launching the next activity. If home is
+ * in front then mHomeStack overrides mFocusedStack.
+ * DO NOT ACCESS DIRECTLY - It may be null, use getFocusedStack() */
+ private ActivityStack mFocusedStack;
+
+ /** All the non-launcher stacks */
+ private ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
+
+ private static final int STACK_STATE_HOME_IN_FRONT = 0;
+ private static final int STACK_STATE_HOME_TO_BACK = 1;
+ private static final int STACK_STATE_HOME_IN_BACK = 2;
+ private static final int STACK_STATE_HOME_TO_FRONT = 3;
+ private int mStackState = STACK_STATE_HOME_IN_FRONT;
+
+ /** List of activities that are waiting for a new activity to become visible before completing
+ * whatever operation they are supposed to do. */
+ final ArrayList<ActivityRecord> mWaitingVisibleActivities = new ArrayList<ActivityRecord>();
+
+ /** List of processes waiting to find out about the next visible activity. */
+ final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible =
+ new ArrayList<IActivityManager.WaitResult>();
+
+ /** List of processes waiting to find out about the next launched activity. */
+ final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched =
+ new ArrayList<IActivityManager.WaitResult>();
+
+ /** List of activities that are ready to be stopped, but waiting for the next activity to
+ * settle down before doing so. */
+ final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<ActivityRecord>();
+
+ /** List of activities that are ready to be finished, but waiting for the previous activity to
+ * settle down before doing so. It contains ActivityRecord objects. */
+ final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<ActivityRecord>();
+
+ /** List of activities that are in the process of going to sleep. */
+ final ArrayList<ActivityRecord> mGoingToSleepActivities = new ArrayList<ActivityRecord>();
+
+ /** List of ActivityRecord objects that have been finished and must still report back to a
+ * pending thumbnail receiver. */
+ final ArrayList<ActivityRecord> mCancelledThumbnails = new ArrayList<ActivityRecord>();
+
+ /** Used on user changes */
+ final ArrayList<UserStartedState> mStartingUsers = new ArrayList<UserStartedState>();
+
+ /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
+ * is being brought in front of us. */
+ boolean mUserLeaving = false;
+
+ /** Stacks belonging to users other than mCurrentUser. Indexed by userId. */
+ final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
+
+ /** Set when we have taken too long waiting to go to sleep. */
+ boolean mSleepTimeout = false;
+
+ /**
+ * We don't want to allow the device to go to sleep while in the process
+ * of launching an activity. This is primarily to allow alarm intent
+ * receivers to launch an activity and get that to run before the device
+ * goes back to sleep.
+ */
+ final PowerManager.WakeLock mLaunchingActivity;
+
+ /**
+ * Set when the system is going to sleep, until we have
+ * successfully paused the current activity and released our wake lock.
+ * At that point the system is allowed to actually sleep.
+ */
+ final PowerManager.WakeLock mGoingToSleep;
+
+ public ActivityStackSupervisor(ActivityManagerService service, Context context,
+ Looper looper) {
+ mService = service;
+ mContext = context;
+ mLooper = looper;
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
+ mHandler = new ActivityStackSupervisorHandler(looper);
+ if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+ throw new IllegalStateException("Calling must be system uid");
+ }
+ mLaunchingActivity =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
+ mLaunchingActivity.setReferenceCounted(false);
+ }
+
+ void setWindowManager(WindowManagerService wm) {
+ mWindowManager = wm;
+ mHomeStack = new ActivityStack(mService, mContext, mLooper, HOME_STACK_ID);
+ mStacks.add(mHomeStack);
+ }
+
+ void dismissKeyguard() {
+ if (mDismissKeyguardOnNextActivity) {
+ mDismissKeyguardOnNextActivity = false;
+ mWindowManager.dismissKeyguard();
+ }
+ }
+
+ ActivityStack getFocusedStack() {
+ if (mFocusedStack == null) {
+ return mHomeStack;
+ }
+ switch (mStackState) {
+ case STACK_STATE_HOME_IN_FRONT:
+ case STACK_STATE_HOME_TO_FRONT:
+ return mHomeStack;
+ case STACK_STATE_HOME_IN_BACK:
+ case STACK_STATE_HOME_TO_BACK:
+ default:
+ return mFocusedStack;
+ }
+ }
+
+ ActivityStack getLastStack() {
+ switch (mStackState) {
+ case STACK_STATE_HOME_IN_FRONT:
+ case STACK_STATE_HOME_TO_BACK:
+ return mHomeStack;
+ case STACK_STATE_HOME_TO_FRONT:
+ case STACK_STATE_HOME_IN_BACK:
+ default:
+ return mFocusedStack;
+ }
+ }
+
+ boolean isFrontStack(ActivityStack stack) {
+ return (stack.mCurrentUser == mCurrentUser) &&
+ !(stack.isHomeStack() ^ getFocusedStack().isHomeStack());
+ }
+
+ void moveHomeStack(boolean toFront) {
+ final boolean homeInFront = isFrontStack(mHomeStack);
+ if (homeInFront ^ toFront) {
+ if (DEBUG_STACK) Slog.d(TAG, "moveHomeTask: mStackState old=" +
+ stackStateToString(mStackState) + " new=" + stackStateToString(homeInFront ?
+ STACK_STATE_HOME_TO_BACK : STACK_STATE_HOME_TO_FRONT));
+ mStackState = homeInFront ? STACK_STATE_HOME_TO_BACK : STACK_STATE_HOME_TO_FRONT;
+ }
+ }
+
+ boolean resumeHomeActivity(ActivityRecord prev) {
+ moveHomeStack(true);
+ if (prev != null) {
+ prev.mLaunchHomeTaskNext = false;
+ }
+ ActivityRecord r = mHomeStack.topRunningActivityLocked(null);
+ if (r != null) {
+ mService.setFocusedActivityLocked(r);
+ return resumeTopActivitiesLocked(mHomeStack, prev, null);
+ }
+ return mService.startHomeActivityLocked(mCurrentUser);
+ }
+
+ final void setLaunchHomeTaskNextFlag(ActivityRecord sourceRecord, ActivityRecord r,
+ ActivityStack stack) {
+ if (stack == mHomeStack) {
+ return;
+ }
+ if ((sourceRecord == null && getLastStack() == mHomeStack) ||
+ (sourceRecord != null && sourceRecord.isHomeActivity())) {
+ if (r == null) {
+ r = stack.topRunningActivityLocked(null);
+ }
+ if (r != null && !r.isHomeActivity() && r.isRootActivity()) {
+ r.mLaunchHomeTaskNext = true;
+ }
+ }
+ }
+
+ void setDismissKeyguard(boolean dismiss) {
+ mDismissKeyguardOnNextActivity = dismiss;
+ }
+
+ TaskRecord anyTaskForIdLocked(int id) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ ActivityStack stack = mStacks.get(stackNdx);
+ TaskRecord task = stack.taskForIdLocked(id);
+ if (task != null) {
+ return task;
+ }
+ }
+ return null;
+ }
+
+ ActivityRecord isInAnyStackLocked(IBinder token) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord r = mStacks.get(stackNdx).isInStackLocked(token);
+ if (r != null) {
+ return r;
+ }
+ }
+ return null;
+ }
+
+ int getNextTaskId() {
+ do {
+ mCurTaskId++;
+ if (mCurTaskId <= 0) {
+ mCurTaskId = 1;
+ }
+ } while (anyTaskForIdLocked(mCurTaskId) != null);
+ return mCurTaskId;
+ }
+
+ void removeTask(TaskRecord task) {
+ mWindowManager.removeTask(task.taskId);
+ final ActivityStack stack = task.stack;
+ final ActivityRecord r = stack.mResumedActivity;
+ if (r != null && r.task == task) {
+ stack.mResumedActivity = null;
+ }
+ if (stack.removeTask(task) && !stack.isHomeStack()) {
+ if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing stack " + stack);
+ mStacks.remove(stack);
+ final int stackId = stack.mStackId;
+ final int nextStackId = mWindowManager.removeStack(stackId);
+ // TODO: Perhaps we need to let the ActivityManager determine the next focus...
+ if (getFocusedStack().mStackId == stackId) {
+ // If this is the last app stack, set mFocusedStack to null.
+ mFocusedStack = nextStackId == HOME_STACK_ID ? null : getStack(nextStackId);
+ }
+ }
+ }
+
+ ActivityRecord resumedAppLocked() {
+ ActivityStack stack = getFocusedStack();
+ if (stack == null) {
+ return null;
+ }
+ ActivityRecord resumedActivity = stack.mResumedActivity;
+ if (resumedActivity == null || resumedActivity.app == null) {
+ resumedActivity = stack.mPausingActivity;
+ if (resumedActivity == null || resumedActivity.app == null) {
+ resumedActivity = stack.topRunningActivityLocked(null);
+ }
+ }
+ return resumedActivity;
+ }
+
+ boolean attachApplicationLocked(ProcessRecord app, boolean headless) throws Exception {
+ boolean didSomething = false;
+ final String processName = app.processName;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (!isFrontStack(stack)) {
+ continue;
+ }
+ ActivityRecord hr = stack.topRunningActivityLocked(null);
+ if (hr != null) {
+ if (hr.app == null && app.uid == hr.info.applicationInfo.uid
+ && processName.equals(hr.processName)) {
+ try {
+ if (headless) {
+ Slog.e(TAG, "Starting activities not supported on headless device: "
+ + hr);
+ } else if (realStartActivityLocked(hr, app, true, true)) {
+ didSomething = true;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception in new application when starting activity "
+ + hr.intent.getComponent().flattenToShortString(), e);
+ throw e;
+ }
+ }
+ }
+ }
+ if (!didSomething) {
+ ensureActivitiesVisibleLocked(null, 0);
+ }
+ return didSomething;
+ }
+
+ boolean allResumedActivitiesIdle() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (!isFrontStack(stack)) {
+ continue;
+ }
+ final ActivityRecord resumedActivity = stack.mResumedActivity;
+ if (resumedActivity == null || !resumedActivity.idle) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean allResumedActivitiesComplete() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (isFrontStack(stack)) {
+ final ActivityRecord r = stack.mResumedActivity;
+ if (r != null && r.state != ActivityState.RESUMED) {
+ return false;
+ }
+ }
+ }
+ // TODO: Not sure if this should check if all Paused are complete too.
+ switch (mStackState) {
+ case STACK_STATE_HOME_TO_BACK:
+ if (DEBUG_STACK) Slog.d(TAG, "allResumedActivitiesComplete: mStackState old=" +
+ stackStateToString(STACK_STATE_HOME_TO_BACK) + " new=" +
+ stackStateToString(STACK_STATE_HOME_IN_BACK));
+ mStackState = STACK_STATE_HOME_IN_BACK;
+ break;
+ case STACK_STATE_HOME_TO_FRONT:
+ if (DEBUG_STACK) Slog.d(TAG, "allResumedActivitiesComplete: mStackState old=" +
+ stackStateToString(STACK_STATE_HOME_TO_FRONT) + " new=" +
+ stackStateToString(STACK_STATE_HOME_IN_FRONT));
+ mStackState = STACK_STATE_HOME_IN_FRONT;
+ break;
+ }
+ return true;
+ }
+
+ boolean allResumedActivitiesVisible() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ final ActivityRecord r = stack.mResumedActivity;
+ if (r != null && (!r.nowVisible || r.waitingVisible)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean pauseBackStacks(boolean userLeaving) {
+ boolean someActivityPaused = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (!isFrontStack(stack) && stack.mResumedActivity != null) {
+ stack.startPausingLocked(userLeaving, false);
+ someActivityPaused = true;
+ }
+ }
+ return someActivityPaused;
+ }
+
+ boolean allPausedActivitiesComplete() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ final ActivityRecord r = stack.mPausingActivity;
+ if (r != null && r.state != ActivityState.PAUSED
+ && r.state != ActivityState.STOPPED
+ && r.state != ActivityState.STOPPING) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void reportActivityVisibleLocked(ActivityRecord r) {
+ for (int i = mWaitingActivityVisible.size()-1; i >= 0; i--) {
+ WaitResult w = mWaitingActivityVisible.get(i);
+ w.timeout = false;
+ if (r != null) {
+ w.who = new ComponentName(r.info.packageName, r.info.name);
+ }
+ w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
+ w.thisTime = w.totalTime;
+ }
+ mService.notifyAll();
+ dismissKeyguard();
+ }
+
+ void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
+ long thisTime, long totalTime) {
+ for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
+ WaitResult w = mWaitingActivityLaunched.remove(i);
+ w.timeout = timeout;
+ if (r != null) {
+ w.who = new ComponentName(r.info.packageName, r.info.name);
+ }
+ w.thisTime = thisTime;
+ w.totalTime = totalTime;
+ }
+ mService.notifyAll();
+ }
+
+ ActivityRecord topRunningActivityLocked() {
+ final ActivityStack focusedStack = getFocusedStack();
+ ActivityRecord r = focusedStack.topRunningActivityLocked(null);
+ if (r != null) {
+ return r;
+ }
+
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (stack.mCurrentUser == mCurrentUser && stack != focusedStack &&
+ isFrontStack(stack)) {
+ r = stack.topRunningActivityLocked(null);
+ if (r != null) {
+ return r;
+ }
+ }
+ }
+ return null;
+ }
+
+ ActivityRecord getTasksLocked(int maxNum, IThumbnailReceiver receiver,
+ PendingThumbnailsRecord pending, List<RunningTaskInfo> list) {
+ ActivityRecord r = null;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ final ActivityRecord ar =
+ stack.getTasksLocked(maxNum - list.size(), receiver, pending, list);
+ if (isFrontStack(stack)) {
+ r = ar;
+ }
+ }
+ return r;
+ }
+
+ ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
+ String profileFile, ParcelFileDescriptor profileFd, int userId) {
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo;
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveIntent(
+ intent, resolvedType,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+
+ if (aInfo != null) {
+ // Store the found target back into the intent, because now that
+ // we have it we never want to do this again. For example, if the
+ // user navigates back to this point in the history, we should
+ // always restart the exact same activity.
+ intent.setComponent(new ComponentName(
+ aInfo.applicationInfo.packageName, aInfo.name));
+
+ // Don't debug things in the system process
+ if ((startFlags&ActivityManager.START_FLAG_DEBUG) != 0) {
+ if (!aInfo.processName.equals("system")) {
+ mService.setDebugApp(aInfo.processName, true, false);
+ }
+ }
+
+ if ((startFlags&ActivityManager.START_FLAG_OPENGL_TRACES) != 0) {
+ if (!aInfo.processName.equals("system")) {
+ mService.setOpenGlTraceApp(aInfo.applicationInfo, aInfo.processName);
+ }
+ }
+
+ if (profileFile != null) {
+ if (!aInfo.processName.equals("system")) {
+ mService.setProfileApp(aInfo.applicationInfo, aInfo.processName,
+ profileFile, profileFd,
+ (startFlags&ActivityManager.START_FLAG_AUTO_STOP_PROFILER) != 0);
+ }
+ }
+ }
+ return aInfo;
+ }
+
+ void startHomeActivity(Intent intent, ActivityInfo aInfo) {
+ moveHomeStack(true);
+ startActivityLocked(null, intent, null, aInfo, null, null, 0, 0, 0, null, 0,
+ null, false, null);
+ }
+
+ final int startActivityMayWait(IApplicationThread caller, int callingUid,
+ String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
+ String resultWho, int requestCode, int startFlags, String profileFile,
+ ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
+ Bundle options, int userId) {
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
+ profileFile, profileFd, userId);
+
+ synchronized (mService) {
+ int callingPid;
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
+ callingPid = Binder.getCallingPid();
+ callingUid = Binder.getCallingUid();
+ } else {
+ callingPid = callingUid = -1;
+ }
+
+ final ActivityStack stack = getFocusedStack();
+ stack.mConfigWillChange = config != null
+ && mService.mConfiguration.diff(config) != 0;
+ if (DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Starting activity when config will change = " + stack.mConfigWillChange);
+
+ final long origId = Binder.clearCallingIdentity();
+
+ if (aInfo != null &&
+ (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+ // This may be a heavy-weight process! Check to see if we already
+ // have another, different heavy-weight process running.
+ if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
+ if (mService.mHeavyWeightProcess != null &&
+ (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
+ !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
+ int realCallingUid = callingUid;
+ if (caller != null) {
+ ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ realCallingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + callingPid + ") when starting: "
+ + intent.toString());
+ ActivityOptions.abort(options);
+ return ActivityManager.START_PERMISSION_DENIED;
+ }
+ }
+
+ IIntentSender target = mService.getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY, "android",
+ realCallingUid, userId, null, null, 0, new Intent[] { intent },
+ new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT, null);
+
+ Intent newIntent = new Intent();
+ if (requestCode >= 0) {
+ // Caller is requesting a result.
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
+ new IntentSender(target));
+ if (mService.mHeavyWeightProcess.activities.size() > 0) {
+ ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
+ hist.packageName);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
+ hist.task.taskId);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+ aInfo.packageName);
+ newIntent.setFlags(intent.getFlags());
+ newIntent.setClassName("android",
+ HeavyWeightSwitcherActivity.class.getName());
+ intent = newIntent;
+ resolvedType = null;
+ caller = null;
+ callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
+ componentSpecified = true;
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveIntent(
+ intent, null,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+ }
+ }
+ }
+
+ int res = startActivityLocked(caller, intent, resolvedType,
+ aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,
+ callingPackage, startFlags, options, componentSpecified, null);
+
+ if (stack.mConfigWillChange) {
+ // If the caller also wants to switch to a new configuration,
+ // do so now. This allows a clean switch, as we are waiting
+ // for the current activity to pause (so we will not destroy
+ // it), and have not yet started the next activity.
+ mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+ "updateConfiguration()");
+ stack.mConfigWillChange = false;
+ if (DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Updating to new configuration after starting activity.");
+ mService.updateConfigurationLocked(config, null, false, false);
+ }
+
+ Binder.restoreCallingIdentity(origId);
+
+ if (outResult != null) {
+ outResult.result = res;
+ if (res == ActivityManager.START_SUCCESS) {
+ mWaitingActivityLaunched.add(outResult);
+ do {
+ try {
+ mService.wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ } else if (res == ActivityManager.START_TASK_TO_FRONT) {
+ ActivityRecord r = stack.topRunningActivityLocked(null);
+ if (r.nowVisible) {
+ outResult.timeout = false;
+ outResult.who = new ComponentName(r.info.packageName, r.info.name);
+ outResult.totalTime = 0;
+ outResult.thisTime = 0;
+ } else {
+ outResult.thisTime = SystemClock.uptimeMillis();
+ mWaitingActivityVisible.add(outResult);
+ do {
+ try {
+ mService.wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ }
+ }
+ }
+
+ return res;
+ }
+ }
+
+ final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo,
+ Bundle options, int userId) {
+ if (intents == null) {
+ throw new NullPointerException("intents is null");
+ }
+ if (resolvedTypes == null) {
+ throw new NullPointerException("resolvedTypes is null");
+ }
+ if (intents.length != resolvedTypes.length) {
+ throw new IllegalArgumentException("intents are length different than resolvedTypes");
+ }
+
+
+ int callingPid;
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
+ callingPid = Binder.getCallingPid();
+ callingUid = Binder.getCallingUid();
+ } else {
+ callingPid = callingUid = -1;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService) {
+ ActivityRecord[] outActivity = new ActivityRecord[1];
+ for (int i=0; i<intents.length; i++) {
+ Intent intent = intents[i];
+ if (intent == null) {
+ continue;
+ }
+
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i],
+ 0, null, null, userId);
+ // TODO: New, check if this is correct
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
+
+ if (aInfo != null &&
+ (aInfo.applicationInfo.flags & ApplicationInfo.FLAG_CANT_SAVE_STATE)
+ != 0) {
+ throw new IllegalArgumentException(
+ "FLAG_CANT_SAVE_STATE not supported here");
+ }
+
+ Bundle theseOptions;
+ if (options != null && i == intents.length-1) {
+ theseOptions = options;
+ } else {
+ theseOptions = null;
+ }
+ int res = startActivityLocked(caller, intent, resolvedTypes[i],
+ aInfo, resultTo, null, -1, callingPid, callingUid, callingPackage,
+ 0, theseOptions, componentSpecified, outActivity);
+ if (res < 0) {
+ return res;
+ }
+
+ resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return ActivityManager.START_SUCCESS;
+ }
+
+ final boolean realStartActivityLocked(ActivityRecord r,
+ ProcessRecord app, boolean andResume, boolean checkConfig)
+ throws RemoteException {
+
+ r.startFreezingScreenLocked(app, 0);
+ mWindowManager.setAppVisibility(r.appToken, true);
+
+ // schedule launch ticks to collect information about slow apps.
+ r.startLaunchTickingLocked();
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order. Note that
+ // as a result of this, it can call back into the activity
+ // manager with a new orientation. We don't care about that,
+ // because the activity is not currently running so we are
+ // just restarting it anyway.
+ if (checkConfig) {
+ Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ mService.mConfiguration,
+ r.mayFreezeScreenLocked(app) ? r.appToken : null);
+ mService.updateConfigurationLocked(config, r, false, false);
+ }
+
+ r.app = app;
+ app.waitingToKill = null;
+ r.launchCount++;
+ r.lastLaunchTime = SystemClock.uptimeMillis();
+
+ if (localLOGV) Slog.v(TAG, "Launching: " + r);
+
+ int idx = app.activities.indexOf(r);
+ if (idx < 0) {
+ app.activities.add(r);
+ }
+ mService.updateLruProcessLocked(app, true);
+
+ final ActivityStack stack = r.task.stack;
+ try {
+ if (app.thread == null) {
+ throw new RemoteException();
+ }
+ List<ResultInfo> results = null;
+ List<Intent> newIntents = null;
+ if (andResume) {
+ results = r.results;
+ newIntents = r.newIntents;
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
+ + " icicle=" + r.icicle
+ + " with results=" + results + " newIntents=" + newIntents
+ + " andResume=" + andResume);
+ if (andResume) {
+ EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
+ r.userId, System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+ }
+ if (r.isHomeActivity()) {
+ mService.mHomeProcess = app;
+ }
+ mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ r.sleeping = false;
+ r.forceNewConfig = false;
+ mService.showAskCompatModeDialogLocked(r);
+ r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+ String profileFile = null;
+ ParcelFileDescriptor profileFd = null;
+ boolean profileAutoStop = false;
+ if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
+ if (mService.mProfileProc == null || mService.mProfileProc == app) {
+ mService.mProfileProc = app;
+ profileFile = mService.mProfileFile;
+ profileFd = mService.mProfileFd;
+ profileAutoStop = mService.mAutoStopProfiler;
+ }
+ }
+ app.hasShownUi = true;
+ app.pendingUiClean = true;
+ if (profileFd != null) {
+ try {
+ profileFd = profileFd.dup();
+ } catch (IOException e) {
+ if (profileFd != null) {
+ try {
+ profileFd.close();
+ } catch (IOException o) {
+ }
+ profileFd = null;
+ }
+ }
+ }
+ app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
+ System.identityHashCode(r), r.info,
+ new Configuration(mService.mConfiguration),
+ r.compat, r.icicle, results, newIntents, !andResume,
+ mService.isNextTransitionForward(), profileFile, profileFd,
+ profileAutoStop);
+
+ if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+ // This may be a heavy-weight process! Note that the package
+ // manager will ensure that only activity can run in the main
+ // process of the .apk, which is the only thing that will be
+ // considered heavy-weight.
+ if (app.processName.equals(app.info.packageName)) {
+ if (mService.mHeavyWeightProcess != null
+ && mService.mHeavyWeightProcess != app) {
+ Slog.w(TAG, "Starting new heavy weight process " + app
+ + " when already running "
+ + mService.mHeavyWeightProcess);
+ }
+ mService.mHeavyWeightProcess = app;
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
+ msg.obj = r;
+ mService.mHandler.sendMessage(msg);
+ }
+ }
+
+ } catch (RemoteException e) {
+ if (r.launchFailed) {
+ // This is the second time we failed -- finish activity
+ // and give up.
+ Slog.e(TAG, "Second failure launching "
+ + r.intent.getComponent().flattenToShortString()
+ + ", giving up", e);
+ mService.appDiedLocked(app, app.pid, app.thread);
+ stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
+ "2nd-crash", false);
+ return false;
+ }
+
+ // This is the first time we failed -- restart process and
+ // retry.
+ app.activities.remove(r);
+ throw e;
+ }
+
+ r.launchFailed = false;
+ if (stack.updateLRUListLocked(r)) {
+ Slog.w(TAG, "Activity " + r
+ + " being launched, but already in LRU list");
+ }
+
+ if (andResume) {
+ // As part of the process of launching, ActivityThread also performs
+ // a resume.
+ stack.minimalResumeActivityLocked(r);
+ } else {
+ // This activity is not starting in the resumed state... which
+ // should look like we asked it to pause+stop (but remain visible),
+ // and it has done so and reported back the current icicle and
+ // other state.
+ if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r
+ + " (starting in stopped state)");
+ r.state = ActivityState.STOPPED;
+ r.stopped = true;
+ }
+
+ // Launch the new version setup screen if needed. We do this -after-
+ // launching the initial activity (that is, home), so that it can have
+ // a chance to initialize itself while in the background, making the
+ // switch back to it faster and look better.
+ if (isFrontStack(stack)) {
+ mService.startSetupActivityLocked();
+ }
+
+ return true;
+ }
+
+ void startSpecificActivityLocked(ActivityRecord r,
+ boolean andResume, boolean checkConfig) {
+ // Is this activity's application already running?
+ ProcessRecord app = mService.getProcessRecordLocked(r.processName,
+ r.info.applicationInfo.uid);
+
+ r.task.stack.setLaunchTime(r);
+
+ if (app != null && app.thread != null) {
+ try {
+ app.addPackage(r.info.packageName, mService.mProcessTracker);
+ realStartActivityLocked(r, app, andResume, checkConfig);
+ return;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when starting activity "
+ + r.intent.getComponent().flattenToShortString(), e);
+ }
+
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
+ }
+
+ mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
+ "activity", r.intent.getComponent(), false, false);
+ }
+
+ final int startActivityLocked(IApplicationThread caller,
+ Intent intent, String resolvedType, ActivityInfo aInfo, IBinder resultTo,
+ String resultWho, int requestCode,
+ int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
+ boolean componentSpecified, ActivityRecord[] outActivity) {
+ int err = ActivityManager.START_SUCCESS;
+
+ ProcessRecord callerApp = null;
+ if (caller != null) {
+ callerApp = mService.getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ callingPid = callerApp.pid;
+ callingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + callingPid + ") when starting: "
+ + intent.toString());
+ err = ActivityManager.START_PERMISSION_DENIED;
+ }
+ }
+
+ if (err == ActivityManager.START_SUCCESS) {
+ final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
+ Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
+ + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
+ }
+
+ ActivityRecord sourceRecord = null;
+ ActivityRecord resultRecord = null;
+ if (resultTo != null) {
+ sourceRecord = isInAnyStackLocked(resultTo);
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Will send result to " + resultTo + " " + sourceRecord);
+ if (sourceRecord != null) {
+ if (requestCode >= 0 && !sourceRecord.finishing) {
+ resultRecord = sourceRecord;
+ }
+ }
+ }
+ ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
+
+ int launchFlags = intent.getFlags();
+
+ if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
+ && sourceRecord != null) {
+ // Transfer the result target from the source activity to the new
+ // one being started, including any failures.
+ if (requestCode >= 0) {
+ ActivityOptions.abort(options);
+ return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+ }
+ resultRecord = sourceRecord.resultTo;
+ resultWho = sourceRecord.resultWho;
+ requestCode = sourceRecord.requestCode;
+ sourceRecord.resultTo = null;
+ if (resultRecord != null) {
+ resultRecord.removeResultsLocked(
+ sourceRecord, resultWho, requestCode);
+ }
+ }
+
+ if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
+ // We couldn't find a class that can handle the given Intent.
+ // That's the end of that!
+ err = ActivityManager.START_INTENT_NOT_RESOLVED;
+ }
+
+ if (err == ActivityManager.START_SUCCESS && aInfo == null) {
+ // We couldn't find the specific class specified in the Intent.
+ // Also the end of the line.
+ err = ActivityManager.START_CLASS_NOT_FOUND;
+ }
+
+ if (err != ActivityManager.START_SUCCESS) {
+ if (resultRecord != null) {
+ resultStack.sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ setDismissKeyguard(false);
+ ActivityOptions.abort(options);
+ return err;
+ }
+
+ final int startAnyPerm = mService.checkPermission(
+ START_ANY_ACTIVITY, callingPid, callingUid);
+ final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
+ callingUid, aInfo.applicationInfo.uid, aInfo.exported);
+ if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
+ if (resultRecord != null) {
+ resultStack.sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ setDismissKeyguard(false);
+ String msg;
+ if (!aInfo.exported) {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " not exported from uid " + aInfo.applicationInfo.uid;
+ } else {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires " + aInfo.permission;
+ }
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
+ callerApp==null?null:callerApp.info, callingUid, callingPid, resolvedType, aInfo);
+
+ if (mService.mController != null) {
+ try {
+ // The Intent we give to the watcher has the extra data
+ // stripped off, since it can contain private information.
+ Intent watchIntent = intent.cloneFilter();
+ abort |= !mService.mController.activityStarting(watchIntent,
+ aInfo.applicationInfo.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ }
+ }
+
+ if (abort) {
+ if (resultRecord != null) {
+ resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ // We pretend to the caller that it was really started, but
+ // they will just get a cancel result.
+ setDismissKeyguard(false);
+ ActivityOptions.abort(options);
+ return ActivityManager.START_SUCCESS;
+ }
+
+ ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
+ intent, resolvedType, aInfo, mService.mConfiguration,
+ resultRecord, resultWho, requestCode, componentSpecified, this);
+ if (outActivity != null) {
+ outActivity[0] = r;
+ }
+
+ final ActivityStack stack = getFocusedStack();
+ if (stack.mResumedActivity == null
+ || stack.mResumedActivity.info.applicationInfo.uid != callingUid) {
+ if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
+ PendingActivityLaunch pal =
+ new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
+ mService.mPendingActivityLaunches.add(pal);
+ setDismissKeyguard(false);
+ ActivityOptions.abort(options);
+ return ActivityManager.START_SWITCHES_CANCELED;
+ }
+ }
+
+ if (mService.mDidAppSwitch) {
+ // This is the second allowed switch since we stopped switches,
+ // so now just generally allow switches. Use case: user presses
+ // home (switches disabled, switch to home, mDidAppSwitch now true);
+ // user taps a home icon (coming from home so allowed, we hit here
+ // and now allow anyone to switch again).
+ mService.mAppSwitchesAllowedTime = 0;
+ } else {
+ mService.mDidAppSwitch = true;
+ }
+
+ mService.doPendingActivityLaunchesLocked(false);
+
+ err = startActivityUncheckedLocked(r, sourceRecord, startFlags, true, options);
+ if (stack.mPausingActivity == null) {
+ // Someone asked to have the keyguard dismissed on the next
+ // activity start, but we are not actually doing an activity
+ // switch... just dismiss the keyguard now, because we
+ // probably want to see whatever is behind it.
+ dismissKeyguard();
+ }
+ return err;
+ }
+
+ ActivityStack getCorrectStack(ActivityRecord r) {
+ final TaskRecord task = r.task;
+ if (r.isApplicationActivity() || (task != null && task.isApplicationTask())) {
+ int stackNdx;
+ for (stackNdx = mStacks.size() - 1; stackNdx > 0; --stackNdx) {
+ if (mStacks.get(stackNdx).mCurrentUser == mCurrentUser) {
+ break;
+ }
+ }
+ if (stackNdx == 0) {
+ // Time to create the first app stack for this user.
+ int stackId = mService.createStack(-1, HOME_STACK_ID,
+ StackBox.TASK_STACK_GOES_OVER, 1.0f);
+ if (DEBUG_FOCUS) Slog.d(TAG, "getCorrectStack: New stack r=" + r + " stackId="
+ + stackId);
+ mFocusedStack = getStack(stackId);
+ }
+ if (task != null) {
+ if (DEBUG_FOCUS) Slog.d(TAG, "getCorrectStack: Setting focused stack to r=" +
+ r + " task=" + task);
+ mFocusedStack = task.stack;
+ }
+ return mFocusedStack;
+ }
+ return mHomeStack;
+ }
+
+ void setFocusedStack(ActivityRecord r) {
+ if (r == null) {
+ return;
+ }
+ if (!r.isApplicationActivity() || (r.task != null && !r.task.isApplicationTask())) {
+ if (mStackState != STACK_STATE_HOME_IN_FRONT) {
+ if (DEBUG_STACK || DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: mStackState old=" +
+ stackStateToString(mStackState) + " new=" +
+ stackStateToString(STACK_STATE_HOME_TO_FRONT) +
+ " Callers=" + Debug.getCallers(3));
+ mStackState = STACK_STATE_HOME_TO_FRONT;
+ }
+ } else {
+ if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: Setting focused stack to r=" +
+ r + " task=" + r.task + " Callers=" + Debug.getCallers(3));
+ mFocusedStack = r.task.stack;
+ if (mStackState != STACK_STATE_HOME_IN_BACK) {
+ if (DEBUG_STACK) Slog.d(TAG, "setFocusedStack: mStackState old=" +
+ stackStateToString(mStackState) + " new=" +
+ stackStateToString(STACK_STATE_HOME_TO_BACK) +
+ " Callers=" + Debug.getCallers(3));
+ mStackState = STACK_STATE_HOME_TO_BACK;
+ }
+ }
+ }
+
+ final int startActivityUncheckedLocked(ActivityRecord r,
+ ActivityRecord sourceRecord, int startFlags, boolean doResume,
+ Bundle options) {
+ final Intent intent = r.intent;
+ final int callingUid = r.launchedFromUid;
+
+ int launchFlags = intent.getFlags();
+
+ // 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;
+ if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving);
+
+ // If the caller has asked not to resume at this point, we make note
+ // of this in the record so that we can skip it when trying to find
+ // the top running activity.
+ if (!doResume) {
+ r.delayedResume = true;
+ }
+
+ 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
+ // a special case, if we do not know the caller then we count the
+ // current top activity as the caller.
+ if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ ActivityRecord checkedCaller = sourceRecord;
+ if (checkedCaller == null) {
+ checkedCaller = getFocusedStack().topRunningNonDelayedActivityLocked(notTop);
+ }
+ if (!checkedCaller.realActivity.equals(r.realActivity)) {
+ // Caller is not the same as launcher, so always needed.
+ startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
+ }
+ }
+
+ if (sourceRecord == null) {
+ // This activity is not being started from another... in this
+ // case we -always- start a new task.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
+ "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // The original activity who is starting us is running as a single
+ // instance... this new activity it is starting must go on its
+ // own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+ // The activity being started is a single instance... it always
+ // gets launched into its own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+
+ final ActivityStack sourceStack;
+ TaskRecord sourceTask;
+ if (sourceRecord != null) {
+ sourceTask = sourceRecord.task;
+ sourceStack = sourceTask.stack;
+ } else {
+ sourceTask = null;
+ sourceStack = null;
+ }
+
+ if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ // For whatever reason this activity is being launched into a new
+ // task... yet the caller has requested a result back. Well, that
+ // is pretty messed up, so instead immediately send back a cancel
+ // and let the new task continue launched as normal without a
+ // dependency on its originator.
+ Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+ r.resultTo.task.stack.sendActivityResultLocked(-1,
+ r.resultTo, r.resultWho, r.requestCode,
+ Activity.RESULT_CANCELED, null);
+ r.resultTo = null;
+ }
+
+ boolean addingToTask = false;
+ boolean movedHome = false;
+ TaskRecord reuseTask = null;
+ ActivityStack targetStack;
+ if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
+ (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // If bring to front is requested, and no result is requested, and
+ // we can find a task that was started with this same
+ // component, then instead of launching bring that one to the front.
+ if (r.resultTo == null) {
+ // See if there is a task to bring to the front. If this is
+ // a SINGLE_INSTANCE activity, there can be one and only one
+ // instance of it in the history, and it is always in its own
+ // unique task, so we do a special search.
+ ActivityRecord intentActivity = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
+ ? findTaskLocked(intent, r.info)
+ : findActivityLocked(intent, r.info);
+ if (intentActivity != null) {
+ if (r.task == null) {
+ r.task = intentActivity.task;
+ }
+ targetStack = intentActivity.task.stack;
+ moveHomeStack(targetStack.isHomeStack());
+ if (intentActivity.task.intent == null) {
+ // This task was started because of movement of
+ // the activity based on affinity... now that we
+ // are actually launching it, we can assign the
+ // base intent.
+ intentActivity.task.setIntent(intent, r.info);
+ }
+ // If the target task is not in the front, then we need
+ // to bring it to the front... except... well, with
+ // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
+ // to have the same behavior as if a new instance was
+ // being started, which means not bringing it to the front
+ // if the caller is not itself in the front.
+ final ActivityStack lastStack = getLastStack();
+ ActivityRecord curTop = lastStack == null?
+ null : lastStack.topRunningNonDelayedActivityLocked(notTop);
+ if (curTop != null && curTop.task != intentActivity.task) {
+ r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+ if (sourceRecord == null || sourceStack.topActivity() == sourceRecord) {
+ // We really do want to push this one into the
+ // user's face, right now.
+ movedHome = true;
+ if ((launchFlags &
+ (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, so before starting
+ // their own activity we will bring home to the front.
+ r.mLaunchHomeTaskNext = true;
+ }
+ targetStack.moveTaskToFrontLocked(intentActivity.task, r, options);
+ options = null;
+ }
+ }
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
+ }
+ if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it! And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
+ resumeTopActivitiesLocked(targetStack, null, options);
+ } else {
+ ActivityOptions.abort(options);
+ }
+ if (r.task == null) Slog.v(TAG,
+ "startActivityUncheckedLocked: task left null",
+ new RuntimeException("here").fillInStackTrace());
+ return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+ }
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
+ // The caller has requested to completely replace any
+ // existing task with its new activity. Well that should
+ // not be too hard...
+ reuseTask = intentActivity.task;
+ reuseTask.performClearTaskLocked();
+ reuseTask.setIntent(r.intent, r.info);
+ } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // In this situation we want to remove all activities
+ // from the task up to the one being started. In most
+ // cases this means we are resetting the task to its
+ // initial state.
+ ActivityRecord top =
+ intentActivity.task.performClearTaskLocked(r, launchFlags);
+ if (top != null) {
+ if (top.frontOfTask) {
+ // Activity aliases may mean we use different
+ // intents for the top activity, so make sure
+ // the task now has the identity of the new
+ // intent.
+ top.task.setIntent(r.intent, r.info);
+ }
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT,
+ r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ } else {
+ // A special case: we need to
+ // start the activity because it is not currently
+ // running, and the caller has asked to clear the
+ // current task to have this activity at the top.
+ addingToTask = true;
+ // Now pretend like this activity is being started
+ // by the top of its task, so it is put in the
+ // right place.
+ sourceRecord = intentActivity;
+ }
+ } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
+ // In this case the top activity on the task is the
+ // same as the one being launched, so we take that
+ // as a request to bring the task to the foreground.
+ // If the top activity in the task is the root
+ // activity, deliver this new intent to it if it
+ // desires.
+ if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
+ && intentActivity.realActivity.equals(r.realActivity)) {
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
+ intentActivity.task);
+ if (intentActivity.frontOfTask) {
+ intentActivity.task.setIntent(r.intent, r.info);
+ }
+ intentActivity.deliverNewIntentLocked(callingUid, r.intent);
+ } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
+ // In this case we are launching the root activity
+ // of the task, but with a different intent. We
+ // should start a new instance on top.
+ addingToTask = true;
+ sourceRecord = intentActivity;
+ }
+ } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+ // In this case an activity is being launched in to an
+ // existing task, without resetting that task. This
+ // is typically the situation of launching an activity
+ // from a notification or shortcut. We want to place
+ // the new activity on top of the current task.
+ addingToTask = true;
+ sourceRecord = intentActivity;
+ } else if (!intentActivity.task.rootWasReset) {
+ // In this case we are launching in to an existing task
+ // that has not yet been started from its front door.
+ // The current task has been brought to the front.
+ // Ideally, we'd probably like to place this new task
+ // at the bottom of its stack, but that's a little hard
+ // to do with the current organization of the code so
+ // for now we'll just drop it.
+ intentActivity.task.setIntent(r.intent, r.info);
+ }
+ if (!addingToTask && reuseTask == null) {
+ // We didn't do anything... but it was needed (a.k.a., client
+ // don't use that intent!) And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, intentActivity, targetStack);
+ targetStack.resumeTopActivityLocked(null, options);
+ } else {
+ ActivityOptions.abort(options);
+ }
+ if (r.task == null) Slog.v(TAG,
+ "startActivityUncheckedLocked: task left null",
+ new RuntimeException("here").fillInStackTrace());
+ return ActivityManager.START_TASK_TO_FRONT;
+ }
+ }
+ }
+ }
+
+ //String uri = r.intent.toURI();
+ //Intent intent2 = new Intent(uri);
+ //Slog.i(TAG, "Given intent: " + r.intent);
+ //Slog.i(TAG, "URI is: " + uri);
+ //Slog.i(TAG, "To intent: " + intent2);
+
+ if (r.packageName != null) {
+ // If the activity being launched is the same as the one currently
+ // at the top, then we need to check if it should only be launched
+ // once.
+ ActivityStack topStack = getFocusedStack();
+ ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
+ if (top != null && r.resultTo == null) {
+ if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
+ if (top.app != null && top.app.thread != null) {
+ if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top,
+ top.task);
+ // For paranoia, make sure we have correctly
+ // resumed the top activity.
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, null, topStack);
+ resumeTopActivitiesLocked();
+ }
+ ActivityOptions.abort(options);
+ if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it!
+ if (r.task == null) Slog.v(TAG,
+ "startActivityUncheckedLocked: task left null",
+ new RuntimeException("here").fillInStackTrace());
+ return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+ }
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ if (r.task == null) Slog.v(TAG,
+ "startActivityUncheckedLocked: task left null",
+ new RuntimeException("here").fillInStackTrace());
+ return ActivityManager.START_DELIVERED_TO_TOP;
+ }
+ }
+ }
+ }
+
+ } else {
+ if (r.resultTo != null) {
+ r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
+ r.requestCode, Activity.RESULT_CANCELED, null);
+ }
+ ActivityOptions.abort(options);
+ if (r.task == null) Slog.v(TAG,
+ "startActivityUncheckedLocked: task left null",
+ new RuntimeException("here").fillInStackTrace());
+ return ActivityManager.START_CLASS_NOT_FOUND;
+ }
+
+ boolean newTask = false;
+ boolean keepCurTransition = false;
+
+ // Should this be considered a new task?
+ if (r.resultTo == null && !addingToTask
+ && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ targetStack = getCorrectStack(r);
+ moveHomeStack(targetStack.isHomeStack());
+ if (reuseTask == null) {
+ r.setTask(targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
+ null, true);
+ 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.mLaunchHomeTaskNext = true;
+ }
+ }
+ } else if (sourceRecord != null) {
+ sourceTask = sourceRecord.task;
+ targetStack = sourceTask.stack;
+ moveHomeStack(targetStack.isHomeStack());
+ if (!addingToTask &&
+ (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+ // In this case, we are adding the activity to an existing
+ // task, but the caller has asked to clear that task if the
+ // activity is already running.
+ ActivityRecord top = sourceTask.performClearTaskLocked(r, launchFlags);
+ keepCurTransition = true;
+ if (top != null) {
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ // For paranoia, make sure we have correctly
+ // resumed the top activity.
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
+ targetStack.resumeTopActivityLocked(null);
+ }
+ ActivityOptions.abort(options);
+ if (r.task == null) Slog.v(TAG,
+ "startActivityUncheckedLocked: task left null",
+ new RuntimeException("here").fillInStackTrace());
+ return ActivityManager.START_DELIVERED_TO_TOP;
+ }
+ } else if (!addingToTask &&
+ (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
+ // In this case, we are launching an activity in our own task
+ // that may already be running somewhere in the history, and
+ // we want to shuffle it to the front of the stack if so.
+ final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
+ if (top != null) {
+ final TaskRecord task = top.task;
+ task.moveActivityToFrontLocked(top);
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
+ top.updateOptionsLocked(options);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
+ targetStack.resumeTopActivityLocked(null);
+ }
+ if (r.task == null) Slog.v(TAG,
+ "startActivityUncheckedLocked: task left null",
+ new RuntimeException("here").fillInStackTrace());
+ return ActivityManager.START_DELIVERED_TO_TOP;
+ }
+ }
+ // An existing activity is starting this new activity, so we want
+ // to keep the new one in the same task as the one that is starting
+ // it.
+ r.setTask(sourceTask, sourceRecord.thumbHolder, false);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in existing task " + r.task);
+
+ } else {
+ // 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 = getCorrectStack(r);
+ moveHomeStack(targetStack.isHomeStack());
+ ActivityRecord prev = targetStack.topActivity();
+ r.setTask(prev != null ? prev.task
+ : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
+ null, true);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new guessed " + r.task);
+ }
+
+ mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+ intent, r.getUriPermissionsLocked());
+
+ if (newTask) {
+ EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
+ }
+ ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
+ setLaunchHomeTaskNextFlag(sourceRecord, r, targetStack);
+ targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
+ mService.setFocusedActivityLocked(r);
+ return ActivityManager.START_SUCCESS;
+ }
+
+ void acquireLaunchWakelock() {
+ if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+ throw new IllegalStateException("Calling must be system uid");
+ }
+ mLaunchingActivity.acquire();
+ if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
+ // To be safe, don't allow the wake lock to be held for too long.
+ mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT);
+ }
+ }
+
+ // Checked.
+ final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
+ Configuration config) {
+ if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
+
+ ArrayList<ActivityRecord> stops = null;
+ ArrayList<ActivityRecord> finishes = null;
+ ArrayList<UserStartedState> startingUsers = null;
+ int NS = 0;
+ int NF = 0;
+ IApplicationThread sendThumbnail = null;
+ boolean booting = false;
+ boolean enableScreen = false;
+ boolean activityRemoved = false;
+
+ ActivityRecord r = ActivityRecord.forToken(token);
+ if (r != null) {
+ if (DEBUG_IDLE) Slog.d(TAG, "activityIdleInternalLocked: Callers=" +
+ Debug.getCallers(4));
+ mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+ r.finishLaunchTickingLocked();
+ if (fromTimeout) {
+ reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
+ }
+
+ // This is a hack to semi-deal with a race condition
+ // in the client where it can be constructed with a
+ // newer configuration from when we asked it to launch.
+ // We'll update with whatever configuration it now says
+ // it used to launch.
+ if (config != null) {
+ r.configuration = config;
+ }
+
+ // We are now idle. If someone is waiting for a thumbnail from
+ // us, we can now deliver.
+ r.idle = true;
+
+ if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
+ sendThumbnail = r.app.thread;
+ r.thumbnailNeeded = false;
+ }
+
+ //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
+ if (!mService.mBooted && isFrontStack(r.task.stack)) {
+ mService.mBooted = true;
+ enableScreen = true;
+ }
+ }
+
+ if (allResumedActivitiesIdle()) {
+ if (r != null) {
+ mService.scheduleAppGcsLocked();
+ mService.requestPssLocked(r.app, SystemClock.uptimeMillis(), false);
+ }
+
+ if (mLaunchingActivity.isHeld()) {
+ mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ if (VALIDATE_WAKE_LOCK_CALLER &&
+ Binder.getCallingUid() != Process.myUid()) {
+ throw new IllegalStateException("Calling must be system uid");
+ }
+ mLaunchingActivity.release();
+ }
+ ensureActivitiesVisibleLocked(null, 0);
+ }
+
+ // Atomically retrieve all of the other things to do.
+ stops = processStoppingActivitiesLocked(true);
+ NS = stops != null ? stops.size() : 0;
+ if ((NF=mFinishingActivities.size()) > 0) {
+ finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
+ mFinishingActivities.clear();
+ }
+
+ final ArrayList<ActivityRecord> thumbnails;
+ final int NT = mCancelledThumbnails.size();
+ if (NT > 0) {
+ thumbnails = new ArrayList<ActivityRecord>(mCancelledThumbnails);
+ mCancelledThumbnails.clear();
+ } else {
+ thumbnails = null;
+ }
+
+ if (isFrontStack(mHomeStack)) {
+ booting = mService.mBooting;
+ mService.mBooting = false;
+ }
+
+ if (mStartingUsers.size() > 0) {
+ startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
+ mStartingUsers.clear();
+ }
+
+ // Perform the following actions from unsynchronized state.
+ final IApplicationThread thumbnailThread = sendThumbnail;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (thumbnailThread != null) {
+ try {
+ thumbnailThread.requestThumbnail(token);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
+ mService.sendPendingThumbnail(null, token, null, null, true);
+ }
+ }
+
+ // Report back to any thumbnail receivers.
+ for (int i = 0; i < NT; i++) {
+ ActivityRecord r = thumbnails.get(i);
+ mService.sendPendingThumbnail(r, null, null, null, true);
+ }
+ }
+ });
+
+ // Stop any activities that are scheduled to do so but have been
+ // waiting for the next one to start.
+ for (int i = 0; i < NS; i++) {
+ r = stops.get(i);
+ final ActivityStack stack = r.task.stack;
+ if (r.finishing) {
+ stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
+ } else {
+ stack.stopActivityLocked(r);
+ }
+ }
+
+ // Finish any activities that are scheduled to do so but have been
+ // waiting for the next one to start.
+ for (int i = 0; i < NF; i++) {
+ r = finishes.get(i);
+ activityRemoved |= r.task.stack.destroyActivityLocked(r, true, false, "finish-idle");
+ }
+
+ if (booting) {
+ mService.finishBooting();
+ } else if (startingUsers != null) {
+ for (int i = 0; i < startingUsers.size(); i++) {
+ mService.finishUserSwitch(startingUsers.get(i));
+ }
+ }
+
+ mService.trimApplications();
+ //dump();
+ //mWindowManager.dump();
+
+ if (enableScreen) {
+ mService.enableScreenAfterBoot();
+ }
+
+ if (activityRemoved) {
+ resumeTopActivitiesLocked();
+ }
+
+ return r;
+ }
+
+ void handleAppDiedLocked(ProcessRecord app, boolean restarting) {
+ // Just in case.
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ mStacks.get(stackNdx).handleAppDiedLocked(app, restarting);
+ }
+ }
+
+ void closeSystemDialogsLocked() {
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.closeSystemDialogsLocked();
+ }
+ }
+
+ /**
+ * @return true if some activity was finished (or would have finished if doit were true).
+ */
+ boolean forceStopPackageLocked(String name, boolean doit, boolean evenPersistent, int userId) {
+ boolean didSomething = false;
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (stack.forceStopPackageLocked(name, doit, evenPersistent, userId)) {
+ didSomething = true;
+ }
+ }
+ return didSomething;
+ }
+
+ boolean resumeTopActivitiesLocked() {
+ return resumeTopActivitiesLocked(null, null, null);
+ }
+
+ boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
+ Bundle targetOptions) {
+ if (targetStack == null) {
+ targetStack = getFocusedStack();
+ }
+ boolean result = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (isFrontStack(stack)) {
+ if (stack == targetStack) {
+ result = stack.resumeTopActivityLocked(target, targetOptions);
+ } else {
+ stack.resumeTopActivityLocked(null);
+ }
+ }
+ }
+ return result;
+ }
+
+ void finishTopRunningActivityLocked(ProcessRecord app) {
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.finishTopRunningActivityLocked(app);
+ }
+ }
+
+ void findTaskToMoveToFrontLocked(int taskId, int flags, Bundle options) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ if (mStacks.get(stackNdx).findTaskToMoveToFrontLocked(taskId, flags, options)) {
+ if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack=" +
+ mStacks.get(stackNdx));
+ return;
+ }
+ }
+ }
+
+ ActivityStack getStack(int stackId) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (stack.getStackId() == stackId) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ ArrayList<ActivityStack> getStacks() {
+ return new ArrayList<ActivityStack>(mStacks);
+ }
+
+ int createStack() {
+ while (true) {
+ if (++mLastStackId <= HOME_STACK_ID) {
+ mLastStackId = HOME_STACK_ID + 1;
+ }
+ if (getStack(mLastStackId) == null) {
+ break;
+ }
+ }
+ mStacks.add(new ActivityStack(mService, mContext, mLooper, mLastStackId));
+ return mLastStackId;
+ }
+
+ void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+ final TaskRecord task = anyTaskForIdLocked(taskId);
+ if (task == null) {
+ return;
+ }
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId);
+ return;
+ }
+ removeTask(task);
+ stack.addTask(task, toTop);
+ mWindowManager.addTask(taskId, stackId, toTop);
+ resumeTopActivitiesLocked();
+ }
+
+ ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord ar = mStacks.get(stackNdx).findTaskLocked(intent, info);
+ if (ar != null) {
+ return ar;
+ }
+ }
+ return null;
+ }
+
+ ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord ar = mStacks.get(stackNdx).findActivityLocked(intent, info);
+ if (ar != null) {
+ return ar;
+ }
+ }
+ return null;
+ }
+
+ void goingToSleepLocked() {
+ scheduleSleepTimeout();
+ if (!mGoingToSleep.isHeld()) {
+ mGoingToSleep.acquire();
+ if (mLaunchingActivity.isHeld()) {
+ if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+ throw new IllegalStateException("Calling must be system uid");
+ }
+ mLaunchingActivity.release();
+ mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ }
+ }
+ }
+
+ boolean shutdownLocked(int timeout) {
+ boolean timedout = false;
+ goingToSleepLocked();
+ checkReadyForSleepLocked();
+
+ final long endTime = System.currentTimeMillis() + timeout;
+ while (true) {
+ boolean cantShutdown = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ cantShutdown |= mStacks.get(stackNdx).checkReadyForSleepLocked();
+ }
+ if (cantShutdown) {
+ long timeRemaining = endTime - System.currentTimeMillis();
+ if (timeRemaining > 0) {
+ try {
+ mService.wait(timeRemaining);
+ } catch (InterruptedException e) {
+ }
+ } else {
+ Slog.w(TAG, "Activity manager shutdown timed out");
+ timedout = true;
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ // Force checkReadyForSleep to complete.
+ mSleepTimeout = true;
+ checkReadyForSleepLocked();
+
+ return timedout;
+ }
+
+ void comeOutOfSleepIfNeededLocked() {
+ removeSleepTimeouts();
+ if (mGoingToSleep.isHeld()) {
+ mGoingToSleep.release();
+ }
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.awakeFromSleepingLocked();
+ if (isFrontStack(stack)) {
+ resumeTopActivitiesLocked();
+ }
+ }
+ mGoingToSleepActivities.clear();
+ }
+
+ void activitySleptLocked(ActivityRecord r) {
+ mGoingToSleepActivities.remove(r);
+ checkReadyForSleepLocked();
+ }
+
+ void checkReadyForSleepLocked() {
+ if (!mService.isSleepingOrShuttingDown()) {
+ // Do not care.
+ return;
+ }
+
+ if (!mSleepTimeout) {
+ boolean dontSleep = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ dontSleep |= mStacks.get(stackNdx).checkReadyForSleepLocked();
+ }
+
+ if (mStoppingActivities.size() > 0) {
+ // Still need to tell some activities to stop; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to stop "
+ + mStoppingActivities.size() + " activities");
+ scheduleIdleLocked();
+ dontSleep = true;
+ }
+
+ if (mGoingToSleepActivities.size() > 0) {
+ // Still need to tell some activities to sleep; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to sleep "
+ + mGoingToSleepActivities.size() + " activities");
+ dontSleep = true;
+ }
+
+ if (dontSleep) {
+ return;
+ }
+ }
+
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ mStacks.get(stackNdx).goToSleep();
+ }
+
+ removeSleepTimeouts();
+
+ if (mGoingToSleep.isHeld()) {
+ mGoingToSleep.release();
+ }
+ if (mService.mShuttingDown) {
+ mService.notifyAll();
+ }
+ }
+
+ boolean reportResumedActivityLocked(ActivityRecord r) {
+ final ActivityStack stack = r.task.stack;
+ if (isFrontStack(stack)) {
+ mService.updateUsageStats(r, true);
+ }
+ if (allResumedActivitiesComplete()) {
+ ensureActivitiesVisibleLocked(null, 0);
+ mWindowManager.executeAppTransition();
+ return true;
+ }
+ return false;
+ }
+
+ void handleAppCrashLocked(ProcessRecord app) {
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.handleAppCrashLocked(app);
+ }
+ }
+
+ void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
+ // First the front stacks. In case any are not fullscreen and are in front of home.
+ boolean showHomeBehindStack = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (isFrontStack(stack)) {
+ showHomeBehindStack =
+ stack.ensureActivitiesVisibleLocked(starting, configChanges);
+ }
+ }
+ // Now do back stacks.
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (!isFrontStack(stack)) {
+ stack.ensureActivitiesVisibleLocked(starting, configChanges, showHomeBehindStack);
+ }
+ }
+ }
+
+ void scheduleDestroyAllActivities(ProcessRecord app, String reason) {
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.scheduleDestroyActivities(app, false, reason);
+ }
+ }
+
+ boolean switchUserLocked(int userId, UserStartedState uss) {
+ mUserStates.put(mCurrentUser, new UserState());
+ mCurrentUser = userId;
+ UserState userState = mUserStates.get(userId);
+ if (userState != null) {
+ userState.restore();
+ mUserStates.delete(userId);
+ } else {
+ mFocusedStack = null;
+ if (DEBUG_STACK) Slog.d(TAG, "switchUserLocked: mStackState=" +
+ stackStateToString(STACK_STATE_HOME_IN_FRONT));
+ mStackState = STACK_STATE_HOME_IN_FRONT;
+ }
+
+ mStartingUsers.add(uss);
+ boolean haveActivities = mHomeStack.switchUserLocked(userId);
+
+ resumeTopActivitiesLocked();
+
+ return haveActivities;
+ }
+
+ final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
+ int N = mStoppingActivities.size();
+ if (N <= 0) return null;
+
+ ArrayList<ActivityRecord> stops = null;
+
+ final boolean nowVisible = allResumedActivitiesVisible();
+ for (int i=0; i<N; i++) {
+ ActivityRecord s = mStoppingActivities.get(i);
+ if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
+ + nowVisible + " waitingVisible=" + s.waitingVisible
+ + " finishing=" + s.finishing);
+ if (s.waitingVisible && nowVisible) {
+ mWaitingVisibleActivities.remove(s);
+ s.waitingVisible = false;
+ if (s.finishing) {
+ // If this activity is finishing, it is sitting on top of
+ // everyone else but we now know it is no longer needed...
+ // so get rid of it. Otherwise, we need to go through the
+ // normal flow and hide it once we determine that it is
+ // hidden by the activities in front of it.
+ if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
+ mWindowManager.setAppVisibility(s.appToken, false);
+ }
+ }
+ if ((!s.waitingVisible || mService.isSleepingOrShuttingDown()) && remove) {
+ if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
+ if (stops == null) {
+ stops = new ArrayList<ActivityRecord>();
+ }
+ stops.add(s);
+ mStoppingActivities.remove(i);
+ N--;
+ i--;
+ }
+ }
+
+ return stops;
+ }
+
+ void validateTopActivitiesLocked() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ final ActivityRecord r = stack.topRunningActivityLocked(null);
+ final ActivityState state = r == null ? ActivityState.DESTROYED : r.state;
+ if (isFrontStack(stack)) {
+ if (r == null) {
+ Slog.e(TAG, "validateTop...: null top activity, stack=" + stack);
+ } else {
+ final ActivityRecord pausing = stack.mPausingActivity;
+ if (pausing != null && pausing == r) {
+ Slog.e(TAG, "validateTop...: top stack has pausing activity r=" + r +
+ " state=" + state);
+ }
+ if (state != ActivityState.INITIALIZING && state != ActivityState.RESUMED) {
+ Slog.e(TAG, "validateTop...: activity in front not resumed r=" + r +
+ " state=" + state);
+ }
+ }
+ } else {
+ final ActivityRecord resumed = stack.mResumedActivity;
+ if (resumed != null && resumed == r) {
+ Slog.e(TAG, "validateTop...: back stack has resumed activity r=" + r +
+ " state=" + state);
+ }
+ if (r != null && (state == ActivityState.INITIALIZING
+ || state == ActivityState.RESUMED)) {
+ Slog.e(TAG, "validateTop...: activity in back resumed r=" + r +
+ " state=" + state);
+ }
+ }
+ }
+ }
+
+ private static String stackStateToString(int stackState) {
+ switch (stackState) {
+ case STACK_STATE_HOME_IN_FRONT: return "STACK_STATE_HOME_IN_FRONT";
+ case STACK_STATE_HOME_TO_BACK: return "STACK_STATE_HOME_TO_BACK";
+ case STACK_STATE_HOME_IN_BACK: return "STACK_STATE_HOME_IN_BACK";
+ case STACK_STATE_HOME_TO_FRONT: return "STACK_STATE_HOME_TO_FRONT";
+ default: return "Unknown stackState=" + stackState;
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mDismissKeyguardOnNextActivity:");
+ pw.println(mDismissKeyguardOnNextActivity);
+ pw.print(prefix); pw.print("mStackState="); pw.println(stackStateToString(mStackState));
+ pw.print(prefix); pw.println("mSleepTimeout: " + mSleepTimeout);
+ pw.print(prefix); pw.println("mCurTaskId: " + mCurTaskId);
+ }
+
+ ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
+ return getFocusedStack().getDumpActivitiesLocked(name);
+ }
+
+ static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage,
+ boolean needSep, String prefix) {
+ if (activity != null) {
+ if (dumpPackage == null || dumpPackage.equals(activity.packageName)) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.print(prefix);
+ pw.println(activity);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ boolean dumpClient, String dumpPackage) {
+ boolean printed = false;
+ boolean needSep = false;
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ StringBuilder stackHeader = new StringBuilder(128);
+ stackHeader.append(" Stack #");
+ stackHeader.append(mStacks.indexOf(stack));
+ stackHeader.append(":");
+ printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage, needSep,
+ stackHeader.toString());
+ printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false, !dumpAll,
+ false, dumpPackage, true, " Running activities (most recent first):", null);
+
+ needSep = printed;
+ boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
+ " mPausingActivity: ");
+ if (pr) {
+ printed = true;
+ needSep = false;
+ }
+ pr = printThisActivity(pw, stack.mResumedActivity, dumpPackage, needSep,
+ " mResumedActivity: ");
+ if (pr) {
+ printed = true;
+ needSep = false;
+ }
+ if (dumpAll) {
+ pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
+ " mLastPausedActivity: ");
+ if (pr) {
+ printed = true;
+ }
+ }
+ needSep = printed;
+ }
+
+ printed |= dumpHistoryList(fd, pw, mFinishingActivities, " ", "Fin", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to finish:", null);
+ printed |= dumpHistoryList(fd, pw, mStoppingActivities, " ", "Stop", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to stop:", null);
+ printed |= dumpHistoryList(fd, pw, mWaitingVisibleActivities, " ", "Wait", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting for another to become visible:",
+ null);
+ printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to sleep:", null);
+ printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to sleep:", null);
+
+ return printed;
+ }
+
+ static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
+ String prefix, String label, boolean complete, boolean brief, boolean client,
+ String dumpPackage, boolean needNL, String header1, String header2) {
+ TaskRecord lastTask = null;
+ String innerPrefix = null;
+ String[] args = null;
+ boolean printed = false;
+ for (int i=list.size()-1; i>=0; i--) {
+ final ActivityRecord r = list.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
+ continue;
+ }
+ if (innerPrefix == null) {
+ innerPrefix = prefix + " ";
+ args = new String[0];
+ }
+ printed = true;
+ final boolean full = !brief && (complete || !r.isInHistory());
+ if (needNL) {
+ pw.println("");
+ needNL = false;
+ }
+ if (header1 != null) {
+ pw.println(header1);
+ header1 = null;
+ }
+ if (header2 != null) {
+ pw.println(header2);
+ header2 = null;
+ }
+ if (lastTask != r.task) {
+ lastTask = r.task;
+ pw.print(prefix);
+ pw.print(full ? "* " : " ");
+ pw.println(lastTask);
+ if (full) {
+ lastTask.dump(pw, prefix + " ");
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ if (lastTask.intent != null) {
+ pw.print(prefix); pw.print(" ");
+ pw.println(lastTask.intent.toInsecureStringWithClip());
+ }
+ }
+ }
+ pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ pw.println(r);
+ if (full) {
+ r.dump(pw, innerPrefix);
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
+ if (r.app != null) {
+ pw.print(innerPrefix); pw.println(r.app);
+ }
+ }
+ if (client && r.app != null && r.app.thread != null) {
+ // flush anything that is already in the PrintWriter since the thread is going
+ // to write to the file descriptor directly
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
+ r.appToken, innerPrefix, args);
+ // Short timeout, since blocking here can
+ // deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+ } catch (RemoteException e) {
+ pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+ }
+ needNL = true;
+ }
+ }
+ return printed;
+ }
+
+ void scheduleIdleTimeoutLocked(ActivityRecord next) {
+ if (DEBUG_IDLE) Slog.d(TAG, "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
+ Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
+ mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
+ }
+
+ final void scheduleIdleLocked() {
+ mHandler.sendEmptyMessage(IDLE_NOW_MSG);
+ }
+
+ void removeTimeoutsForActivityLocked(ActivityRecord r) {
+ if (DEBUG_IDLE) Slog.d(TAG, "removeTimeoutsForActivity: Callers=" + Debug.getCallers(4));
+ mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+ }
+
+ final void scheduleResumeTopActivities() {
+ mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
+ }
+
+ void removeSleepTimeouts() {
+ mSleepTimeout = false;
+ mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
+ }
+
+ final void scheduleSleepTimeout() {
+ removeSleepTimeouts();
+ mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
+ }
+
+ private final class ActivityStackSupervisorHandler extends Handler {
+
+ public ActivityStackSupervisorHandler(Looper looper) {
+ super(looper);
+ }
+
+ void activityIdleInternal(ActivityRecord r) {
+ synchronized (mService) {
+ activityIdleInternalLocked(r != null ? r.appToken : null, true, null);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case IDLE_TIMEOUT_MSG: {
+ if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
+ if (mService.mDidDexOpt) {
+ mService.mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+ nmsg.obj = msg.obj;
+ mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
+ return;
+ }
+ // We don't at this point know if the activity is fullscreen,
+ // so we need to be conservative and assume it isn't.
+ activityIdleInternal((ActivityRecord)msg.obj);
+ } break;
+ case IDLE_NOW_MSG: {
+ if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
+ activityIdleInternal((ActivityRecord)msg.obj);
+ } break;
+ case RESUME_TOP_ACTIVITY_MSG: {
+ synchronized (mService) {
+ resumeTopActivitiesLocked();
+ }
+ } break;
+ case SLEEP_TIMEOUT_MSG: {
+ synchronized (mService) {
+ if (mService.isSleepingOrShuttingDown()) {
+ Slog.w(TAG, "Sleep timeout! Sleeping now.");
+ mSleepTimeout = true;
+ checkReadyForSleepLocked();
+ }
+ }
+ } break;
+ case LAUNCH_TIMEOUT_MSG: {
+ if (mService.mDidDexOpt) {
+ mService.mDidDexOpt = false;
+ mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT);
+ return;
+ }
+ synchronized (mService) {
+ if (mLaunchingActivity.isHeld()) {
+ Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
+ if (VALIDATE_WAKE_LOCK_CALLER
+ && Binder.getCallingUid() != Process.myUid()) {
+ throw new IllegalStateException("Calling must be system uid");
+ }
+ mLaunchingActivity.release();
+ }
+ }
+ } break;
+ }
+ }
+ }
+
+ private final class UserState {
+ final ActivityStack mSavedFocusedStack;
+ final int mSavedStackState;
+
+ public UserState() {
+ ActivityStackSupervisor supervisor = ActivityStackSupervisor.this;
+ mSavedFocusedStack = supervisor.mFocusedStack;
+ mSavedStackState = supervisor.mStackState;
+ }
+
+ void restore() {
+ ActivityStackSupervisor supervisor = ActivityStackSupervisor.this;
+ supervisor.mFocusedStack = mSavedFocusedStack;
+ if (DEBUG_STACK) Slog.d(TAG, "UserState.restore: mStackState old=" +
+ stackStateToString(mSavedStackState));
+ supervisor.mStackState = mSavedStackState;
+ }
+ }
+}
diff --git a/services/java/com/android/server/am/AppBindRecord.java b/services/java/com/android/server/am/AppBindRecord.java
index f1c54fa..06265fd 100644
--- a/services/java/com/android/server/am/AppBindRecord.java
+++ b/services/java/com/android/server/am/AppBindRecord.java
@@ -23,7 +23,7 @@
/**
* An association between a service and one of its client applications.
*/
-class AppBindRecord {
+final class AppBindRecord {
final ServiceRecord service; // The running service.
final IntentBindRecord intent; // The intent we are bound to.
final ProcessRecord client; // Who has started/bound the service.
diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java
index ffa1e92..bfc86b0 100644
--- a/services/java/com/android/server/am/AppErrorDialog.java
+++ b/services/java/com/android/server/am/AppErrorDialog.java
@@ -25,7 +25,7 @@
import android.os.Message;
import android.view.WindowManager;
-class AppErrorDialog extends BaseErrorDialog {
+final class AppErrorDialog extends BaseErrorDialog {
private final ActivityManagerService mService;
private final AppErrorResult mResult;
private final ProcessRecord mProc;
diff --git a/services/java/com/android/server/am/AppErrorResult.java b/services/java/com/android/server/am/AppErrorResult.java
index ebfcfe2..c6a5720 100644
--- a/services/java/com/android/server/am/AppErrorResult.java
+++ b/services/java/com/android/server/am/AppErrorResult.java
@@ -16,8 +16,7 @@
package com.android.server.am;
-
-class AppErrorResult {
+final class AppErrorResult {
public void set(int res) {
synchronized (this) {
mHasResult = true;
diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java
index af61c9b..b5e0715 100644
--- a/services/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/java/com/android/server/am/AppNotRespondingDialog.java
@@ -28,7 +28,7 @@
import android.util.Slog;
import android.view.WindowManager;
-class AppNotRespondingDialog extends BaseErrorDialog {
+final class AppNotRespondingDialog extends BaseErrorDialog {
private static final String TAG = "AppNotRespondingDialog";
// Event 'what' codes
diff --git a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
index d08bb10..27865a8 100644
--- a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
+++ b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
@@ -22,7 +22,7 @@
import android.os.Message;
import android.view.WindowManager;
-class AppWaitingForDebuggerDialog extends BaseErrorDialog {
+final class AppWaitingForDebuggerDialog extends BaseErrorDialog {
final ActivityManagerService mService;
final ProcessRecord mProc;
private CharSequence mAppName;
diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java
index 7e73106..5fa7e6a 100644
--- a/services/java/com/android/server/am/BackupRecord.java
+++ b/services/java/com/android/server/am/BackupRecord.java
@@ -21,7 +21,7 @@
import android.content.pm.ApplicationInfo;
/** @hide */
-class BackupRecord {
+final class BackupRecord {
// backup/restore modes
public static final int BACKUP_NORMAL = 0;
public static final int BACKUP_FULL = 1;
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index d19c7f6..12cad7b 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -22,11 +22,13 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.os.BatteryStats;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Process;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
@@ -58,7 +60,7 @@
public void publish(Context context) {
mContext = context;
- ServiceManager.addService("batteryinfo", asBinder());
+ ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps());
mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout)
@@ -76,7 +78,7 @@
if (sService != null) {
return sService;
}
- IBinder b = ServiceManager.getService("batteryinfo");
+ IBinder b = ServiceManager.getService(BatteryStats.SERVICE_NAME);
sService = asInterface(b);
return sService;
}
@@ -431,6 +433,7 @@
}
}
+ @Override
public void noteNetworkInterfaceType(String iface, int type) {
enforceCallingPermission();
synchronized (mStats) {
@@ -438,6 +441,14 @@
}
}
+ @Override
+ public void noteNetworkStatsEnabled() {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteNetworkStatsEnabledLocked();
+ }
+ }
+
public boolean isOnBattery() {
return mStats.isOnBattery();
}
@@ -469,12 +480,14 @@
}
private void dumpHelp(PrintWriter pw) {
- pw.println("Battery stats (batteryinfo) dump options:");
- pw.println(" [--checkin] [--reset] [--write] [-h]");
+ pw.println("Battery stats (batterystats) dump options:");
+ pw.println(" [--checkin] [--unplugged] [--reset] [--write] [-h] [<package.name>]");
pw.println(" --checkin: format output for a checkin report.");
+ pw.println(" --unplugged: only output data since last unplugged.");
pw.println(" --reset: reset the stats, clearing all current data.");
pw.println(" --write: force write current collected stats to disk.");
pw.println(" -h: print this help text.");
+ pw.println(" <package.name>: optional name of package to filter output by.");
}
@Override
@@ -488,11 +501,15 @@
}
boolean isCheckin = false;
+ boolean isUnpluggedOnly = false;
boolean noOutput = false;
+ int reqUid = -1;
if (args != null) {
for (String arg : args) {
if ("--checkin".equals(arg)) {
isCheckin = true;
+ } else if ("--unplugged".equals(arg)) {
+ isUnpluggedOnly = true;
} else if ("--reset".equals(arg)) {
synchronized (mStats) {
mStats.resetAllStatsLocked();
@@ -510,9 +527,20 @@
return;
} else if ("-a".equals(arg)) {
// fall through
- } else {
+ } else if (arg.length() > 0 && arg.charAt(0) == '-'){
pw.println("Unknown option: " + arg);
dumpHelp(pw);
+ return;
+ } else {
+ // Not an option, last argument must be a package name.
+ try {
+ reqUid = mContext.getPackageManager().getPackageUid(arg,
+ UserHandle.getCallingUserId());
+ } catch (PackageManager.NameNotFoundException e) {
+ pw.println("Unknown package: " + arg);
+ dumpHelp(pw);
+ return;
+ }
}
}
}
@@ -522,11 +550,11 @@
if (isCheckin) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
synchronized (mStats) {
- mStats.dumpCheckinLocked(pw, args, apps);
+ mStats.dumpCheckinLocked(pw, apps, isUnpluggedOnly);
}
} else {
synchronized (mStats) {
- mStats.dumpLocked(pw);
+ mStats.dumpLocked(pw, isUnpluggedOnly, reqUid);
}
}
}
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index c631b6e..986b8ea 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -22,7 +22,7 @@
import java.io.PrintWriter;
-class BroadcastFilter extends IntentFilter {
+final class BroadcastFilter extends IntentFilter {
// Back-pointer to the list this filter is in.
final ReceiverList receiverList;
final String packageName;
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index ac7eb89..5238bd1 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -47,7 +47,7 @@
* We keep two broadcast queues and associated bookkeeping, one for those at
* foreground priority, and one for normal (background-priority) broadcasts.
*/
-public class BroadcastQueue {
+public final class BroadcastQueue {
static final String TAG = "BroadcastQueue";
static final String TAG_MU = ActivityManagerService.TAG_MU;
static final boolean DEBUG_BROADCAST = ActivityManagerService.DEBUG_BROADCAST;
@@ -410,7 +410,7 @@
}
}
if (r.appOp != AppOpsManager.OP_NONE) {
- int mode = mService.mAppOpsService.checkOperation(r.appOp,
+ int mode = mService.mAppOpsService.noteOperation(r.appOp,
filter.receiverList.uid, filter.packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG_BROADCAST) Slog.v(TAG,
@@ -717,7 +717,7 @@
}
}
if (r.appOp != AppOpsManager.OP_NONE) {
- int mode = mService.mAppOpsService.checkOperation(r.appOp,
+ int mode = mService.mAppOpsService.noteOperation(r.appOp,
info.activityInfo.applicationInfo.uid, info.activityInfo.packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG_BROADCAST) Slog.v(TAG,
@@ -795,7 +795,7 @@
info.activityInfo.applicationInfo.uid);
if (app != null && app.thread != null) {
try {
- app.addPackage(info.activityInfo.packageName);
+ app.addPackage(info.activityInfo.packageName, mService.mProcessTracker);
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 83cc0ea..eb1df6e 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -36,7 +36,7 @@
/**
* An active intent broadcast.
*/
-class BroadcastRecord extends Binder {
+final class BroadcastRecord extends Binder {
final Intent intent; // the original intent that generated us
final ComponentName targetComp; // original component name set on the intent
final ProcessRecord callerApp; // process that sent this
diff --git a/services/java/com/android/server/am/CompatModeDialog.java b/services/java/com/android/server/am/CompatModeDialog.java
index 0442bda..202cc7c 100644
--- a/services/java/com/android/server/am/CompatModeDialog.java
+++ b/services/java/com/android/server/am/CompatModeDialog.java
@@ -28,7 +28,7 @@
import android.widget.CompoundButton;
import android.widget.Switch;
-public class CompatModeDialog extends Dialog {
+public final class CompatModeDialog extends Dialog {
final ActivityManagerService mService;
final ApplicationInfo mAppInfo;
diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java
index 3a6492e..59e6787 100644
--- a/services/java/com/android/server/am/CompatModePackages.java
+++ b/services/java/com/android/server/am/CompatModePackages.java
@@ -15,7 +15,6 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.res.CompatibilityInfo;
@@ -26,7 +25,7 @@
import android.util.Slog;
import android.util.Xml;
-public class CompatModePackages {
+public final class CompatModePackages {
private final String TAG = ActivityManagerService.TAG;
private final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION;
@@ -166,7 +165,7 @@
}
public boolean getFrontActivityAskCompatModeLocked() {
- ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
if (r == null) {
return false;
}
@@ -178,7 +177,7 @@
}
public void setFrontActivityAskCompatModeLocked(boolean ask) {
- ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
if (r != null) {
setPackageAskCompatModeLocked(r.packageName, ask);
}
@@ -200,7 +199,7 @@
}
public int getFrontActivityScreenCompatModeLocked() {
- ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
if (r == null) {
return ActivityManager.COMPAT_MODE_UNKNOWN;
}
@@ -208,7 +207,7 @@
}
public void setFrontActivityScreenCompatModeLocked(int mode) {
- ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
if (r == null) {
Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
return;
@@ -295,25 +294,13 @@
Message msg = mHandler.obtainMessage(MSG_WRITE);
mHandler.sendMessageDelayed(msg, 10000);
- ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);
-
- // All activities that came from the package must be
- // restarted as if there was a config change.
- for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
- ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
- if (a.info.packageName.equals(packageName)) {
- a.forceNewConfig = true;
- if (starting != null && a == starting && a.visible) {
- a.startFreezingScreenLocked(starting.app,
- ActivityInfo.CONFIG_SCREEN_LAYOUT);
- }
- }
- }
+ final ActivityStack stack = mService.getFocusedStack();
+ ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord app = mService.mLruProcesses.get(i);
- if (!app.pkgList.contains(packageName)) {
+ if (!app.pkgList.containsKey(packageName)) {
continue;
}
try {
@@ -327,10 +314,10 @@
}
if (starting != null) {
- mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
+ stack.ensureActivityConfigurationLocked(starting, 0);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
- mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0);
+ stack.ensureActivitiesVisibleLocked(starting, 0);
}
}
}
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index 4ed3c31..dd81493 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -25,7 +25,7 @@
/**
* Description of a single binding to a service.
*/
-class ConnectionRecord {
+final class ConnectionRecord {
final AppBindRecord binding; // The application/service binding.
final ActivityRecord activity; // If non-null, the owning activity.
final IServiceConnection conn; // The client connection.
diff --git a/services/java/com/android/server/am/ContentProviderConnection.java b/services/java/com/android/server/am/ContentProviderConnection.java
index 7f69b24..f2c9e2f 100644
--- a/services/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/java/com/android/server/am/ContentProviderConnection.java
@@ -23,7 +23,7 @@
/**
* Represents a link between a content provider and client.
*/
-public class ContentProviderConnection extends Binder {
+public final class ContentProviderConnection extends Binder {
public final ContentProviderRecord provider;
public final ProcessRecord client;
public final long createTime;
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index 8fb6a93..646b7d2 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -33,7 +33,7 @@
import java.util.HashMap;
import java.util.HashSet;
-class ContentProviderRecord {
+final class ContentProviderRecord {
final ActivityManagerService service;
public final ProviderInfo info;
final int uid;
diff --git a/services/java/com/android/server/am/CoreSettingsObserver.java b/services/java/com/android/server/am/CoreSettingsObserver.java
index 585cf2b..10ea67c 100644
--- a/services/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/java/com/android/server/am/CoreSettingsObserver.java
@@ -33,7 +33,7 @@
* disk I/O operations. Note: This class assumes that all core settings reside
* in {@link Settings.Secure}.
*/
-class CoreSettingsObserver extends ContentObserver {
+final class CoreSettingsObserver extends ContentObserver {
private static final String LOG_TAG = CoreSettingsObserver.class.getSimpleName();
// mapping form property name to its type
diff --git a/services/java/com/android/server/am/DeviceMonitor.java b/services/java/com/android/server/am/DeviceMonitor.java
deleted file mode 100644
index 21e7252..0000000
--- a/services/java/com/android/server/am/DeviceMonitor.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.am;
-
-import android.os.SELinux;
-import android.util.Slog;
-
-import java.io.*;
-import java.util.Arrays;
-
-/**
- * Monitors device resources periodically for some period of time. Useful for
- * tracking down performance problems.
- */
-class DeviceMonitor {
-
- private static final String LOG_TAG = DeviceMonitor.class.getName();
-
- /** Number of samples to take. */
- private static final int SAMPLE_COUNT = 10;
-
- /** Time to wait in ms between samples. */
- private static final int INTERVAL = 1000;
-
- /** Time to wait in ms between samples. */
- private static final int MAX_FILES = 30;
-
- private final byte[] buffer = new byte[1024];
-
- /** Is the monitor currently running? */
- private boolean running = false;
-
- private DeviceMonitor() {
- new Thread() {
- public void run() {
- monitor();
- }
- }.start();
- }
-
- /**
- * Loops continuously. Pauses until someone tells us to start monitoring.
- */
- @SuppressWarnings("InfiniteLoopStatement")
- private void monitor() {
- while (true) {
- waitForStart();
-
- purge();
-
- for (int i = 0; i < SAMPLE_COUNT; i++) {
- try {
- dump();
- } catch (IOException e) {
- Slog.w(LOG_TAG, "Dump failed.", e);
- }
- pause();
- }
-
- stop();
- }
- }
-
- private static final File PROC = new File("/proc");
- private static final File BASE = new File("/data/anr/");
- static {
- if (!BASE.isDirectory() && !BASE.mkdirs()) {
- throw new AssertionError("Couldn't create " + BASE + ".");
- }
- if (!SELinux.restorecon(BASE)) {
- throw new AssertionError("Couldn't restorecon " + BASE + ".");
- }
- }
-
- private static final File[] PATHS = {
- new File(PROC, "zoneinfo"),
- new File(PROC, "interrupts"),
- new File(PROC, "meminfo"),
- new File(PROC, "slabinfo"),
- };
-
-
- /**
- * Deletes old files.
- */
- private void purge() {
- File[] files = BASE.listFiles();
- int count = files.length - MAX_FILES;
- if (count > 0) {
- Arrays.sort(files);
- for (int i = 0; i < count; i++) {
- if (!files[i].delete()) {
- Slog.w(LOG_TAG, "Couldn't delete " + files[i] + ".");
- }
- }
- }
- }
-
- /**
- * Dumps the current device stats to a new file.
- */
- private void dump() throws IOException {
- OutputStream out = new FileOutputStream(
- new File(BASE, String.valueOf(System.currentTimeMillis())));
- try {
- // Copy /proc/*/stat
- for (File processDirectory : PROC.listFiles()) {
- if (isProcessDirectory(processDirectory)) {
- dump(new File(processDirectory, "stat"), out);
- }
- }
-
- // Copy other files.
- for (File file : PATHS) {
- dump(file, out);
- }
- } finally {
- closeQuietly(out);
- }
- }
-
- /**
- * Returns true if the given file represents a process directory.
- */
- private static boolean isProcessDirectory(File file) {
- try {
- Integer.parseInt(file.getName());
- return file.isDirectory();
- } catch (NumberFormatException e) {
- return false;
- }
- }
-
- /**
- * Copies from a file to an output stream.
- */
- private void dump(File from, OutputStream out) throws IOException {
- writeHeader(from, out);
-
- FileInputStream in = null;
- try {
- in = new FileInputStream(from);
- int count;
- while ((count = in.read(buffer)) != -1) {
- out.write(buffer, 0, count);
- }
- } finally {
- closeQuietly(in);
- }
- }
-
- /**
- * Writes a header for the given file.
- */
- private static void writeHeader(File file, OutputStream out)
- throws IOException {
- String header = "*** " + file.toString() + "\n";
- out.write(header.getBytes());
- }
-
- /**
- * Closes the given resource. Logs exceptions.
- * @param closeable
- */
- private static void closeQuietly(Closeable closeable) {
- try {
- if (closeable != null) {
- closeable.close();
- }
- } catch (IOException e) {
- Slog.w(LOG_TAG, e);
- }
- }
-
- /**
- * Pauses momentarily before we start the next dump.
- */
- private void pause() {
- try {
- Thread.sleep(INTERVAL);
- } catch (InterruptedException e) { /* ignore */ }
- }
-
- /**
- * Stops dumping.
- */
- private synchronized void stop() {
- running = false;
- }
-
- /**
- * Waits until someone starts us.
- */
- private synchronized void waitForStart() {
- while (!running) {
- try {
- wait();
- } catch (InterruptedException e) { /* ignore */ }
- }
- }
-
- /**
- * Instructs the monitoring to start if it hasn't already.
- */
- private synchronized void startMonitoring() {
- if (!running) {
- running = true;
- notifyAll();
- }
- }
-
- private static DeviceMonitor instance = new DeviceMonitor();
-
- /**
- * Starts monitoring if it hasn't started already.
- */
- static void start() {
- instance.startMonitoring();
- }
-}
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index f784861..f4bf9f3 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -86,3 +86,6 @@
# User switched
30041 am_switch_user (id|1|5)
+
+# Activity fully drawn time
+30042 am_activity_fully_drawn_time (User|1|5),(Token|1|5),(Component Name|3),(time|2|3)
diff --git a/services/java/com/android/server/am/FactoryErrorDialog.java b/services/java/com/android/server/am/FactoryErrorDialog.java
index 0ffb588..f4632c1 100644
--- a/services/java/com/android/server/am/FactoryErrorDialog.java
+++ b/services/java/com/android/server/am/FactoryErrorDialog.java
@@ -22,7 +22,7 @@
import android.os.Message;
import android.view.WindowManager;
-class FactoryErrorDialog extends BaseErrorDialog {
+final class FactoryErrorDialog extends BaseErrorDialog {
public FactoryErrorDialog(Context context, CharSequence msg) {
super(context);
setCancelable(false);
diff --git a/services/java/com/android/server/am/IntentBindRecord.java b/services/java/com/android/server/am/IntentBindRecord.java
index 0a92964..6c84b9f 100644
--- a/services/java/com/android/server/am/IntentBindRecord.java
+++ b/services/java/com/android/server/am/IntentBindRecord.java
@@ -27,7 +27,7 @@
/**
* A particular Intent that has been bound to a Service.
*/
-class IntentBindRecord {
+final class IntentBindRecord {
/** The running service. */
final ServiceRecord service;
/** The intent that is bound.*/
diff --git a/services/java/com/android/server/am/LaunchWarningWindow.java b/services/java/com/android/server/am/LaunchWarningWindow.java
index cb2b7bb..30c3066 100644
--- a/services/java/com/android/server/am/LaunchWarningWindow.java
+++ b/services/java/com/android/server/am/LaunchWarningWindow.java
@@ -26,7 +26,7 @@
import android.widget.ImageView;
import android.widget.TextView;
-public class LaunchWarningWindow extends Dialog {
+public final class LaunchWarningWindow extends Dialog {
public LaunchWarningWindow(Context context, ActivityRecord cur, ActivityRecord next) {
super(context, R.style.Theme_Toast);
diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java
index a9454bd..2c7f1f1 100644
--- a/services/java/com/android/server/am/NativeCrashListener.java
+++ b/services/java/com/android/server/am/NativeCrashListener.java
@@ -40,7 +40,7 @@
*
* Note that this component runs in a separate thread.
*/
-class NativeCrashListener extends Thread {
+final class NativeCrashListener extends Thread {
static final String TAG = "NativeCrashListener";
static final boolean DEBUG = false;
static final boolean MORE_DEBUG = DEBUG && false;
@@ -152,6 +152,13 @@
Slog.d(TAG, "Exception writing ack: " + e.getMessage());
}
}
+ try {
+ Libcore.os.close(peerFd);
+ } catch (ErrnoException e) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Exception closing socket: " + e.getMessage());
+ }
+ }
}
}
}
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 8ab71dd..17f24a9 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -31,7 +31,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-class PendingIntentRecord extends IIntentSender.Stub {
+final class PendingIntentRecord extends IIntentSender.Stub {
final ActivityManagerService owner;
final Key key;
final int uid;
@@ -259,7 +259,7 @@
}
break;
case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
- key.activity.stack.sendActivityResultLocked(-1, key.activity,
+ key.activity.task.stack.sendActivityResultLocked(-1, key.activity,
key.who, key.requestCode, code, finalIntent);
break;
case ActivityManager.INTENT_SENDER_BROADCAST:
diff --git a/services/java/com/android/server/am/PendingThumbnailsRecord.java b/services/java/com/android/server/am/PendingThumbnailsRecord.java
index ed478c9..e4eb4d0 100644
--- a/services/java/com/android/server/am/PendingThumbnailsRecord.java
+++ b/services/java/com/android/server/am/PendingThumbnailsRecord.java
@@ -24,16 +24,16 @@
* This class keeps track of calls to getTasks() that are still
* waiting for thumbnail images.
*/
-class PendingThumbnailsRecord
+final class PendingThumbnailsRecord
{
final IThumbnailReceiver receiver; // who is waiting.
- HashSet pendingRecords; // HistoryRecord objects we still wait for.
+ final HashSet<ActivityRecord> pendingRecords; // HistoryRecord objects we still wait for.
boolean finished; // Is pendingRecords empty?
PendingThumbnailsRecord(IThumbnailReceiver _receiver)
{
receiver = _receiver;
- pendingRecords = new HashSet();
+ pendingRecords = new HashSet<ActivityRecord>();
finished = false;
}
}
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index 1a635a9..6a72e44f 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -29,7 +29,7 @@
/**
* Activity manager code dealing with processes.
*/
-class ProcessList {
+final class ProcessList {
// The minimum time we allow between crashes, for us to consider this
// application to be bad and stop and its services and reject broadcasts.
static final int MIN_CRASH_INTERVAL = 60*1000;
@@ -38,8 +38,8 @@
// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
- static final int HIDDEN_APP_MAX_ADJ = 15;
- static int HIDDEN_APP_MIN_ADJ = 9;
+ static final int CACHED_APP_MAX_ADJ = 15;
+ static final int CACHED_APP_MIN_ADJ = 9;
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
@@ -94,37 +94,46 @@
// Memory pages are 4K.
static final int PAGE_SIZE = 4*1024;
- // The minimum number of hidden apps we want to be able to keep around,
+ // The minimum number of cached apps we want to be able to keep around,
// without empty apps being able to push them out of memory.
- static final int MIN_HIDDEN_APPS = 2;
+ static final int MIN_CACHED_APPS = 2;
- // The maximum number of hidden processes we will keep around before
- // killing them; this is just a control to not let us go too crazy with
- // keeping around processes on devices with large amounts of RAM.
- static final int MAX_HIDDEN_APPS = 24;
+ // The maximum number of cached processes we will keep around before killing them.
+ // NOTE: this constant is *only* a control to not let us go too crazy with
+ // keeping around processes on devices with large amounts of RAM. For devices that
+ // are tighter on RAM, the out of memory killer is responsible for killing background
+ // processes as RAM is needed, and we should *never* be relying on this limit to
+ // kill them. Also note that this limit only applies to cached background processes;
+ // we have no limit on the number of service, visible, foreground, or other such
+ // processes and the number of those processes does not count against the cached
+ // process limit.
+ static final int MAX_CACHED_APPS = 24;
// We allow empty processes to stick around for at most 30 minutes.
static final long MAX_EMPTY_TIME = 30*60*1000;
- // The number of hidden at which we don't consider it necessary to do
- // memory trimming.
- static final int TRIM_HIDDEN_APPS = 3;
+ // The maximum number of empty app processes we will let sit around.
+ private static final int MAX_EMPTY_APPS = computeEmptyProcessLimit(MAX_CACHED_APPS);
// The number of empty apps at which we don't consider it necessary to do
// memory trimming.
- static final int TRIM_EMPTY_APPS = 3;
+ static final int TRIM_EMPTY_APPS = MAX_EMPTY_APPS/2;
- // Threshold of number of hidden+empty where we consider memory critical.
+ // The number of cached at which we don't consider it necessary to do
+ // memory trimming.
+ static final int TRIM_CACHED_APPS = (MAX_CACHED_APPS-MAX_EMPTY_APPS)/2;
+
+ // Threshold of number of cached+empty where we consider memory critical.
static final int TRIM_CRITICAL_THRESHOLD = 3;
- // Threshold of number of hidden+empty where we consider memory critical.
+ // Threshold of number of cached+empty where we consider memory critical.
static final int TRIM_LOW_THRESHOLD = 5;
- // We put empty content processes after any hidden processes that have
+ // We put empty content processes after any cached processes that have
// been idle for less than 15 seconds.
static final long CONTENT_APP_IDLE_OFFSET = 15*1000;
- // We put empty content processes after any hidden processes that have
+ // We put empty content processes after any cached processes that have
// been idle for less than 120 seconds.
static final long EMPTY_APP_IDLE_OFFSET = 120*1000;
@@ -133,7 +142,7 @@
// can't give it a different value for every possible kind of process.
private final int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
- BACKUP_APP_ADJ, HIDDEN_APP_MIN_ADJ, HIDDEN_APP_MAX_ADJ
+ BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
};
// These are the low-end OOM level limits. This is appropriate for an
// HVGA or smaller phone with less than 512MB. Values are in KB.
@@ -154,11 +163,34 @@
private boolean mHaveDisplaySize;
+ private final int[] mAdjToTrackedState = new int[CACHED_APP_MAX_ADJ+1];
+
ProcessList() {
MemInfoReader minfo = new MemInfoReader();
minfo.readMemInfo();
mTotalMemMb = minfo.getTotalSize()/(1024*1024);
updateOomLevels(0, 0, false);
+ for (int i=0; i<=CACHED_APP_MAX_ADJ; i++) {
+ if (i <= FOREGROUND_APP_ADJ) {
+ mAdjToTrackedState[i] = ProcessTracker.STATE_FOREGROUND;
+ } else if (i <= VISIBLE_APP_ADJ) {
+ mAdjToTrackedState[i] = ProcessTracker.STATE_VISIBLE;
+ } else if (i <= PERCEPTIBLE_APP_ADJ) {
+ mAdjToTrackedState[i] = ProcessTracker.STATE_PERCEPTIBLE;
+ } else if (i <= BACKUP_APP_ADJ) {
+ mAdjToTrackedState[i] = ProcessTracker.STATE_BACKUP;
+ } else if (i <= SERVICE_ADJ) {
+ mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE;
+ } else if (i <= HOME_APP_ADJ) {
+ mAdjToTrackedState[i] = ProcessTracker.STATE_HOME;
+ } else if (i <= PREVIOUS_APP_ADJ) {
+ mAdjToTrackedState[i] = ProcessTracker.STATE_PREVIOUS;
+ } else if (i <= SERVICE_B_ADJ) {
+ mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE;
+ } else {
+ mAdjToTrackedState[i] = ProcessTracker.STATE_CACHED;
+ }
+ }
}
void applyDisplaySize(WindowManagerService wm) {
@@ -211,6 +243,10 @@
// HC: 8192,10240,12288,14336,16384,20480
}
+ public static int computeEmptyProcessLimit(int totalProcessLimit) {
+ return (totalProcessLimit*2)/3;
+ }
+
long getMemLevel(int adjustment) {
for (int i=0; i<mOomAdj.length; i++) {
if (adjustment <= mOomAdj[i]) {
@@ -220,6 +256,11 @@
return mOomMinFree[mOomAdj.length-1] * 1024;
}
+ int adjToTrackedState(int adj) {
+ return adj >= FOREGROUND_APP_ADJ
+ ? mAdjToTrackedState[adj] : ProcessTracker.STATE_PERSISTENT;
+ }
+
private void writeFile(String path, String data) {
FileOutputStream fos = null;
try {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 7929f96..21a6ff0 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -32,27 +32,29 @@
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.PrintWriterPrinter;
import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
/**
* Full information about a particular process that
* is currently running.
*/
-class ProcessRecord {
+final class ProcessRecord {
final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics
final ApplicationInfo info; // all about the first app in the process
final boolean isolated; // true if this is a special isolated process
final int uid; // uid of process; may be different from 'info' if isolated
final int userId; // user of process.
final String processName; // name of the process
+ final ProcessTracker.ProcessState baseProcessTracker;
// List of packages running in the process
- final HashSet<String> pkgList = new HashSet<String>();
+ final ArrayMap<String, ProcessTracker.ProcessState> pkgList
+ = new ArrayMap<String, ProcessTracker.ProcessState>();
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
// are in the process of launching the app)
@@ -60,9 +62,10 @@
boolean starting; // True if the process is being started
long lastActivityTime; // For managing the LRU list
long lruWeight; // Weight for ordering in LRU list
+ long lastPssTime; // Last time we requested PSS data
int maxAdj; // Maximum OOM adjustment for this process
- int hiddenAdj; // If hidden, this is the adjustment to use
- int clientHiddenAdj; // If empty but hidden client, this is the adjustment to use
+ int cachedAdj; // If cached, this is the adjustment to use
+ int clientCachedAdj; // If empty but cached client, this is the adjustment to use
int emptyAdj; // If empty, this is the adjustment to use
int curRawAdj; // Current OOM unlimited adjustment for this process
int setRawAdj; // Last set OOM unlimited adjustment for this process
@@ -78,6 +81,7 @@
boolean setIsForeground; // Running foreground UI when last set?
boolean hasActivities; // Are there any activities running in this process?
boolean hasClientActivities; // Are there any client services with activities?
+ boolean hasStartedServices; // Are there any started services running in this process?
boolean foregroundServices; // Running any services that are foreground?
boolean foregroundActivities; // Running any activities that are foreground?
boolean systemNoUi; // This is a system process, but not currently showing UI.
@@ -86,6 +90,7 @@
boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so want to be lower
boolean bad; // True if disabled in the bad process list
boolean killedBackground; // True when proc has been killed due to too many bg
+ boolean setAdjChanged; // Keep track of whether we changed 'setAdj'.
String waitingToKill; // Process is waiting to be killed when in the bg; reason
IBinder forcingToForeground;// Token that is forcing this process to be foreground
int adjSeq; // Sequence id for identifying oom_adj assignment cycles
@@ -108,8 +113,7 @@
long lastLowMemory; // When we last told the app that memory is low
boolean reportLowMemory; // Set to true when waiting to report low mem
boolean empty; // Is this an empty background process?
- boolean hidden; // Is this a hidden process?
- int lastPss; // Last pss size reported by app.
+ boolean cached; // Is this a cached process?
String adjType; // Debugging: primary thing impacting oom_adj.
int adjTypeCode; // Debugging: adj code to report to app.
Object adjSource; // Debugging: option dependent object.
@@ -129,8 +133,8 @@
// all IIntentReceivers that are registered from this process.
final HashSet<ReceiverList> receivers = new HashSet<ReceiverList>();
// class (String) -> ContentProviderRecord
- final HashMap<String, ContentProviderRecord> pubProviders
- = new HashMap<String, ContentProviderRecord>();
+ final ArrayMap<String, ContentProviderRecord> pubProviders
+ = new ArrayMap<String, ContentProviderRecord>();
// All ContentProviderRecord process is using
final ArrayList<ContentProviderConnection> conProviders
= new ArrayList<ContentProviderConnection>();
@@ -177,7 +181,12 @@
pw.print(prefix); pw.print("dir="); pw.print(info.sourceDir);
pw.print(" publicDir="); pw.print(info.publicSourceDir);
pw.print(" data="); pw.println(info.dataDir);
- pw.print(prefix); pw.print("packageList="); pw.println(pkgList);
+ pw.print(prefix); pw.print("packageList={");
+ for (int i=0; i<pkgList.size(); i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(pkgList.keyAt(i));
+ }
+ pw.println("}");
pw.print(prefix); pw.print("compat="); pw.println(compat);
if (instrumentationClass != null || instrumentationProfileFile != null
|| instrumentationArguments != null) {
@@ -195,17 +204,17 @@
}
pw.print(prefix); pw.print("thread="); pw.println(thread);
pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
- pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
+ pw.println(starting);
pw.print(prefix); pw.print("lastActivityTime=");
TimeUtils.formatDuration(lastActivityTime, now, pw);
pw.print(" lruWeight="); pw.print(lruWeight);
pw.print(" serviceb="); pw.print(serviceb);
pw.print(" keeping="); pw.print(keeping);
- pw.print(" hidden="); pw.print(hidden);
+ pw.print(" cached="); pw.print(cached);
pw.print(" empty="); pw.println(empty);
pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
- pw.print(" hidden="); pw.print(hiddenAdj);
- pw.print(" client="); pw.print(clientHiddenAdj);
+ pw.print(" cached="); pw.print(cachedAdj);
+ pw.print(" client="); pw.print(clientCachedAdj);
pw.print(" empty="); pw.print(emptyAdj);
pw.print(" curRaw="); pw.print(curRawAdj);
pw.print(" setRaw="); pw.print(setRawAdj);
@@ -217,7 +226,8 @@
pw.print(" systemNoUi="); pw.print(systemNoUi);
pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
- pw.print(" lruSeq="); pw.println(lruSeq);
+ pw.print(" lruSeq="); pw.print(lruSeq);
+ pw.print(" lastPssTime="); pw.println(lastPssTime);
if (hasShownUi || pendingUiClean || hasAboveClient) {
pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
pw.print(" pendingUiClean="); pw.print(pendingUiClean);
@@ -237,6 +247,9 @@
pw.print(" hasClientActivities="); pw.print(hasClientActivities);
pw.print(" foregroundActivities="); pw.println(foregroundActivities);
}
+ if (hasStartedServices) {
+ pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
+ }
if (!keeping) {
long wtime;
synchronized (batteryStats.getBatteryStats()) {
@@ -302,9 +315,9 @@
}
if (pubProviders.size() > 0) {
pw.print(prefix); pw.println("Published Providers:");
- for (HashMap.Entry<String, ContentProviderRecord> ent : pubProviders.entrySet()) {
- pw.print(prefix); pw.print(" - "); pw.println(ent.getKey());
- pw.print(prefix); pw.print(" -> "); pw.println(ent.getValue());
+ for (int i=0; i<pubProviders.size(); i++) {
+ pw.print(prefix); pw.print(" - "); pw.println(pubProviders.keyAt(i));
+ pw.print(prefix); pw.print(" -> "); pw.println(pubProviders.valueAt(i));
}
}
if (conProviders.size() > 0) {
@@ -325,21 +338,24 @@
}
ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread,
- ApplicationInfo _info, String _processName, int _uid) {
+ ApplicationInfo _info, String _processName, int _uid,
+ ProcessTracker.ProcessState tracker) {
batteryStats = _batteryStats;
info = _info;
isolated = _info.uid != _uid;
uid = _uid;
userId = UserHandle.getUserId(_uid);
processName = _processName;
- pkgList.add(_info.packageName);
+ baseProcessTracker = tracker;
+ pkgList.put(_info.packageName, tracker);
thread = _thread;
- maxAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
- hiddenAdj = clientHiddenAdj = emptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+ maxAdj = ProcessList.CACHED_APP_MAX_ADJ;
+ cachedAdj = clientCachedAdj = emptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
curRawAdj = setRawAdj = -100;
curAdj = setAdj = -100;
persistent = false;
removed = false;
+ lastPssTime = SystemClock.uptimeMillis();
}
public void setPid(int _pid) {
@@ -409,8 +425,14 @@
} else {
sb.append('u');
sb.append(userId);
- sb.append('a');
- sb.append(UserHandle.getAppId(info.uid));
+ int appId = UserHandle.getAppId(info.uid);
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ sb.append('a');
+ sb.append(appId - Process.FIRST_APPLICATION_UID);
+ } else {
+ sb.append('s');
+ sb.append(appId);
+ }
if (uid != info.uid) {
sb.append('i');
sb.append(UserHandle.getAppId(uid) - Process.FIRST_ISOLATED_UID);
@@ -434,20 +456,41 @@
/*
* Return true if package has been added false if not
*/
- public boolean addPackage(String pkg) {
- if (!pkgList.contains(pkg)) {
- pkgList.add(pkg);
+ public boolean addPackage(String pkg, ProcessTracker tracker) {
+ if (!pkgList.containsKey(pkg)) {
+ pkgList.put(pkg, tracker.getProcessStateLocked(pkg, info.uid, processName));
return true;
}
return false;
}
-
+
+ public int getSetAdjWithServices() {
+ if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+ if (hasStartedServices) {
+ return ProcessList.SERVICE_B_ADJ;
+ }
+ }
+ return setAdj;
+ }
+
+ public void setProcessTrackerState(ProcessRecord TOP_APP, int memFactor, long now,
+ ProcessList plist) {
+ int state = this == TOP_APP ? ProcessTracker.STATE_TOP
+ : plist.adjToTrackedState(getSetAdjWithServices());
+ baseProcessTracker.setState(state, memFactor, now, pkgList);
+ }
+
/*
* Delete all packages from list except the package indicated in info
*/
- public void resetPackageList() {
- pkgList.clear();
- pkgList.add(info.packageName);
+ public void resetPackageList(ProcessTracker tracker) {
+ long now = SystemClock.uptimeMillis();
+ baseProcessTracker.setState(ProcessTracker.STATE_NOTHING, 0, now, pkgList);
+ if (pkgList.size() != 1) {
+ pkgList.clear();
+ pkgList.put(info.packageName, tracker.getProcessStateLocked(
+ info.packageName, info.uid, processName));
+ }
}
public String[] getPackageList() {
@@ -456,7 +499,9 @@
return null;
}
String list[] = new String[size];
- pkgList.toArray(list);
+ for (int i=0; i<pkgList.size(); i++) {
+ list[i] = pkgList.keyAt(i);
+ }
return list;
}
}
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
new file mode 100644
index 0000000..0ea93e8
--- /dev/null
+++ b/services/java/com/android/server/am/ProcessTracker.java
@@ -0,0 +1,2616 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.AppGlobals;
+import android.content.pm.IPackageManager;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.ProcessMap;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.concurrent.locks.ReentrantLock;
+
+public final class ProcessTracker {
+ static final String TAG = "ProcessTracker";
+ static final boolean DEBUG = false;
+
+ public static final int STATE_NOTHING = -1;
+ public static final int STATE_PERSISTENT = 0;
+ public static final int STATE_TOP = 1;
+ public static final int STATE_FOREGROUND = 2;
+ public static final int STATE_VISIBLE = 3;
+ public static final int STATE_PERCEPTIBLE = 4;
+ public static final int STATE_BACKUP = 5;
+ public static final int STATE_SERVICE = 6;
+ public static final int STATE_HOME = 7;
+ public static final int STATE_PREVIOUS = 8;
+ public static final int STATE_CACHED = 9;
+ public static final int STATE_COUNT = STATE_CACHED+1;
+
+ static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, STATE_TOP,
+ STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE, STATE_BACKUP,
+ STATE_SERVICE, STATE_HOME, STATE_PREVIOUS, STATE_CACHED
+ };
+
+ public static final int PSS_SAMPLE_COUNT = 0;
+ public static final int PSS_MINIMUM = 1;
+ public static final int PSS_AVERAGE = 2;
+ public static final int PSS_MAXIMUM = 3;
+ public static final int PSS_COUNT = PSS_MAXIMUM+1;
+
+ public static final int ADJ_NOTHING = -1;
+ public static final int ADJ_MEM_FACTOR_NORMAL = 0;
+ public static final int ADJ_MEM_FACTOR_MODERATE = 1;
+ public static final int ADJ_MEM_FACTOR_LOW = 2;
+ public static final int ADJ_MEM_FACTOR_CRITICAL = 3;
+ public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1;
+ public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT;
+ public static final int ADJ_SCREEN_OFF = 0;
+ public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD;
+ public static final int ADJ_COUNT = ADJ_SCREEN_ON*2;
+
+ static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
+ static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE,
+ ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
+
+ // Most data is kept in a sparse data structure: an integer array which integer
+ // holds the type of the entry, and the identifier for a long array that data
+ // exists in and the offset into the array to find it. The constants below
+ // define the encoding of that data in an integer.
+
+ // Where the "type"/"state" part of the data appears in an offset integer.
+ static int OFFSET_TYPE_SHIFT = 0;
+ static int OFFSET_TYPE_MASK = 0xff;
+
+ // Where the "which array" part of the data appears in an offset integer.
+ static int OFFSET_ARRAY_SHIFT = 8;
+ static int OFFSET_ARRAY_MASK = 0xff;
+
+ // Where the "index into array" part of the data appears in an offset integer.
+ static int OFFSET_INDEX_SHIFT = 16;
+ static int OFFSET_INDEX_MASK = 0xffff;
+
+ static final String[] STATE_NAMES = new String[] {
+ "Persistent ", "Top ", "Foreground ", "Visible ", "Perceptible",
+ "Backup ", "Service ", "Home ", "Previous ", "Cached "
+ };
+
+ static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
+ "off", "on"
+ };
+
+ static final String[] ADJ_MEM_NAMES_CSV = new String[] {
+ "norm", "mod", "low", "crit"
+ };
+
+ static final String[] STATE_NAMES_CSV = new String[] {
+ "pers", "top", "fore", "vis", "percept",
+ "backup", "service", "home", "prev", "cached"
+ };
+
+ static final String[] ADJ_SCREEN_TAGS = new String[] {
+ "0", "1"
+ };
+
+ static final String[] ADJ_MEM_TAGS = new String[] {
+ "n", "m", "l", "c"
+ };
+
+ static final String[] STATE_TAGS = new String[] {
+ "y", "t", "f", "v", "r",
+ "b", "s", "h", "p", "c"
+ };
+
+ static final String CSV_SEP = "\t";
+
+ static final int MAX_HISTORIC_STATES = 4; // Maximum number of historic states we will keep.
+ static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames.
+ static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames.
+ static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in.
+ static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so.
+ static long COMMIT_PERIOD = 24*60*60*1000; // Commit current stats every day.
+
+ final Object mLock;
+ final File mBaseDir;
+ State mState;
+ boolean mCommitPending;
+ boolean mShuttingDown;
+
+ final ReentrantLock mWriteLock = new ReentrantLock();
+
+ public static final class ProcessState {
+ static final int[] BAD_TABLE = new int[0];
+
+ final State mState;
+ final ProcessState mCommonProcess;
+ final String mPackage;
+ final int mUid;
+ final String mName;
+
+ int[] mDurationsTable;
+ int mDurationsTableSize;
+
+ //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT];
+ int mCurState = STATE_NOTHING;
+ long mStartTime;
+
+ int mLastPssState = STATE_NOTHING;
+ long mLastPssTime;
+ int[] mPssTable;
+ int mPssTableSize;
+
+ int mNumExcessiveWake;
+ int mNumExcessiveCpu;
+
+ boolean mMultiPackage;
+
+ long mTmpTotalTime;
+
+ /**
+ * Create a new top-level process state, for the initial case where there is only
+ * a single package running in a process. The initial state is not running.
+ */
+ public ProcessState(State state, String pkg, int uid, String name) {
+ mState = state;
+ mCommonProcess = this;
+ mPackage = pkg;
+ mUid = uid;
+ mName = name;
+ }
+
+ /**
+ * Create a new per-package process state for an existing top-level process
+ * state. The current running state of the top-level process is also copied,
+ * marked as started running at 'now'.
+ */
+ public ProcessState(ProcessState commonProcess, String pkg, int uid, String name,
+ long now) {
+ mState = commonProcess.mState;
+ mCommonProcess = commonProcess;
+ mPackage = pkg;
+ mUid = uid;
+ mName = name;
+ mCurState = commonProcess.mCurState;
+ mStartTime = now;
+ }
+
+ ProcessState clone(String pkg, long now) {
+ ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now);
+ if (mDurationsTable != null) {
+ mState.mAddLongTable = new int[mDurationsTable.length];
+ mState.mAddLongTableSize = 0;
+ for (int i=0; i<mDurationsTableSize; i++) {
+ int origEnt = mDurationsTable[i];
+ int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ int newOff = mState.addLongData(i, type, 1);
+ mState.mAddLongTable[i] = newOff | type;
+ mState.setLong(newOff, 0, mState.getLong(origEnt, 0));
+ }
+ pnew.mDurationsTable = mState.mAddLongTable;
+ pnew.mDurationsTableSize = mState.mAddLongTableSize;
+ }
+ /*
+ if (mPssTable != null) {
+ mState.mAddLongTable = new int[mPssTable.length];
+ mState.mAddLongTableSize = 0;
+ for (int i=0; i<mPssTableSize; i++) {
+ int origEnt = mPssTable[i];
+ int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ int newOff = mState.addLongData(i, type, PSS_COUNT);
+ mState.mAddLongTable[i] = newOff | type;
+ for (int j=0; j<PSS_COUNT; j++) {
+ mState.setLong(newOff, j, mState.getLong(origEnt, j));
+ }
+ }
+ pnew.mPssTable = mState.mAddLongTable;
+ pnew.mPssTableSize = mState.mAddLongTableSize;
+ }
+ */
+ pnew.mNumExcessiveWake = mNumExcessiveWake;
+ pnew.mNumExcessiveCpu = mNumExcessiveCpu;
+ return pnew;
+ }
+
+ void resetSafely(long now) {
+ mDurationsTable = null;
+ mDurationsTableSize = 0;
+ mStartTime = now;
+ mLastPssState = STATE_NOTHING;
+ mLastPssTime = 0;
+ mPssTable = null;
+ mPssTableSize = 0;
+ mNumExcessiveWake = 0;
+ mNumExcessiveCpu = 0;
+ }
+
+ void writeToParcel(Parcel out, long now) {
+ commitStateTime(now);
+ out.writeInt(mMultiPackage ? 1 : 0);
+ out.writeInt(mDurationsTableSize);
+ for (int i=0; i<mDurationsTableSize; i++) {
+ if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": "
+ + State.printLongOffset(mDurationsTable[i]));
+ out.writeInt(mDurationsTable[i]);
+ }
+ out.writeInt(mPssTableSize);
+ for (int i=0; i<mPssTableSize; i++) {
+ if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": "
+ + State.printLongOffset(mPssTable[i]));
+ out.writeInt(mPssTable[i]);
+ }
+ out.writeInt(mNumExcessiveWake);
+ out.writeInt(mNumExcessiveCpu);
+ }
+
+ private int[] readTable(Parcel in, String what) {
+ final int size = in.readInt();
+ if (size < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size);
+ return BAD_TABLE;
+ }
+ if (size == 0) {
+ return null;
+ }
+ final int[] table = new int[size];
+ for (int i=0; i<size; i++) {
+ table[i] = in.readInt();
+ if (DEBUG) Slog.i(TAG, "Reading in " + mName + " table #" + i + ": "
+ + State.printLongOffset(table[i]));
+ if (!mState.validateLongOffset(table[i])) {
+ Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: "
+ + State.printLongOffset(table[i]));
+ return null;
+ }
+ }
+ return table;
+ }
+
+ boolean readFromParcel(Parcel in, boolean fully) {
+ boolean multiPackage = in.readInt() != 0;
+ if (fully) {
+ mMultiPackage = multiPackage;
+ }
+ if (DEBUG) Slog.d(TAG, "Reading durations table...");
+ mDurationsTable = readTable(in, "durations");
+ if (mDurationsTable == BAD_TABLE) {
+ return false;
+ }
+ mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
+ if (DEBUG) Slog.d(TAG, "Reading pss table...");
+ mPssTable = readTable(in, "pss");
+ if (mPssTable == BAD_TABLE) {
+ return false;
+ }
+ mPssTableSize = mPssTable != null ? mPssTable.length : 0;
+ mNumExcessiveWake = in.readInt();
+ mNumExcessiveCpu = in.readInt();
+ return true;
+ }
+
+ public void setState(int state, int memFactor, long now,
+ ArrayMap<String, ProcessTracker.ProcessState> pkgList) {
+ if (state != STATE_NOTHING) {
+ state += memFactor*STATE_COUNT;
+ }
+
+ // First update the common process.
+ mCommonProcess.setState(state, now);
+
+ // If the common process is not multi-package, there is nothing else to do.
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).setState(state, now);
+ }
+ }
+
+ void setState(int state, long now) {
+ if (mCurState != state) {
+ commitStateTime(now);
+ mCurState = state;
+ }
+ }
+
+ void commitStateTime(long now) {
+ if (mCurState != STATE_NOTHING) {
+ long dur = now - mStartTime;
+ if (dur > 0) {
+ int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, mCurState);
+ int off;
+ if (idx >= 0) {
+ off = mDurationsTable[idx];
+ } else {
+ mState.mAddLongTable = mDurationsTable;
+ mState.mAddLongTableSize = mDurationsTableSize;
+ off = mState.addLongData(~idx, mCurState, 1);
+ mDurationsTable = mState.mAddLongTable;
+ mDurationsTableSize = mState.mAddLongTableSize;
+ }
+ long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
+ }
+ }
+ mStartTime = now;
+ }
+
+ public void addPss(long pss, boolean always) {
+ if (!always) {
+ if (mLastPssState == mCurState && SystemClock.uptimeMillis()
+ < (mLastPssTime+(30*1000))) {
+ return;
+ }
+ }
+ mLastPssState = mCurState;
+ mLastPssTime = SystemClock.uptimeMillis();
+ if (mCurState != STATE_NOTHING) {
+ int idx = State.binarySearch(mPssTable, mPssTableSize, mCurState);
+ int off;
+ if (idx >= 0) {
+ off = mPssTable[idx];
+ } else {
+ mState.mAddLongTable = mPssTable;
+ mState.mAddLongTableSize = mPssTableSize;
+ off = mState.addLongData(~idx, mCurState, PSS_COUNT);
+ mPssTable = mState.mAddLongTable;
+ mPssTableSize = mState.mAddLongTableSize;
+ }
+ long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
+ long count = longs[idx+PSS_SAMPLE_COUNT];
+ if (count == 0) {
+ longs[idx+PSS_SAMPLE_COUNT] = 1;
+ longs[idx+PSS_MINIMUM] = pss;
+ longs[idx+PSS_AVERAGE] = pss;
+ longs[idx+PSS_MAXIMUM] = pss;
+ } else {
+ longs[idx+PSS_SAMPLE_COUNT] = count+1;
+ if (longs[idx+PSS_MINIMUM] > pss) {
+ longs[idx+PSS_MINIMUM] = pss;
+ }
+ longs[idx+PSS_AVERAGE] = (long)( ((longs[idx+PSS_AVERAGE]*(double)count)+pss)
+ / (count+1) );
+ if (longs[idx+PSS_MAXIMUM] < pss) {
+ longs[idx+PSS_MAXIMUM] = pss;
+ }
+ }
+ }
+ }
+
+ public void reportExcessiveWake(ArrayMap<String, ProcessTracker.ProcessState> pkgList) {
+ mCommonProcess.mNumExcessiveWake++;
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).mNumExcessiveWake++;
+ }
+ }
+
+ public void reportExcessiveCpu(ArrayMap<String, ProcessTracker.ProcessState> pkgList) {
+ mCommonProcess.mNumExcessiveCpu++;
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).mNumExcessiveCpu++;
+ }
+ }
+
+ private ProcessState pullFixedProc(ArrayMap<String, ProcessTracker.ProcessState> pkgList,
+ int index) {
+ ProcessState proc = pkgList.valueAt(index);
+ if (proc.mMultiPackage) {
+ // The array map is still pointing to a common process state
+ // that is now shared across packages. Update it to point to
+ // the new per-package state.
+ proc = mState.mPackages.get(pkgList.keyAt(index),
+ proc.mUid).mProcesses.get(proc.mName);
+ if (proc == null) {
+ throw new IllegalStateException("Didn't create per-package process");
+ }
+ pkgList.setValueAt(index, proc);
+ }
+ return proc;
+ }
+
+ long getDuration(int state, long now) {
+ int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, state);
+ long time = idx >= 0 ? mState.getLong(mDurationsTable[idx], 0) : 0;
+ if (mCurState == state) {
+ time += now - mStartTime;
+ }
+ return time;
+ }
+
+ long getPssSampleCount(int state) {
+ int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+ return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0;
+ }
+
+ long getPssMinimum(int state) {
+ int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+ return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_MINIMUM) : 0;
+ }
+
+ long getPssAverage(int state) {
+ int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+ return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_AVERAGE) : 0;
+ }
+
+ long getPssMaximum(int state) {
+ int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+ return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
+ }
+ }
+
+ public static final class ServiceState {
+ final long[] mStartedDurations = new long[ADJ_COUNT];
+ int mStartedCount;
+ int mStartedState = STATE_NOTHING;
+ long mStartedStartTime;
+
+ final long[] mBoundDurations = new long[ADJ_COUNT];
+ int mBoundCount;
+ int mBoundState = STATE_NOTHING;
+ long mBoundStartTime;
+
+ final long[] mExecDurations = new long[ADJ_COUNT];
+ int mExecCount;
+ int mExecState = STATE_NOTHING;
+ long mExecStartTime;
+
+ void resetSafely(long now) {
+ for (int i=0; i<ADJ_COUNT; i++) {
+ mStartedDurations[i] = mBoundDurations[i] = mExecDurations[i] = 0;
+ }
+ mStartedCount = mBoundCount = mExecCount = 0;
+ mStartedStartTime = mBoundStartTime = mExecStartTime = now;
+ }
+
+ void writeToParcel(Parcel out, long now) {
+ if (mStartedState != STATE_NOTHING) {
+ mStartedDurations[mStartedState] += now - mStartedStartTime;
+ mStartedStartTime = now;
+ }
+ if (mBoundState != STATE_NOTHING) {
+ mBoundDurations[mBoundState] += now - mBoundStartTime;
+ mBoundStartTime = now;
+ }
+ if (mExecState != STATE_NOTHING) {
+ mExecDurations[mExecState] += now - mExecStartTime;
+ mExecStartTime = now;
+ }
+ out.writeLongArray(mStartedDurations);
+ out.writeInt(mStartedCount);
+ out.writeLongArray(mBoundDurations);
+ out.writeInt(mBoundCount);
+ out.writeLongArray(mExecDurations);
+ out.writeInt(mExecCount);
+ }
+
+ boolean readFromParcel(Parcel in) {
+ in.readLongArray(mStartedDurations);
+ mStartedCount = in.readInt();
+ in.readLongArray(mBoundDurations);
+ mBoundCount = in.readInt();
+ in.readLongArray(mExecDurations);
+ mExecCount = in.readInt();
+ return true;
+ }
+
+ public void setStarted(boolean started, int memFactor, long now) {
+ int state = started ? memFactor : STATE_NOTHING;
+ if (mStartedState != state) {
+ if (mStartedState != STATE_NOTHING) {
+ mStartedDurations[mStartedState] += now - mStartedStartTime;
+ } else if (started) {
+ mStartedCount++;
+ }
+ mStartedState = state;
+ mStartedStartTime = now;
+ }
+ }
+
+ public void setBound(boolean bound, int memFactor, long now) {
+ int state = bound ? memFactor : STATE_NOTHING;
+ if (mBoundState != state) {
+ if (mBoundState != STATE_NOTHING) {
+ mBoundDurations[mBoundState] += now - mBoundStartTime;
+ } else if (bound) {
+ mBoundCount++;
+ }
+ mBoundState = state;
+ mBoundStartTime = now;
+ }
+ }
+
+ public void setExecuting(boolean executing, int memFactor, long now) {
+ int state = executing ? memFactor : STATE_NOTHING;
+ if (mExecState != state) {
+ if (mExecState != STATE_NOTHING) {
+ mExecDurations[mExecState] += now - mExecStartTime;
+ } else if (executing) {
+ mExecCount++;
+ }
+ mExecState = state;
+ mExecStartTime = now;
+ }
+ }
+ }
+
+ public static final class PackageState {
+ final ArrayMap<String, ProcessState> mProcesses = new ArrayMap<String, ProcessState>();
+ final ArrayMap<String, ServiceState> mServices = new ArrayMap<String, ServiceState>();
+ final int mUid;
+
+ public PackageState(int uid) {
+ mUid = uid;
+ }
+ }
+
+ static final class State {
+ // Current version of the parcel format.
+ private static final int PARCEL_VERSION = 3;
+ // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
+ private static final int MAGIC = 0x50535453;
+
+ final File mBaseDir;
+ final ProcessTracker mProcessTracker;
+ AtomicFile mFile;
+ String mReadError;
+
+ long mTimePeriodStartClock;
+ String mTimePeriodStartClockStr;
+ long mTimePeriodStartRealtime;
+ long mTimePeriodEndRealtime;
+ boolean mRunning;
+
+ final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
+ final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
+ final long[] mMemFactorDurations = new long[ADJ_COUNT];
+ int mMemFactor = STATE_NOTHING;
+ long mStartTime;
+
+ static final int LONGS_SIZE = 4096;
+ final ArrayList<long[]> mLongs = new ArrayList<long[]>();
+ int mNextLong;
+
+ int[] mAddLongTable;
+ int mAddLongTableSize;
+
+ final Object mPendingWriteLock = new Object();
+ Parcel mPendingWrite;
+ long mLastWriteTime;
+
+ State(File baseDir, ProcessTracker tracker) {
+ mBaseDir = baseDir;
+ reset();
+ mProcessTracker = tracker;
+ }
+
+ State(String file) {
+ mBaseDir = null;
+ reset();
+ mFile = new AtomicFile(new File(file));
+ mProcessTracker = null;
+ readLocked();
+ }
+
+ void reset() {
+ if (DEBUG && mFile != null) Slog.d(TAG, "Resetting state of " + mFile.getBaseFile());
+ resetCommon();
+ mPackages.getMap().clear();
+ mProcesses.getMap().clear();
+ mMemFactor = STATE_NOTHING;
+ mStartTime = 0;
+ if (DEBUG && mFile != null) Slog.d(TAG, "State reset; now " + mFile.getBaseFile());
+ }
+
+ void resetSafely() {
+ if (DEBUG && mFile != null) Slog.d(TAG, "Safely resetting state of " + mFile.getBaseFile());
+ resetCommon();
+ long now = SystemClock.uptimeMillis();
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ final int NPROC = procMap.size();
+ for (int ip=0; ip<NPROC; ip++) {
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ final int NUID = uids.size();
+ for (int iu=0; iu<NUID; iu++) {
+ uids.valueAt(iu).resetSafely(now);
+ }
+ }
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ final int NPKG = pkgMap.size();
+ for (int ip=0; ip<NPKG; ip++) {
+ SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final int NUID = uids.size();
+ for (int iu=0; iu<NUID; iu++) {
+ PackageState pkgState = uids.valueAt(iu);
+ final int NPROCS = pkgState.mProcesses.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ pkgState.mProcesses.valueAt(iproc).resetSafely(now);
+ }
+ final int NSRVS = pkgState.mServices.size();
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ pkgState.mServices.valueAt(isvc).resetSafely(now);
+ }
+ }
+ }
+ mStartTime = SystemClock.uptimeMillis();
+ if (DEBUG && mFile != null) Slog.d(TAG, "State reset; now " + mFile.getBaseFile());
+ }
+
+ private void resetCommon() {
+ mLastWriteTime = SystemClock.uptimeMillis();
+ mTimePeriodStartClock = System.currentTimeMillis();
+ buildTimePeriodStartClockStr();
+ mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+ mLongs.clear();
+ mLongs.add(new long[LONGS_SIZE]);
+ mNextLong = 0;
+ Arrays.fill(mMemFactorDurations, 0);
+ mMemFactor = STATE_NOTHING;
+ mStartTime = 0;
+ }
+
+ private void buildTimePeriodStartClockStr() {
+ mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+ mTimePeriodStartClock).toString();
+ if (mBaseDir != null) {
+ mFile = new AtomicFile(new File(mBaseDir,
+ STATE_FILE_PREFIX + mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
+ }
+ }
+
+ static byte[] readFully(FileInputStream stream) throws java.io.IOException {
+ int pos = 0;
+ int avail = stream.available();
+ byte[] data = new byte[avail];
+ while (true) {
+ int amt = stream.read(data, pos, data.length-pos);
+ //Log.i("foo", "Read " + amt + " bytes at " + pos
+ // + " of avail " + data.length);
+ if (amt <= 0) {
+ //Log.i("foo", "**** FINISHED READING: pos=" + pos
+ // + " len=" + data.length);
+ return data;
+ }
+ pos += amt;
+ avail = stream.available();
+ if (avail > data.length-pos) {
+ byte[] newData = new byte[pos+avail];
+ System.arraycopy(data, 0, newData, 0, pos);
+ data = newData;
+ }
+ }
+ }
+
+ void readLocked() {
+ try {
+ FileInputStream stream = mFile.openRead();
+
+ byte[] raw = readFully(stream);
+ Parcel in = Parcel.obtain();
+ in.unmarshall(raw, 0, raw.length);
+ in.setDataPosition(0);
+ stream.close();
+
+ readFromParcel(in);
+ if (mReadError != null) {
+ Slog.w(TAG, "Ignoring existing stats; " + mReadError);
+ if (DEBUG) {
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ final int NPROC = procMap.size();
+ for (int ip=0; ip<NPROC; ip++) {
+ Slog.w(TAG, "Process: " + procMap.keyAt(ip));
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ final int NUID = uids.size();
+ for (int iu=0; iu<NUID; iu++) {
+ Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
+ }
+ }
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ final int NPKG = pkgMap.size();
+ for (int ip=0; ip<NPKG; ip++) {
+ Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
+ SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final int NUID = uids.size();
+ for (int iu=0; iu<NUID; iu++) {
+ Slog.w(TAG, " Uid: " + uids.keyAt(iu));
+ PackageState pkgState = uids.valueAt(iu);
+ final int NPROCS = pkgState.mProcesses.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ Slog.w(TAG, " Process " + pkgState.mProcesses.keyAt(iproc)
+ + ": " + pkgState.mProcesses.valueAt(iproc));
+ }
+ final int NSRVS = pkgState.mServices.size();
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ Slog.w(TAG, " Service " + pkgState.mServices.keyAt(isvc)
+ + ": " + pkgState.mServices.valueAt(isvc));
+ }
+ }
+ }
+ }
+ }
+ } catch (Throwable e) {
+ mReadError = "error reading: " + e;
+ Slog.e(TAG, "Error reading process statistics", e);
+ }
+ }
+
+ private void writeStateLocked(boolean sync, final boolean commit) {
+ synchronized (mPendingWriteLock) {
+ long now = SystemClock.uptimeMillis();
+ mPendingWrite = Parcel.obtain();
+ mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+ writeToParcel(mPendingWrite);
+ mLastWriteTime = SystemClock.uptimeMillis();
+ Slog.i(TAG, "Prepared write state in " + (SystemClock.uptimeMillis()-now) + "ms");
+ if (!sync) {
+ BackgroundThread.getHandler().post(new Runnable() {
+ @Override public void run() {
+ performWriteState(commit);
+ }
+ });
+ return;
+ }
+ }
+
+ performWriteState(commit);
+ }
+
+ void performWriteState(boolean commit) {
+ if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile()
+ + " commit=" + commit);
+ Parcel data;
+ synchronized (mPendingWriteLock) {
+ data = mPendingWrite;
+ if (data == null) {
+ return;
+ }
+ mPendingWrite = null;
+ if (mProcessTracker != null) {
+ mProcessTracker.mWriteLock.lock();
+ }
+ }
+
+ FileOutputStream stream = null;
+ try {
+ stream = mFile.startWrite();
+ stream.write(data.marshall());
+ stream.flush();
+ mFile.finishWrite(stream);
+ if (DEBUG) Slog.d(TAG, "Write completed successfully!");
+ } catch (IOException e) {
+ Slog.w(TAG, "Error writing process statistics", e);
+ mFile.failWrite(stream);
+ } finally {
+ data.recycle();
+ if (mProcessTracker != null) {
+ mProcessTracker.trimHistoricStatesWriteLocked();
+ mProcessTracker.mWriteLock.unlock();
+ }
+ }
+
+ if (commit) {
+ resetSafely();
+ }
+ }
+
+ void writeToParcel(Parcel out) {
+ long now = SystemClock.uptimeMillis();
+ out.writeInt(MAGIC);
+ out.writeInt(PARCEL_VERSION);
+ out.writeInt(STATE_COUNT);
+ out.writeInt(ADJ_COUNT);
+ out.writeInt(PSS_COUNT);
+ out.writeInt(LONGS_SIZE);
+
+ out.writeLong(mTimePeriodStartClock);
+ out.writeLong(mTimePeriodStartRealtime);
+ out.writeLong(mTimePeriodEndRealtime);
+
+ out.writeInt(mLongs.size());
+ out.writeInt(mNextLong);
+ for (int i=0; i<(mLongs.size()-1); i++) {
+ out.writeLongArray(mLongs.get(i));
+ }
+ long[] lastLongs = mLongs.get(mLongs.size()-1);
+ for (int i=0; i<mNextLong; i++) {
+ out.writeLong(lastLongs[i]);
+ if (DEBUG) Slog.d(TAG, "Writing last long #" + i + ": " + lastLongs[i]);
+ }
+
+ if (mMemFactor != STATE_NOTHING) {
+ mMemFactorDurations[mMemFactor] += now - mStartTime;
+ mStartTime = now;
+ }
+ out.writeLongArray(mMemFactorDurations);
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ final int NPROC = procMap.size();
+ out.writeInt(NPROC);
+ for (int ip=0; ip<NPROC; ip++) {
+ out.writeString(procMap.keyAt(ip));
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ final int NUID = uids.size();
+ out.writeInt(NUID);
+ for (int iu=0; iu<NUID; iu++) {
+ out.writeInt(uids.keyAt(iu));
+ ProcessState proc = uids.valueAt(iu);
+ out.writeString(proc.mPackage);
+ proc.writeToParcel(out, now);
+ }
+ }
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ final int NPKG = pkgMap.size();
+ out.writeInt(NPKG);
+ for (int ip=0; ip<NPKG; ip++) {
+ out.writeString(pkgMap.keyAt(ip));
+ SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final int NUID = uids.size();
+ out.writeInt(NUID);
+ for (int iu=0; iu<NUID; iu++) {
+ out.writeInt(uids.keyAt(iu));
+ PackageState pkgState = uids.valueAt(iu);
+ final int NPROCS = pkgState.mProcesses.size();
+ out.writeInt(NPROCS);
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ out.writeString(pkgState.mProcesses.keyAt(iproc));
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (proc.mCommonProcess == proc) {
+ // This is the same as the common process we wrote above.
+ out.writeInt(0);
+ } else {
+ // There is separate data for this package's process.
+ out.writeInt(1);
+ proc.writeToParcel(out, now);
+ }
+ }
+ final int NSRVS = pkgState.mServices.size();
+ out.writeInt(NSRVS);
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ out.writeString(pkgState.mServices.keyAt(isvc));
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ svc.writeToParcel(out, now);
+ }
+ }
+ }
+ }
+
+ private boolean readCheckedInt(Parcel in, int val, String what) {
+ int got;
+ if ((got=in.readInt()) != val) {
+ mReadError = "bad " + ": " + got;
+ return false;
+ }
+ return true;
+ }
+
+ private void readFromParcel(Parcel in) {
+ final boolean hadData = mPackages.getMap().size() > 0
+ || mProcesses.getMap().size() > 0;
+ if (hadData) {
+ resetSafely();
+ }
+
+ if (!readCheckedInt(in, MAGIC, "magic number")) {
+ return;
+ }
+ if (!readCheckedInt(in, PARCEL_VERSION, "version")) {
+ return;
+ }
+ if (!readCheckedInt(in, STATE_COUNT, "state count")) {
+ return;
+ }
+ if (!readCheckedInt(in, ADJ_COUNT, "adj count")) {
+ return;
+ }
+ if (!readCheckedInt(in, PSS_COUNT, "pss count")) {
+ return;
+ }
+ if (!readCheckedInt(in, LONGS_SIZE, "longs size")) {
+ return;
+ }
+
+ mTimePeriodStartClock = in.readLong();
+ buildTimePeriodStartClockStr();
+ mTimePeriodStartRealtime = in.readLong();
+ mTimePeriodEndRealtime = in.readLong();
+
+ final int NLONGS = in.readInt();
+ final int NEXTLONG = in.readInt();
+ mLongs.clear();
+ for (int i=0; i<(NLONGS-1); i++) {
+ while (i >= mLongs.size()) {
+ mLongs.add(new long[LONGS_SIZE]);
+ }
+ in.readLongArray(mLongs.get(i));
+ }
+ long[] longs = new long[LONGS_SIZE];
+ mNextLong = NEXTLONG;
+ for (int i=0; i<NEXTLONG; i++) {
+ longs[i] = in.readLong();
+ if (DEBUG) Slog.d(TAG, "Reading last long #" + i + ": " + longs[i]);
+ }
+ mLongs.add(longs);
+
+ in.readLongArray(mMemFactorDurations);
+
+ int NPROC = in.readInt();
+ if (NPROC < 0) {
+ mReadError = "bad process count: " + NPROC;
+ return;
+ }
+ while (NPROC > 0) {
+ NPROC--;
+ String procName = in.readString();
+ if (procName == null) {
+ mReadError = "bad process name";
+ return;
+ }
+ int NUID = in.readInt();
+ if (NUID < 0) {
+ mReadError = "bad uid count: " + NUID;
+ return;
+ }
+ while (NUID > 0) {
+ NUID--;
+ int uid = in.readInt();
+ if (uid < 0) {
+ mReadError = "bad uid: " + uid;
+ return;
+ }
+ String pkgName = in.readString();
+ if (pkgName == null) {
+ mReadError = "bad process package name";
+ return;
+ }
+ ProcessState proc = hadData ? mProcesses.get(procName, uid) : null;
+ if (proc != null) {
+ if (!proc.readFromParcel(in, false)) {
+ return;
+ }
+ } else {
+ proc = new ProcessState(this, pkgName, uid, procName);
+ if (!proc.readFromParcel(in, true)) {
+ return;
+ }
+ }
+ if (DEBUG) Slog.d(TAG, "Adding process: " + procName + " " + uid + " " + proc);
+ mProcesses.put(procName, uid, proc);
+ }
+ }
+
+ if (DEBUG) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes");
+
+ int NPKG = in.readInt();
+ if (NPKG < 0) {
+ mReadError = "bad package count: " + NPKG;
+ return;
+ }
+ while (NPKG > 0) {
+ NPKG--;
+ String pkgName = in.readString();
+ if (pkgName == null) {
+ mReadError = "bad package name";
+ return;
+ }
+ int NUID = in.readInt();
+ if (NUID < 0) {
+ mReadError = "bad uid count: " + NUID;
+ return;
+ }
+ while (NUID > 0) {
+ NUID--;
+ int uid = in.readInt();
+ if (uid < 0) {
+ mReadError = "bad uid: " + uid;
+ return;
+ }
+ PackageState pkgState = new PackageState(uid);
+ mPackages.put(pkgName, uid, pkgState);
+ int NPROCS = in.readInt();
+ if (NPROCS < 0) {
+ mReadError = "bad package process count: " + NPROCS;
+ return;
+ }
+ while (NPROCS > 0) {
+ NPROCS--;
+ String procName = in.readString();
+ if (procName == null) {
+ mReadError = "bad package process name";
+ return;
+ }
+ int hasProc = in.readInt();
+ if (DEBUG) Slog.d(TAG, "Reading package " + pkgName + " " + uid
+ + " process " + procName + " hasProc=" + hasProc);
+ ProcessState commonProc = mProcesses.get(procName, uid);
+ if (DEBUG) Slog.d(TAG, "Got common proc " + procName + " " + uid
+ + ": " + commonProc);
+ if (commonProc == null) {
+ mReadError = "no common proc: " + procName;
+ return;
+ }
+ if (hasProc != 0) {
+ // The process for this package is unique to the package; we
+ // need to load it. We don't need to do anything about it if
+ // it is not unique because if someone later looks for it
+ // they will find and use it from the global procs.
+ ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null;
+ if (proc != null) {
+ if (!proc.readFromParcel(in, false)) {
+ return;
+ }
+ } else {
+ proc = new ProcessState(commonProc, pkgName, uid, procName, 0);
+ if (!proc.readFromParcel(in, true)) {
+ return;
+ }
+ }
+ if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: "
+ + procName + " " + uid + " " + proc);
+ pkgState.mProcesses.put(procName, proc);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: "
+ + procName + " " + uid + " " + commonProc);
+ pkgState.mProcesses.put(procName, commonProc);
+ }
+ }
+ int NSRVS = in.readInt();
+ if (NSRVS < 0) {
+ mReadError = "bad package service count: " + NSRVS;
+ return;
+ }
+ while (NSRVS > 0) {
+ NSRVS--;
+ String serviceName = in.readString();
+ if (serviceName == null) {
+ mReadError = "bad package service name";
+ return;
+ }
+ ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null;
+ if (serv == null) {
+ serv = new ServiceState();
+ }
+ if (!serv.readFromParcel(in)) {
+ return;
+ }
+ if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " service: "
+ + serviceName + " " + uid + " " + serv);
+ pkgState.mServices.put(serviceName, serv);
+ }
+ }
+ }
+
+ if (DEBUG) Slog.d(TAG, "Successfully read procstats!");
+ }
+
+ int addLongData(int index, int type, int num) {
+ int tableLen = mAddLongTable != null ? mAddLongTable.length : 0;
+ if (mAddLongTableSize >= tableLen) {
+ int newSize = ArrayUtils.idealIntArraySize(tableLen + 1);
+ int[] newTable = new int[newSize];
+ if (tableLen > 0) {
+ System.arraycopy(mAddLongTable, 0, newTable, 0, tableLen);
+ }
+ mAddLongTable = newTable;
+ }
+ if (mAddLongTableSize > 0 && mAddLongTableSize - index != 0) {
+ System.arraycopy(mAddLongTable, index, mAddLongTable, index + 1,
+ mAddLongTableSize - index);
+ }
+ int off = allocLongData(num);
+ mAddLongTable[index] = type | off;
+ mAddLongTableSize++;
+ return off;
+ }
+
+ int allocLongData(int num) {
+ int whichLongs = mLongs.size()-1;
+ long[] longs = mLongs.get(whichLongs);
+ if (mNextLong + num > longs.length) {
+ longs = new long[LONGS_SIZE];
+ mLongs.add(longs);
+ whichLongs++;
+ mNextLong = 0;
+ }
+ int off = (whichLongs<<OFFSET_ARRAY_SHIFT) | (mNextLong<<OFFSET_INDEX_SHIFT);
+ mNextLong += num;
+ return off;
+ }
+
+ boolean validateLongOffset(int off) {
+ int arr = (off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK;
+ if (arr >= mLongs.size()) {
+ return false;
+ }
+ int idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
+ if (idx >= LONGS_SIZE) {
+ return false;
+ }
+ if (DEBUG) Slog.d(TAG, "Validated long " + printLongOffset(off)
+ + ": " + getLong(off, 0));
+ return true;
+ }
+
+ static String printLongOffset(int off) {
+ StringBuilder sb = new StringBuilder(16);
+ sb.append("a"); sb.append((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ sb.append("i"); sb.append((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK);
+ sb.append("t"); sb.append((off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK);
+ return sb.toString();
+ }
+
+ void setLong(int off, int index, long value) {
+ long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)] = value;
+ }
+
+ long getLong(int off, int index) {
+ long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ return longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)];
+ }
+
+ static int binarySearch(int[] array, int size, int value) {
+ int lo = 0;
+ int hi = size - 1;
+
+ while (lo <= hi) {
+ int mid = (lo + hi) >>> 1;
+ int midVal = (array[mid] >> OFFSET_TYPE_SHIFT) & OFFSET_TYPE_MASK;
+
+ if (midVal < value) {
+ lo = mid + 1;
+ } else if (midVal > value) {
+ hi = mid - 1;
+ } else {
+ return mid; // value found
+ }
+ }
+ return ~lo; // value not present
+ }
+
+ PackageState getPackageStateLocked(String packageName, int uid) {
+ PackageState as = mPackages.get(packageName, uid);
+ if (as != null) {
+ return as;
+ }
+ as = new PackageState(uid);
+ mPackages.put(packageName, uid, as);
+ return as;
+ }
+
+ ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
+ final PackageState pkgState = getPackageStateLocked(packageName, uid);
+ ProcessState ps = pkgState.mProcesses.get(processName);
+ if (ps != null) {
+ return ps;
+ }
+ ProcessState commonProc = mProcesses.get(processName, uid);
+ if (commonProc == null) {
+ commonProc = new ProcessState(this, packageName, uid, processName);
+ mProcesses.put(processName, uid, commonProc);
+ }
+ if (!commonProc.mMultiPackage) {
+ if (packageName.equals(commonProc.mPackage)) {
+ // This common process is not in use by multiple packages, and
+ // is for the calling package, so we can just use it directly.
+ ps = commonProc;
+ } else {
+ // This common process has not been in use by multiple packages,
+ // but it was created for a different package than the caller.
+ // We need to convert it to a multi-package process.
+ commonProc.mMultiPackage = true;
+ // The original package it was created for now needs to point
+ // to its own copy.
+ long now = SystemClock.uptimeMillis();
+ pkgState.mProcesses.put(commonProc.mName, commonProc.clone(
+ commonProc.mPackage, now));
+ ps = new ProcessState(commonProc, packageName, uid, processName, now);
+ }
+ } else {
+ // The common process is for multiple packages, we need to create a
+ // separate object for the per-package data.
+ ps = new ProcessState(commonProc, packageName, uid, processName,
+ SystemClock.uptimeMillis());
+ }
+ pkgState.mProcesses.put(processName, ps);
+ return ps;
+ }
+
+ void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpAll) {
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ boolean printedHeader = false;
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ String pkgName = pkgMap.keyAt(ip);
+ if (reqPackage != null && !reqPackage.equals(pkgName)) {
+ continue;
+ }
+ SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ PackageState pkgState = uids.valueAt(iu);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ if (NPROCS > 0 || NSRVS > 0) {
+ if (!printedHeader) {
+ pw.println("Per-Package Process Stats:");
+ printedHeader = true;
+ }
+ pw.print(" * "); pw.print(pkgName); pw.print(" / ");
+ UserHandle.formatUid(pw, uid); pw.println(":");
+ }
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ pw.print(" Process ");
+ pw.print(pkgState.mProcesses.keyAt(iproc));
+ pw.print(" (");
+ pw.print(proc.mDurationsTableSize);
+ pw.print(" entries)");
+ pw.println(":");
+ dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES);
+ }
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ pw.print(" Service ");
+ pw.print(pkgState.mServices.keyAt(isvc));
+ pw.println(":");
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ if (svc.mStartedCount != 0) {
+ pw.print(" Started op count "); pw.print(svc.mStartedCount);
+ pw.println(":");
+ dumpSingleTime(pw, " ", svc.mStartedDurations, svc.mStartedState,
+ svc.mStartedStartTime, now);
+ }
+ if (svc.mBoundCount != 0) {
+ pw.print(" Bound op count "); pw.print(svc.mBoundCount);
+ pw.println(":");
+ dumpSingleTime(pw, " ", svc.mBoundDurations, svc.mBoundState,
+ svc.mBoundStartTime, now);
+ }
+ if (svc.mExecCount != 0) {
+ pw.print(" Executing op count "); pw.print(svc.mExecCount);
+ pw.println(":");
+ dumpSingleTime(pw, " ", svc.mExecDurations, svc.mExecState,
+ svc.mExecStartTime, now);
+ }
+ }
+ }
+ }
+
+ if (reqPackage == null) {
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ printedHeader = false;
+ for (int ip=0; ip<procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ ProcessState proc = uids.valueAt(iu);
+ if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING
+ && proc.mPssTableSize == 0) {
+ continue;
+ }
+ if (!printedHeader) {
+ pw.println("Process Stats:");
+ printedHeader = true;
+ }
+ pw.print(" * "); pw.print(procName); pw.print(" / ");
+ UserHandle.formatUid(pw, uid);
+ pw.print(" ("); pw.print(proc.mDurationsTableSize);
+ pw.print(" entries)"); pw.println(":");
+ dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES);
+ }
+ }
+
+ pw.println();
+ pw.println("Summary:");
+ dumpSummaryLocked(pw, reqPackage, now);
+ }
+
+ if (dumpAll) {
+ pw.println();
+ pw.println("Internal state:");
+ pw.print(" mFile="); pw.println(mFile.getBaseFile());
+ pw.print(" Num long arrays: "); pw.println(mLongs.size());
+ pw.print(" Next long entry: "); pw.println(mNextLong);
+ pw.print(" mRunning="); pw.println(mRunning);
+ }
+ }
+
+ void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) {
+ dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ new int[] { STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+ STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS },
+ now, reqPackage);
+ pw.println();
+ pw.println("Run time Stats:");
+ dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now);
+ pw.println();
+ pw.print(" Start time: ");
+ pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
+ pw.println();
+ pw.print(" Total elapsed time: ");
+ TimeUtils.formatDuration(
+ (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
+ - mTimePeriodStartRealtime, pw);
+ pw.println();
+ }
+
+ void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix,
+ int[] screenStates, int[] memStates, int[] procStates, long now, String reqPackage) {
+ ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
+ procStates, now, reqPackage);
+ if (procs.size() > 0) {
+ if (header != null) {
+ pw.println();
+ pw.println(header);
+ }
+ dumpProcessSummary(pw, prefix, procs, screenStates, memStates, procStates, now);
+ }
+ }
+
+ ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
+ int[] procStates, long now, String reqPackage) {
+ ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) {
+ continue;
+ }
+ SparseArray<PackageState> procs = pkgMap.valueAt(ip);
+ for (int iu=0; iu<procs.size(); iu++) {
+ PackageState state = procs.valueAt(iu);
+ for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
+ ProcessState proc = state.mProcesses.valueAt(iproc);
+ foundProcs.add(proc.mCommonProcess);
+ }
+ }
+ }
+ ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size());
+ for (int i=0; i<foundProcs.size(); i++) {
+ ProcessState proc = foundProcs.valueAt(i);
+ if (computeProcessTimeLocked(proc, screenStates, memStates,
+ procStates, now) > 0) {
+ outProcs.add(proc);
+ }
+ }
+ Collections.sort(outProcs, new Comparator<ProcessState>() {
+ @Override
+ public int compare(ProcessState lhs, ProcessState rhs) {
+ if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
+ return -1;
+ } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
+ return 1;
+ }
+ return 0;
+ }
+ });
+ return outProcs;
+ }
+
+ String collapseString(String pkgName, String itemName) {
+ if (itemName.startsWith(pkgName)) {
+ final int ITEMLEN = itemName.length();
+ final int PKGLEN = pkgName.length();
+ if (ITEMLEN == PKGLEN) {
+ return "";
+ } else if (ITEMLEN >= PKGLEN) {
+ if (itemName.charAt(PKGLEN) == '.') {
+ return itemName.substring(PKGLEN);
+ }
+ }
+ }
+ return itemName;
+ }
+
+ void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
+ final long now = SystemClock.uptimeMillis();
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ pw.println("vers,1");
+ pw.print("period,"); pw.print(mTimePeriodStartClockStr);
+ pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
+ pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+ pw.println();
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ String pkgName = pkgMap.keyAt(ip);
+ if (reqPackage != null && !reqPackage.equals(pkgName)) {
+ continue;
+ }
+ SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ PackageState pkgState = uids.valueAt(iu);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ pw.print("pkgproc,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
+ dumpAllProcessStateCheckin(pw, proc, now);
+ pw.println();
+ if (proc.mPssTableSize > 0) {
+ pw.print("pkgpss,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
+ dumpAllProcessPssCheckin(pw, proc);
+ pw.println();
+ }
+ if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) {
+ pw.print("pkgkills,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
+ pw.print(",");
+ pw.print(proc.mNumExcessiveWake);
+ pw.print(",");
+ pw.print(proc.mNumExcessiveCpu);
+ pw.println();
+ }
+ }
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ String serviceName = collapseString(pkgName,
+ pkgState.mServices.keyAt(isvc));
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName,
+ svc, svc.mStartedCount, svc.mStartedDurations, svc.mStartedState,
+ svc.mStartedStartTime, now);
+ dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, serviceName,
+ svc, svc.mBoundCount, svc.mBoundDurations, svc.mBoundState,
+ svc.mBoundStartTime, now);
+ dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, serviceName,
+ svc, svc.mExecCount, svc.mExecDurations, svc.mExecState,
+ svc.mExecStartTime, now);
+ }
+ }
+ }
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip=0; ip<procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ ProcessState procState = uids.valueAt(iu);
+ if (procState.mDurationsTableSize > 0) {
+ pw.print("proc,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ dumpAllProcessStateCheckin(pw, procState, now);
+ pw.println();
+ }
+ if (procState.mPssTableSize > 0) {
+ pw.print("pss,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ dumpAllProcessPssCheckin(pw, procState);
+ pw.println();
+ }
+ if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0) {
+ pw.print("kills,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(procState.mNumExcessiveWake);
+ pw.print(",");
+ pw.print(procState.mNumExcessiveCpu);
+ pw.println();
+ }
+ }
+ }
+ pw.print("total");
+ dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor,
+ mStartTime, now);
+ pw.println();
+ }
+ }
+
+ public ProcessTracker(Object lock, File file) {
+ mLock = lock;
+ mBaseDir = file;
+ mBaseDir.mkdirs();
+ mState = new State(mBaseDir, this);
+ mState.mRunning = true;
+ }
+
+ public ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
+ return mState.getProcessStateLocked(packageName, uid, processName);
+ }
+
+ public ServiceState getServiceStateLocked(String packageName, int uid, String className) {
+ final PackageState as = mState.getPackageStateLocked(packageName, uid);
+ ServiceState ss = as.mServices.get(className);
+ if (ss != null) {
+ return ss;
+ }
+ ss = new ServiceState();
+ as.mServices.put(className, ss);
+ return ss;
+ }
+
+ public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
+ if (screenOn) {
+ memFactor += ADJ_SCREEN_ON;
+ }
+ if (memFactor != mState.mMemFactor) {
+ if (mState.mMemFactor != STATE_NOTHING) {
+ mState.mMemFactorDurations[mState.mMemFactor] += now - mState.mStartTime;
+ }
+ mState.mMemFactor = memFactor;
+ mState.mStartTime = now;
+ ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap();
+ for (int i=0; i<pmap.size(); i++) {
+ SparseArray<PackageState> uids = pmap.valueAt(i);
+ for (int j=0; j<uids.size(); j++) {
+ PackageState pkg = uids.valueAt(j);
+ ArrayMap<String, ServiceState> services = pkg.mServices;
+ for (int k=0; k<services.size(); k++) {
+ ServiceState service = services.valueAt(k);
+ if (service.mStartedState != STATE_NOTHING) {
+ service.setStarted(true, memFactor, now);
+ }
+ if (service.mBoundState != STATE_NOTHING) {
+ service.setBound(true, memFactor, now);
+ }
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public int getMemFactorLocked() {
+ return mState.mMemFactor != STATE_NOTHING ? mState.mMemFactor : 0;
+ }
+
+ public void readLocked() {
+ mState.readLocked();
+ }
+
+ public boolean shouldWriteNowLocked(long now) {
+ if (now > (mState.mLastWriteTime+WRITE_PERIOD)) {
+ if (SystemClock.elapsedRealtime() > (mState.mTimePeriodStartRealtime+COMMIT_PERIOD)) {
+ mCommitPending = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public void shutdownLocked() {
+ Slog.w(TAG, "Writing process stats before shutdown...");
+ writeStateSyncLocked();
+ mShuttingDown = true;
+ }
+
+ public void writeStateAsyncLocked() {
+ writeStateLocked(false);
+ }
+
+ public void writeStateSyncLocked() {
+ writeStateLocked(true);
+ }
+
+ private void writeStateLocked(boolean sync) {
+ if (mShuttingDown) {
+ return;
+ }
+ boolean commitPending = mCommitPending;
+ mCommitPending = false;
+ mState.writeStateLocked(sync, commitPending);
+ }
+
+ private ArrayList<String> getCommittedFiles(int minNum, boolean inclAll) {
+ File[] files = mBaseDir.listFiles();
+ if (files == null || files.length <= minNum) {
+ return null;
+ }
+ ArrayList<String> filesArray = new ArrayList<String>(files.length);
+ String currentFile = mState.mFile.getBaseFile().getPath();
+ if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile);
+ for (int i=0; i<files.length; i++) {
+ File file = files[i];
+ String fileStr = file.getPath();
+ if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr);
+ if (!inclAll && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) {
+ if (DEBUG) Slog.d(TAG, "Skipping: already checked in");
+ continue;
+ }
+ if (fileStr.equals(currentFile)) {
+ if (DEBUG) Slog.d(TAG, "Skipping: current stats");
+ continue;
+ }
+ filesArray.add(fileStr);
+ }
+ Collections.sort(filesArray);
+ return filesArray;
+ }
+
+ public void trimHistoricStatesWriteLocked() {
+ ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, true);
+ if (filesArray == null) {
+ return;
+ }
+ while (filesArray.size() > MAX_HISTORIC_STATES) {
+ String file = filesArray.remove(0);
+ Slog.i(TAG, "Pruning old procstats: " + file);
+ (new File(file)).delete();
+ }
+ }
+
+ static private void printScreenLabel(PrintWriter pw, int offset) {
+ switch (offset) {
+ case ADJ_NOTHING:
+ pw.print(" ");
+ break;
+ case ADJ_SCREEN_OFF:
+ pw.print("Screen Off / ");
+ break;
+ case ADJ_SCREEN_ON:
+ pw.print("Screen On / ");
+ break;
+ default:
+ pw.print("?????????? / ");
+ break;
+ }
+ }
+
+ static private void printScreenLabelCsv(PrintWriter pw, int offset) {
+ switch (offset) {
+ case ADJ_NOTHING:
+ break;
+ case ADJ_SCREEN_OFF:
+ pw.print(ADJ_SCREEN_NAMES_CSV[0]);
+ break;
+ case ADJ_SCREEN_ON:
+ pw.print(ADJ_SCREEN_NAMES_CSV[1]);
+ break;
+ default:
+ pw.print("???");
+ break;
+ }
+ }
+
+ static private void printMemLabel(PrintWriter pw, int offset) {
+ switch (offset) {
+ case ADJ_NOTHING:
+ pw.print(" ");
+ break;
+ case ADJ_MEM_FACTOR_NORMAL:
+ pw.print("Norm / ");
+ break;
+ case ADJ_MEM_FACTOR_MODERATE:
+ pw.print("Mod / ");
+ break;
+ case ADJ_MEM_FACTOR_LOW:
+ pw.print("Low / ");
+ break;
+ case ADJ_MEM_FACTOR_CRITICAL:
+ pw.print("Crit / ");
+ break;
+ default:
+ pw.print("???? / ");
+ break;
+ }
+ }
+
+ static private void printMemLabelCsv(PrintWriter pw, int offset) {
+ if (offset >= ADJ_MEM_FACTOR_NORMAL) {
+ if (offset <= ADJ_MEM_FACTOR_CRITICAL) {
+ pw.print(ADJ_MEM_NAMES_CSV[offset]);
+ } else {
+ pw.print("???");
+ }
+ }
+ }
+
+ static void dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
+ int curState, long curStartTime, long now) {
+ long totalTime = 0;
+ int printedScreen = -1;
+ for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
+ int printedMem = -1;
+ for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
+ int state = imem+iscreen;
+ long time = durations[state];
+ String running = "";
+ if (curState == state) {
+ time += now - curStartTime;
+ running = " (running)";
+ }
+ if (time != 0) {
+ pw.print(prefix);
+ printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
+ printedMem = imem;
+ TimeUtils.formatDuration(time, pw); pw.println(running);
+ totalTime += time;
+ }
+ }
+ }
+ if (totalTime != 0) {
+ pw.print(prefix);
+ printScreenLabel(pw, STATE_NOTHING);
+ pw.print("TOTAL: ");
+ TimeUtils.formatDuration(totalTime, pw);
+ pw.println();
+ }
+ }
+
+ static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations,
+ int curState, long curStartTime, long now) {
+ for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
+ for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
+ int state = imem+iscreen;
+ long time = durations[state];
+ if (curState == state) {
+ time += now - curStartTime;
+ }
+ if (time != 0) {
+ printAdjTagAndValue(pw, state, time);
+ }
+ }
+ }
+ }
+
+ static void dumpServiceTimeCheckin(PrintWriter pw, String label, String packageName,
+ int uid, String serviceName, ServiceState svc, int opCount, long[] durations,
+ int curState, long curStartTime, long now) {
+ if (opCount <= 0) {
+ return;
+ }
+ pw.print(label);
+ pw.print(",");
+ pw.print(packageName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(serviceName);
+ pw.print(opCount);
+ dumpAdjTimesCheckin(pw, ",", durations, curState, curStartTime, now);
+ pw.println();
+ }
+
+ static final class ProcessDataCollection {
+ final int[] screenStates;
+ final int[] memStates;
+ final int[] procStates;
+
+ long totalTime;
+ long numPss;
+ long minPss;
+ long avgPss;
+ long maxPss;
+
+ ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) {
+ screenStates = _screenStates;
+ memStates = _memStates;
+ procStates = _procStates;
+ }
+
+ void print(PrintWriter pw, boolean full) {
+ TimeUtils.formatDuration(totalTime, pw);
+ if (numPss > 0) {
+ pw.print(" (");
+ printSizeValue(pw, minPss * 1024);
+ pw.print("-");
+ printSizeValue(pw, avgPss * 1024);
+ pw.print("-");
+ printSizeValue(pw, maxPss * 1024);
+ if (full) {
+ pw.print(" over ");
+ pw.print(numPss);
+ }
+ pw.print(")");
+ }
+ }
+ }
+
+ static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) {
+ data.totalTime = 0;
+ data.numPss = data.minPss = data.avgPss = data.maxPss = 0;
+ for (int is=0; is<data.screenStates.length; is++) {
+ for (int im=0; im<data.memStates.length; im++) {
+ for (int ip=0; ip<data.procStates.length; ip++) {
+ int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT)
+ + data.procStates[ip];
+ data.totalTime += proc.getDuration(bucket, now);
+ long samples = proc.getPssSampleCount(bucket);
+ if (samples > 0) {
+ long minPss = proc.getPssMinimum(bucket);
+ long avgPss = proc.getPssAverage(bucket);
+ long maxPss = proc.getPssMaximum(bucket);
+ if (data.numPss == 0) {
+ data.minPss = minPss;
+ data.avgPss = avgPss;
+ data.maxPss = maxPss;
+ } else {
+ if (minPss < data.minPss) {
+ data.minPss = minPss;
+ }
+ data.avgPss = (long)( ((data.avgPss*(double)data.numPss)
+ + (avgPss*(double)samples)) / (data.numPss+samples) );
+ if (maxPss > data.maxPss) {
+ data.maxPss = maxPss;
+ }
+ }
+ data.numPss += samples;
+ }
+ }
+ }
+ }
+ }
+
+ static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates,
+ int[] procStates, long now) {
+ long totalTime = 0;
+ /*
+ for (int i=0; i<proc.mDurationsTableSize; i++) {
+ int val = proc.mDurationsTable[i];
+ totalTime += proc.mState.getLong(val, 0);
+ if ((val&0xff) == proc.mCurState) {
+ totalTime += now - proc.mStartTime;
+ }
+ }
+ */
+ for (int is=0; is<screenStates.length; is++) {
+ for (int im=0; im<memStates.length; im++) {
+ for (int ip=0; ip<procStates.length; ip++) {
+ int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT)
+ + procStates[ip];
+ totalTime += proc.getDuration(bucket, now);
+ }
+ }
+ }
+ proc.mTmpTotalTime = totalTime;
+ return totalTime;
+ }
+
+ static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc,
+ int[] screenStates, int[] memStates, int[] procStates, long now) {
+ long totalTime = 0;
+ int printedScreen = -1;
+ for (int is=0; is<screenStates.length; is++) {
+ int printedMem = -1;
+ for (int im=0; im<memStates.length; im++) {
+ for (int ip=0; ip<procStates.length; ip++) {
+ final int iscreen = screenStates[is];
+ final int imem = memStates[im];
+ final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
+ long time = proc.getDuration(bucket, now);
+ String running = "";
+ if (proc.mCurState == bucket) {
+ running = " (running)";
+ }
+ if (time != 0) {
+ pw.print(prefix);
+ if (screenStates.length > 1) {
+ printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ }
+ if (memStates.length > 1) {
+ printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
+ printedMem = imem;
+ }
+ pw.print(STATE_NAMES[procStates[ip]]); pw.print(": ");
+ TimeUtils.formatDuration(time, pw); pw.println(running);
+ totalTime += time;
+ }
+ }
+ }
+ }
+ if (totalTime != 0) {
+ pw.print(prefix);
+ if (screenStates.length > 1) {
+ printScreenLabel(pw, STATE_NOTHING);
+ }
+ if (memStates.length > 1) {
+ printMemLabel(pw, STATE_NOTHING);
+ }
+ pw.print("TOTAL : ");
+ TimeUtils.formatDuration(totalTime, pw);
+ pw.println();
+ }
+ }
+
+ static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates,
+ int[] memStates, int[] procStates) {
+ boolean printedHeader = false;
+ int printedScreen = -1;
+ for (int is=0; is<screenStates.length; is++) {
+ int printedMem = -1;
+ for (int im=0; im<memStates.length; im++) {
+ for (int ip=0; ip<procStates.length; ip++) {
+ final int iscreen = screenStates[is];
+ final int imem = memStates[im];
+ final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
+ long count = proc.getPssSampleCount(bucket);
+ if (count > 0) {
+ if (!printedHeader) {
+ pw.print(prefix);
+ pw.print("PSS (");
+ pw.print(proc.mPssTableSize);
+ pw.println(" entries):");
+ printedHeader = true;
+ }
+ pw.print(prefix);
+ pw.print(" ");
+ if (screenStates.length > 1) {
+ printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ }
+ if (memStates.length > 1) {
+ printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
+ printedMem = imem;
+ }
+ pw.print(STATE_NAMES[procStates[ip]]); pw.print(": ");
+ pw.print(count);
+ pw.print(" samples ");
+ printSizeValue(pw, proc.getPssMinimum(bucket) * 1024);
+ pw.print(" ");
+ printSizeValue(pw, proc.getPssAverage(bucket) * 1024);
+ pw.print(" ");
+ printSizeValue(pw, proc.getPssMaximum(bucket) * 1024);
+ pw.println();
+ }
+ }
+ }
+ }
+ if (proc.mNumExcessiveWake != 0) {
+ pw.print(prefix); pw.print("Killed for excessive wake locks: ");
+ pw.print(proc.mNumExcessiveWake); pw.println(" times");
+ }
+ if (proc.mNumExcessiveCpu != 0) {
+ pw.print(prefix); pw.print("Killed for excessive CPU use: ");
+ pw.print(proc.mNumExcessiveCpu); pw.println(" times");
+ }
+ }
+
+ static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
+ int[] memStates, int[] procStates) {
+ final int NS = screenStates != null ? screenStates.length : 1;
+ final int NM = memStates != null ? memStates.length : 1;
+ final int NP = procStates != null ? procStates.length : 1;
+ for (int is=0; is<NS; is++) {
+ for (int im=0; im<NM; im++) {
+ for (int ip=0; ip<NP; ip++) {
+ pw.print(sep);
+ boolean printed = false;
+ if (screenStates != null && screenStates.length > 1) {
+ printScreenLabelCsv(pw, screenStates[is]);
+ printed = true;
+ }
+ if (memStates != null && memStates.length > 1) {
+ if (printed) {
+ pw.print("-");
+ }
+ printMemLabelCsv(pw, memStates[im]);
+ printed = true;
+ }
+ if (procStates != null && procStates.length > 1) {
+ if (printed) {
+ pw.print("-");
+ }
+ pw.print(STATE_NAMES_CSV[procStates[ip]]);
+ }
+ }
+ }
+ }
+ }
+
+ static void dumpProcessStateCsv(PrintWriter pw, ProcessState proc,
+ boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
+ boolean sepProcStates, int[] procStates, long now) {
+ final int NSS = sepScreenStates ? screenStates.length : 1;
+ final int NMS = sepMemStates ? memStates.length : 1;
+ final int NPS = sepProcStates ? procStates.length : 1;
+ for (int iss=0; iss<NSS; iss++) {
+ for (int ims=0; ims<NMS; ims++) {
+ for (int ips=0; ips<NPS; ips++) {
+ final int vsscreen = sepScreenStates ? screenStates[iss] : 0;
+ final int vsmem = sepMemStates ? memStates[ims] : 0;
+ final int vsproc = sepProcStates ? procStates[ips] : 0;
+ final int NSA = sepScreenStates ? 1 : screenStates.length;
+ final int NMA = sepMemStates ? 1 : memStates.length;
+ final int NPA = sepProcStates ? 1 : procStates.length;
+ long totalTime = 0;
+ for (int isa=0; isa<NSA; isa++) {
+ for (int ima=0; ima<NMA; ima++) {
+ for (int ipa=0; ipa<NPA; ipa++) {
+ final int vascreen = sepScreenStates ? 0 : screenStates[isa];
+ final int vamem = sepMemStates ? 0 : memStates[ima];
+ final int vaproc = sepProcStates ? 0 : procStates[ipa];
+ final int bucket = ((vsscreen + vascreen + vsmem + vamem)
+ * STATE_COUNT) + vsproc + vaproc;
+ totalTime += proc.getDuration(bucket, now);
+ }
+ }
+ }
+ pw.print(CSV_SEP);
+ pw.print(totalTime);
+ }
+ }
+ }
+ }
+
+ static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs,
+ int[] screenStates, int[] memStates, int[] procStates, long now) {
+ String innerPrefix = prefix + " ";
+ for (int i=procs.size()-1; i>=0; i--) {
+ ProcessState proc = procs.get(i);
+ pw.print(prefix);
+ pw.print(proc.mName);
+ pw.print(" / ");
+ UserHandle.formatUid(pw, proc.mUid);
+ pw.print(" (");
+ pw.print(proc.mDurationsTableSize);
+ pw.print(" entries)");
+ pw.println(":");
+ dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now);
+ if (proc.mPssTableSize > 0) {
+ dumpProcessPss(pw, innerPrefix, proc, screenStates, memStates, procStates);
+ }
+ }
+ }
+
+ static void dumpProcessSummaryDetails(PrintWriter pw, ProcessState proc, String prefix,
+ String label, int[] screenStates, int[] memStates, int[] procStates,
+ long now, boolean full) {
+ ProcessDataCollection totals = new ProcessDataCollection(screenStates,
+ memStates, procStates);
+ computeProcessData(proc, totals, now);
+ if (totals.totalTime != 0 || totals.numPss != 0) {
+ if (prefix != null) {
+ pw.print(prefix);
+ }
+ if (label != null) {
+ pw.print(label);
+ }
+ totals.print(pw, full);
+ if (prefix != null) {
+ pw.println();
+ }
+ }
+ }
+
+ static void dumpProcessSummary(PrintWriter pw, String prefix, ArrayList<ProcessState> procs,
+ int[] screenStates, int[] memStates, int[] procStates, long now) {
+ for (int i=procs.size()-1; i>=0; i--) {
+ ProcessState proc = procs.get(i);
+ pw.print(prefix);
+ pw.print("* ");
+ pw.print(proc.mName);
+ pw.print(" / ");
+ UserHandle.formatUid(pw, proc.mUid);
+ pw.println(":");
+ dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates,
+ procStates, now, true);
+ dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates,
+ new int[] {STATE_PERSISTENT}, now, true);
+ dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates,
+ new int[] {STATE_TOP}, now, true);
+ dumpProcessSummaryDetails(pw, proc, prefix, " Foreground: ", screenStates, memStates,
+ new int[] {STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE}, now, true);
+ dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates,
+ new int[] {STATE_BACKUP}, now, true);
+ dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates,
+ new int[] {STATE_SERVICE}, now, true);
+ dumpProcessSummaryDetails(pw, proc, prefix, " Home: ", screenStates, memStates,
+ new int[] {STATE_HOME}, now, true);
+ dumpProcessSummaryDetails(pw, proc, prefix, " Previous: ", screenStates, memStates,
+ new int[] {STATE_PREVIOUS}, now, true);
+ dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates,
+ new int[] {STATE_CACHED}, now, true);
+ }
+ }
+
+ private static void printSizeValue(PrintWriter pw, long number) {
+ float result = number;
+ String suffix = "";
+ if (result > 900) {
+ suffix = "KB";
+ result = result / 1024;
+ }
+ if (result > 900) {
+ suffix = "MB";
+ result = result / 1024;
+ }
+ if (result > 900) {
+ suffix = "GB";
+ result = result / 1024;
+ }
+ if (result > 900) {
+ suffix = "TB";
+ result = result / 1024;
+ }
+ if (result > 900) {
+ suffix = "PB";
+ result = result / 1024;
+ }
+ String value;
+ if (result < 1) {
+ value = String.format("%.2f", result);
+ } else if (result < 10) {
+ value = String.format("%.2f", result);
+ } else if (result < 100) {
+ value = String.format("%.2f", result);
+ } else {
+ value = String.format("%.0f", result);
+ }
+ pw.print(value);
+ pw.print(suffix);
+ }
+
+ static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
+ boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
+ boolean sepProcStates, int[] procStates, long now) {
+ pw.print("process");
+ pw.print(CSV_SEP);
+ pw.print("uid");
+ dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null,
+ sepMemStates ? memStates : null,
+ sepProcStates ? procStates : null);
+ pw.println();
+ for (int i=procs.size()-1; i>=0; i--) {
+ ProcessState proc = procs.get(i);
+ pw.print(proc.mName);
+ pw.print(CSV_SEP);
+ UserHandle.formatUid(pw, proc.mUid);
+ dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates,
+ sepMemStates, memStates, sepProcStates, procStates, now);
+ pw.println();
+ }
+ }
+
+ boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
+ boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
+ boolean sepProcStates, int[] procStates, long now, String reqPackage) {
+ ArrayList<ProcessState> procs = mState.collectProcessesLocked(screenStates, memStates,
+ procStates, now, reqPackage);
+ if (procs.size() > 0) {
+ if (header != null) {
+ pw.println(header);
+ }
+ dumpProcessListCsv(pw, procs, sepScreenStates, screenStates,
+ sepMemStates, memStates, sepProcStates, procStates, now);
+ return true;
+ }
+ return false;
+ }
+
+ static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) {
+ int index = value/mod;
+ if (index >= 0 && index < array.length) {
+ pw.print(array[index]);
+ } else {
+ pw.print('?');
+ }
+ return value - index*mod;
+ }
+
+ static void printProcStateTag(PrintWriter pw, int state) {
+ state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT);
+ state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT);
+ printArrayEntry(pw, STATE_TAGS, state, 1);
+ }
+
+ static void printAdjTag(PrintWriter pw, int state) {
+ state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD);
+ printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
+ }
+
+ static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
+ pw.print(',');
+ printProcStateTag(pw, state);
+ pw.print(':');
+ pw.print(value);
+ }
+
+ static void printAdjTagAndValue(PrintWriter pw, int state, long value) {
+ pw.print(',');
+ printAdjTag(pw, state);
+ pw.print(':');
+ pw.print(value);
+ }
+
+ static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) {
+ boolean didCurState = false;
+ for (int i=0; i<proc.mDurationsTableSize; i++) {
+ int off = proc.mDurationsTable[i];
+ int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ long time = proc.mState.getLong(off, 0);
+ if (proc.mCurState == type) {
+ didCurState = true;
+ time += now - proc.mStartTime;
+ }
+ printProcStateTagAndValue(pw, type, time);
+ }
+ if (!didCurState && proc.mCurState != STATE_NOTHING) {
+ printProcStateTagAndValue(pw, proc.mCurState, now - proc.mStartTime);
+ }
+ }
+
+ static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) {
+ for (int i=0; i<proc.mPssTableSize; i++) {
+ int off = proc.mPssTable[i];
+ int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ long count = proc.mState.getLong(off, PSS_SAMPLE_COUNT);
+ long min = proc.mState.getLong(off, PSS_MINIMUM);
+ long avg = proc.mState.getLong(off, PSS_AVERAGE);
+ long max = proc.mState.getLong(off, PSS_MAXIMUM);
+ pw.print(',');
+ printProcStateTag(pw, type);
+ pw.print(':');
+ pw.print(count);
+ pw.print(':');
+ pw.print(min);
+ pw.print(':');
+ pw.print(avg);
+ pw.print(':');
+ pw.print(max);
+ }
+ }
+
+ static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep,
+ String[] outError) {
+ ArrayList<Integer> res = new ArrayList<Integer>();
+ int lastPos = 0;
+ for (int i=0; i<=arg.length(); i++) {
+ char c = i < arg.length() ? arg.charAt(i) : 0;
+ if (c != ',' && c != '+' && c != ' ' && c != 0) {
+ continue;
+ }
+ boolean isSep = c == ',';
+ if (lastPos == 0) {
+ // We now know the type of op.
+ outSep[0] = isSep;
+ } else if (c != 0 && outSep[0] != isSep) {
+ outError[0] = "inconsistent separators (can't mix ',' with '+')";
+ return null;
+ }
+ if (lastPos < (i-1)) {
+ String str = arg.substring(lastPos, i);
+ for (int j=0; j<states.length; j++) {
+ if (str.equals(states[j])) {
+ res.add(j);
+ str = null;
+ break;
+ }
+ }
+ if (str != null) {
+ outError[0] = "invalid word \"" + str + "\"";
+ return null;
+ }
+ }
+ lastPos = i + 1;
+ }
+
+ int[] finalRes = new int[res.size()];
+ for (int i=0; i<res.size(); i++) {
+ finalRes[i] = res.get(i) * mult;
+ }
+ return finalRes;
+ }
+
+ static private void dumpHelp(PrintWriter pw) {
+ pw.println("Process stats (procstats) dump options:");
+ pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
+ pw.println(" [--details] [--current] [--commit] [--write] [-h] [<package.name>]");
+ pw.println(" --checkin: perform a checkin: print and delete old committed states.");
+ pw.println(" --c: print only state in checkin format.");
+ pw.println(" --csv: output data suitable for putting in a spreadsheet.");
+ pw.println(" --csv-screen: on, off.");
+ pw.println(" --csv-mem: norm, mod, low, crit.");
+ pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,");
+ pw.println(" service, home, prev, cached");
+ pw.println(" --details: dump all execution details, not just summary.");
+ pw.println(" --current: only dump current state.");
+ pw.println(" --commit: commit current stats to disk and reset to start new stats.");
+ pw.println(" --write: write current in-memory stats to disk.");
+ pw.println(" --read: replace current stats with last-written stats.");
+ pw.println(" -a: print everything.");
+ pw.println(" -h: print this help text.");
+ pw.println(" <package.name>: optional name of package to filter output by.");
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final long now = SystemClock.uptimeMillis();
+
+ boolean isCheckin = false;
+ boolean isCompact = false;
+ boolean isCsv = false;
+ boolean currentOnly = false;
+ boolean dumpDetails = false;
+ boolean dumpAll = false;
+ String reqPackage = null;
+ boolean csvSepScreenStats = false;
+ int[] csvScreenStats = new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON};
+ boolean csvSepMemStats = false;
+ int[] csvMemStats = new int[] {ADJ_MEM_FACTOR_CRITICAL};
+ boolean csvSepProcStats = true;
+ int[] csvProcStats = new int[] {
+ STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+ STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
+ STATE_PREVIOUS, STATE_CACHED };
+ if (args != null) {
+ for (int i=0; i<args.length; i++) {
+ String arg = args[i];
+ if ("--checkin".equals(arg)) {
+ isCheckin = true;
+ } else if ("-c".equals(arg)) {
+ isCompact = true;
+ } else if ("--csv".equals(arg)) {
+ isCsv = true;
+ } else if ("--csv-screen".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("Error: argument required for --csv-screen");
+ dumpHelp(pw);
+ return;
+ }
+ boolean[] sep = new boolean[1];
+ String[] error = new String[1];
+ csvScreenStats = parseStateList(ADJ_SCREEN_NAMES_CSV, ADJ_SCREEN_MOD,
+ args[i], sep, error);
+ if (csvScreenStats == null) {
+ pw.println("Error in \"" + args[i] + "\": " + error[0]);
+ dumpHelp(pw);
+ return;
+ }
+ csvSepScreenStats = sep[0];
+ } else if ("--csv-mem".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("Error: argument required for --csv-mem");
+ dumpHelp(pw);
+ return;
+ }
+ boolean[] sep = new boolean[1];
+ String[] error = new String[1];
+ csvMemStats = parseStateList(ADJ_MEM_NAMES_CSV, 1, args[i], sep, error);
+ if (csvMemStats == null) {
+ pw.println("Error in \"" + args[i] + "\": " + error[0]);
+ dumpHelp(pw);
+ return;
+ }
+ csvSepMemStats = sep[0];
+ } else if ("--csv-proc".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("Error: argument required for --csv-proc");
+ dumpHelp(pw);
+ return;
+ }
+ boolean[] sep = new boolean[1];
+ String[] error = new String[1];
+ csvProcStats = parseStateList(STATE_NAMES_CSV, 1, args[i], sep, error);
+ if (csvProcStats == null) {
+ pw.println("Error in \"" + args[i] + "\": " + error[0]);
+ dumpHelp(pw);
+ return;
+ }
+ csvSepProcStats = sep[0];
+ } else if ("--details".equals(arg)) {
+ dumpDetails = true;
+ } else if ("--current".equals(arg)) {
+ currentOnly = true;
+ } else if ("--commit".equals(arg)) {
+ mState.writeStateLocked(true, true);
+ pw.println("Process stats committed.");
+ return;
+ } else if ("--write".equals(arg)) {
+ writeStateSyncLocked();
+ pw.println("Process stats written.");
+ return;
+ } else if ("--read".equals(arg)) {
+ readLocked();
+ pw.println("Process stats read.");
+ return;
+ } else if ("-h".equals(arg)) {
+ dumpHelp(pw);
+ return;
+ } else if ("-a".equals(arg)) {
+ dumpDetails = true;
+ dumpAll = true;
+ } else if (arg.length() > 0 && arg.charAt(0) == '-'){
+ pw.println("Unknown option: " + arg);
+ dumpHelp(pw);
+ return;
+ } else {
+ // Not an option, last argument must be a package name.
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) {
+ reqPackage = arg;
+ // Include all details, since we know we are only going to
+ // be dumping a smaller set of data. In fact only the details
+ // container per-package data, so that are needed to be able
+ // to dump anything at all when filtering by package.
+ dumpDetails = true;
+ }
+ } catch (RemoteException e) {
+ }
+ if (reqPackage == null) {
+ pw.println("Unknown package: " + arg);
+ dumpHelp(pw);
+ return;
+ }
+ }
+ }
+ }
+
+ if (isCsv) {
+ pw.print("Processes running summed over");
+ if (!csvSepScreenStats) {
+ for (int i=0; i<csvScreenStats.length; i++) {
+ pw.print(" ");
+ printScreenLabelCsv(pw, csvScreenStats[i]);
+ }
+ }
+ if (!csvSepMemStats) {
+ for (int i=0; i<csvMemStats.length; i++) {
+ pw.print(" ");
+ printMemLabelCsv(pw, csvMemStats[i]);
+ }
+ }
+ if (!csvSepProcStats) {
+ for (int i=0; i<csvProcStats.length; i++) {
+ pw.print(" ");
+ pw.print(STATE_NAMES_CSV[csvProcStats[i]]);
+ }
+ }
+ pw.println();
+ synchronized (mLock) {
+ dumpFilteredProcessesCsvLocked(pw, null,
+ csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
+ csvSepProcStats, csvProcStats, now, reqPackage);
+ /*
+ dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:",
+ false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+ true, new int[] {ADJ_MEM_FACTOR_CRITICAL},
+ true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+ STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
+ STATE_PREVIOUS, STATE_CACHED},
+ now, reqPackage);
+ dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:",
+ false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+ false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW,
+ ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE},
+ true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+ STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
+ STATE_PREVIOUS, STATE_CACHED},
+ now, reqPackage);
+ */
+ }
+ return;
+ }
+
+ boolean sepNeeded = false;
+ if (!currentOnly || isCheckin) {
+ mWriteLock.lock();
+ try {
+ ArrayList<String> files = getCommittedFiles(0, !isCheckin);
+ if (files != null) {
+ for (int i=0; i<files.size(); i++) {
+ if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
+ try {
+ State state = new State(files.get(i));
+ String fileStr = state.mFile.getBaseFile().getPath();
+ boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
+ if (isCheckin || isCompact) {
+ // Don't really need to lock because we uniquely own this object.
+ state.dumpCheckinLocked(pw, reqPackage);
+ } else {
+ if (sepNeeded) {
+ pw.println();
+ } else {
+ sepNeeded = true;
+ }
+ pw.print("COMMITTED STATS FROM ");
+ pw.print(state.mTimePeriodStartClockStr);
+ if (checkedIn) pw.print(" (checked in)");
+ pw.println(":");
+ // Don't really need to lock because we uniquely own this object.
+ if (dumpDetails) {
+ state.dumpLocked(pw, reqPackage, now, dumpAll);
+ } else {
+ state.dumpSummaryLocked(pw, reqPackage, now);
+ }
+ }
+ if (isCheckin) {
+ // Rename file suffix to mark that it has checked in.
+ state.mFile.getBaseFile().renameTo(new File(
+ fileStr + STATE_FILE_CHECKIN_SUFFIX));
+ }
+ } catch (Throwable e) {
+ pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
+ e.printStackTrace(pw);
+ }
+ if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
+ }
+ }
+ } finally {
+ mWriteLock.unlock();
+ }
+ }
+ if (!isCheckin) {
+ synchronized (mLock) {
+ if (isCompact) {
+ mState.dumpCheckinLocked(pw, reqPackage);
+ } else {
+ if (sepNeeded) {
+ pw.println();
+ pw.println("CURRENT STATS:");
+ }
+ if (dumpDetails) {
+ mState.dumpLocked(pw, reqPackage, now, dumpAll);
+ } else {
+ mState.dumpSummaryLocked(pw, reqPackage, now);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index 9dbf5f5..7da8c48 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -22,6 +22,7 @@
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.os.TransferPipe;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -35,7 +36,7 @@
* Keeps track of content providers by authority (name) and class. It separates the mapping by
* user and ones that are not user-specific (system providers).
*/
-public class ProviderMap {
+public final class ProviderMap {
private static final String TAG = "ProviderMap";
@@ -230,58 +231,87 @@
return didSomething;
}
- private void dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll,
- HashMap<ComponentName, ContentProviderRecord> map) {
+ private boolean dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, String dumpPackage,
+ String header, boolean needSep, HashMap<ComponentName, ContentProviderRecord> map) {
Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator();
+ boolean written = false;
while (it.hasNext()) {
Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
ContentProviderRecord r = e.getValue();
+ if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+ continue;
+ }
+ if (needSep) {
+ pw.println("");
+ needSep = false;
+ }
+ if (header != null) {
+ pw.println(header);
+ header = null;
+ }
+ written = true;
pw.print(" * ");
pw.println(r);
r.dump(pw, " ", dumpAll);
}
+ return written;
}
- private void dumpProvidersByNameLocked(PrintWriter pw,
- HashMap<String, ContentProviderRecord> map) {
+ private boolean dumpProvidersByNameLocked(PrintWriter pw, String dumpPackage,
+ String header, boolean needSep, HashMap<String, ContentProviderRecord> map) {
Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator();
+ boolean written = false;
while (it.hasNext()) {
Map.Entry<String, ContentProviderRecord> e = it.next();
ContentProviderRecord r = e.getValue();
+ if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+ continue;
+ }
+ if (needSep) {
+ pw.println("");
+ needSep = false;
+ }
+ if (header != null) {
+ pw.println(header);
+ header = null;
+ }
+ written = true;
pw.print(" ");
pw.print(e.getKey());
pw.print(": ");
pw.println(r.toShortString());
}
+ return written;
}
- void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) {
+ boolean dumpProvidersLocked(PrintWriter pw, boolean dumpAll, String dumpPackage) {
+ boolean needSep = false;
+
if (mSingletonByClass.size() > 0) {
- pw.println(" Published single-user content providers (by class):");
- dumpProvidersByClassLocked(pw, dumpAll, mSingletonByClass);
+ needSep |= dumpProvidersByClassLocked(pw, dumpAll, dumpPackage,
+ " Published single-user content providers (by class):", needSep,
+ mSingletonByClass);
}
- pw.println("");
for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
- pw.println("");
- pw.println(" Published user " + mProvidersByClassPerUser.keyAt(i)
- + " content providers (by class):");
- dumpProvidersByClassLocked(pw, dumpAll, map);
+ needSep |= dumpProvidersByClassLocked(pw, dumpAll, dumpPackage,
+ " Published user " + mProvidersByClassPerUser.keyAt(i)
+ + " content providers (by class):", needSep, map);
}
if (dumpAll) {
- pw.println("");
- pw.println(" Single-user authority to provider mappings:");
- dumpProvidersByNameLocked(pw, mSingletonByName);
+ needSep |= dumpProvidersByNameLocked(pw, dumpPackage,
+ " Single-user authority to provider mappings:", needSep, mSingletonByName);
for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
- pw.println("");
- pw.println(" User " + mProvidersByNamePerUser.keyAt(i)
- + " authority to provider mappings:");
- dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i));
+ needSep |= dumpProvidersByNameLocked(pw, dumpPackage,
+ " User " + mProvidersByNamePerUser.keyAt(i)
+ + " authority to provider mappings:", needSep,
+ mProvidersByNamePerUser.valueAt(i));
}
}
+ return needSep;
}
protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java
index 9b6701e..b19cc5d 100644
--- a/services/java/com/android/server/am/ReceiverList.java
+++ b/services/java/com/android/server/am/ReceiverList.java
@@ -32,7 +32,7 @@
* A receiver object that has registered for one or more broadcasts.
* The ArrayList holds BroadcastFilter objects.
*/
-class ReceiverList extends ArrayList<BroadcastFilter>
+final class ReceiverList extends ArrayList<BroadcastFilter>
implements IBinder.DeathRecipient {
final ActivityManagerService owner;
public final IIntentReceiver receiver;
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 9fdd293..5000940 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -16,40 +16,38 @@
package com.android.server.am;
-import android.app.PendingIntent;
-import android.net.Uri;
-import android.provider.Settings;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.NotificationManagerService;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
/**
* A running application service.
*/
-class ServiceRecord extends Binder {
+final class ServiceRecord extends Binder {
// Maximum number of delivery attempts before giving up.
static final int MAX_DELIVERY_COUNT = 3;
@@ -75,12 +73,13 @@
final String dataDir; // where activity data should go
final boolean exported; // from ServiceInfo.exported
final Runnable restarter; // used to schedule retries of starting the service
+ final ProcessTracker.ServiceState tracker; // tracking service execution, may be null
final long createTime; // when this service was created
- final HashMap<Intent.FilterComparison, IntentBindRecord> bindings
- = new HashMap<Intent.FilterComparison, IntentBindRecord>();
+ final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
+ = new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
// All active bindings to the service.
- final HashMap<IBinder, ArrayList<ConnectionRecord>> connections
- = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
+ final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
+ = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();
// IBinder -> ConnectionRecord of all bound clients
ProcessRecord app; // where this service is running or null.
@@ -258,10 +257,9 @@
dumpStartList(pw, prefix, pendingStarts, 0);
}
if (bindings.size() > 0) {
- Iterator<IntentBindRecord> it = bindings.values().iterator();
pw.print(prefix); pw.println("Bindings:");
- while (it.hasNext()) {
- IntentBindRecord b = it.next();
+ for (int i=0; i<bindings.size(); i++) {
+ IntentBindRecord b = bindings.valueAt(i);
pw.print(prefix); pw.print("* IntentBindRecord{");
pw.print(Integer.toHexString(System.identityHashCode(b)));
if ((b.collectFlags()&Context.BIND_AUTO_CREATE) != 0) {
@@ -273,9 +271,8 @@
}
if (connections.size() > 0) {
pw.print(prefix); pw.println("All Connections:");
- Iterator<ArrayList<ConnectionRecord>> it = connections.values().iterator();
- while (it.hasNext()) {
- ArrayList<ConnectionRecord> c = it.next();
+ for (int conni=0; conni<connections.size(); conni++) {
+ ArrayList<ConnectionRecord> c = connections.valueAt(conni);
for (int i=0; i<c.size(); i++) {
pw.print(prefix); pw.print(" "); pw.println(c.get(i));
}
@@ -285,7 +282,8 @@
ServiceRecord(ActivityManagerService ams,
BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
- Intent.FilterComparison intent, ServiceInfo sInfo, Runnable restarter) {
+ Intent.FilterComparison intent, ServiceInfo sInfo, Runnable restarter,
+ ProcessTracker.ServiceState tracker) {
this.ams = ams;
this.stats = servStats;
this.name = name;
@@ -301,6 +299,7 @@
dataDir = sInfo.applicationInfo.dataDir;
exported = sInfo.exported;
this.restarter = restarter;
+ this.tracker = tracker;
createTime = SystemClock.elapsedRealtime();
lastActivity = SystemClock.uptimeMillis();
userId = UserHandle.getUserId(appInfo.uid);
@@ -323,6 +322,20 @@
return a;
}
+ public boolean hasAutoCreateConnections() {
+ // XXX should probably keep a count of the number of auto-create
+ // connections directly in the service.
+ for (int conni=connections.size()-1; conni>=0; conni--) {
+ ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
+ for (int i=0; i<cr.size(); i++) {
+ if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public void resetRestartCounter() {
restartCount = 0;
restartDelay = 0;
diff --git a/services/java/com/android/server/am/StrictModeViolationDialog.java b/services/java/com/android/server/am/StrictModeViolationDialog.java
index 35d50a1..b6d0daa 100644
--- a/services/java/com/android/server/am/StrictModeViolationDialog.java
+++ b/services/java/com/android/server/am/StrictModeViolationDialog.java
@@ -25,7 +25,7 @@
import android.os.Message;
import android.util.Slog;
-class StrictModeViolationDialog extends BaseErrorDialog {
+final class StrictModeViolationDialog extends BaseErrorDialog {
private final static String TAG = "StrictModeViolationDialog";
private final ActivityManagerService mService;
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 1bae9ca..d79211c 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -16,15 +16,24 @@
package com.android.server.am;
+import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.IThumbnailRetriever;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.graphics.Bitmap;
import android.os.UserHandle;
import android.util.Slog;
import java.io.PrintWriter;
+import java.util.ArrayList;
-class TaskRecord extends ThumbnailHolder {
+final class TaskRecord extends ThumbnailHolder {
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
Intent intent; // The original intent that started the task.
@@ -39,21 +48,32 @@
String stringName; // caching of toString() result.
int userId; // user for which this task was created
-
- TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
+
+ int numFullscreen; // Number of fullscreen activities.
+
+ /** List of all activities in the task arranged in history order */
+ final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>();
+
+ /** Current stack */
+ ActivityStack stack;
+
+ private boolean mApplicationTask = true;
+
+ TaskRecord(int _taskId, ActivityInfo info, Intent _intent, ActivityStack _stack) {
taskId = _taskId;
affinity = info.taskAffinity;
setIntent(_intent, info);
+ stack = _stack;
}
void touchActiveTime() {
lastActiveTime = android.os.SystemClock.elapsedRealtime();
}
-
+
long getInactiveDuration() {
return android.os.SystemClock.elapsedRealtime() - lastActiveTime;
}
-
+
void setIntent(Intent _intent, ActivityInfo info) {
stringName = null;
@@ -104,12 +124,291 @@
userId = UserHandle.getUserId(info.applicationInfo.uid);
}
}
-
+
+ ActivityRecord getTopActivity() {
+ for (int i = mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ return r;
+ }
+ return null;
+ }
+
+ /**
+ * Reorder the history stack so that the activity at the given index is
+ * brought to the front.
+ */
+ final void moveActivityToFrontLocked(ActivityRecord newTop) {
+ if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop
+ + " to stack at top", new RuntimeException("here").fillInStackTrace());
+
+ getTopActivity().frontOfTask = false;
+ mActivities.remove(newTop);
+ mActivities.add(newTop);
+ newTop.frontOfTask = true;
+ }
+
+ void addActivityAtBottom(ActivityRecord r) {
+ addActivityAtIndex(0, r);
+ }
+
+ void addActivityToTop(ActivityRecord r) {
+ addActivityAtIndex(mActivities.size(), r);
+ }
+
+ void addActivityAtIndex(int index, ActivityRecord r) {
+ // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
+ if (!mActivities.remove(r) && r.fullscreen) {
+ // Was not previously in list.
+ numFullscreen++;
+ }
+ // Only set this to be an application task if it has not already been set as home task.
+ if (mApplicationTask) {
+ mApplicationTask = r.isApplicationActivity();
+ }
+ mActivities.add(index, r);
+ }
+
+ /** @return true if this was the last activity in the task */
+ boolean removeActivity(ActivityRecord r) {
+ if (mActivities.remove(r) && r.fullscreen) {
+ // Was previously in list.
+ numFullscreen--;
+ }
+ return mActivities.size() == 0;
+ }
+
+ /**
+ * Completely remove all activities associated with an existing
+ * task starting at a specified index.
+ */
+ final void performClearTaskAtIndexLocked(int activityNdx) {
+ int numActivities = mActivities.size();
+ for ( ; activityNdx < numActivities; ++activityNdx) {
+ final ActivityRecord r = mActivities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) {
+ --activityNdx;
+ --numActivities;
+ }
+ }
+ }
+
+ /**
+ * Completely remove all activities associated with an existing task.
+ */
+ final void performClearTaskLocked() {
+ performClearTaskAtIndexLocked(0);
+ }
+
+ /**
+ * Perform clear operation as requested by
+ * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
+ * stack to the given task, then look for
+ * an instance of that activity in the stack and, if found, finish all
+ * activities on top of it and return the instance.
+ *
+ * @param newR Description of the new activity being started.
+ * @return Returns the old activity that should be continued to be used,
+ * or null if none was found.
+ */
+ final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
+ int numActivities = mActivities.size();
+ for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = mActivities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.realActivity.equals(newR.realActivity)) {
+ // Here it is! Now finish everything in front...
+ final ActivityRecord ret = r;
+
+ for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
+ r = mActivities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ ActivityOptions opts = r.takeOptionsLocked();
+ if (opts != null) {
+ ret.updateOptionsLocked(opts);
+ }
+ if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear",
+ false)) {
+ --activityNdx;
+ --numActivities;
+ }
+ }
+
+ // Finally, if this is a normal launch mode (that is, not
+ // expecting onNewIntent()), then we will finish the current
+ // instance of the activity so a new fresh one can be started.
+ if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
+ && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
+ if (!ret.finishing) {
+ stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null,
+ "clear", false);
+ return null;
+ }
+ }
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() {
+ TaskAccessInfo info = getTaskAccessInfoLocked(true);
+ final ActivityRecord resumedActivity = stack.mResumedActivity;
+ if (resumedActivity != null && resumedActivity.thumbHolder == this) {
+ info.mainThumbnail = stack.screenshotActivities(resumedActivity);
+ }
+ if (info.mainThumbnail == null) {
+ info.mainThumbnail = lastThumbnail;
+ }
+ return info;
+ }
+
+ 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);
+ }
+ // Return the information about the task, to figure out the top
+ // thumbnail to return.
+ TaskAccessInfo info = getTaskAccessInfoLocked(true);
+ if (info.numSubThumbbails <= 0) {
+ return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail;
+ }
+ return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail;
+ }
+
+ public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex,
+ boolean taskRequired) {
+ TaskAccessInfo info = getTaskAccessInfoLocked(false);
+ if (info.root == null) {
+ if (taskRequired) {
+ Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
+ }
+ return null;
+ }
+
+ if (subTaskIndex < 0) {
+ // Just remove the entire task.
+ performClearTaskAtIndexLocked(info.rootIndex);
+ return info.root;
+ }
+
+ if (subTaskIndex >= info.subtasks.size()) {
+ if (taskRequired) {
+ Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex);
+ }
+ return null;
+ }
+
+ // Remove all of this task's activities starting at the sub task.
+ TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex);
+ performClearTaskAtIndexLocked(subtask.index);
+ return subtask.activity;
+ }
+
+ boolean isApplicationTask() {
+ return mApplicationTask;
+ }
+
+ public TaskAccessInfo getTaskAccessInfoLocked(boolean inclThumbs) {
+ final TaskAccessInfo thumbs = new TaskAccessInfo();
+ // How many different sub-thumbnails?
+ final int NA = mActivities.size();
+ int j = 0;
+ ThumbnailHolder holder = null;
+ while (j < NA) {
+ ActivityRecord ar = mActivities.get(j);
+ if (!ar.finishing) {
+ thumbs.root = ar;
+ thumbs.rootIndex = j;
+ holder = ar.thumbHolder;
+ if (holder != null) {
+ thumbs.mainThumbnail = holder.lastThumbnail;
+ }
+ j++;
+ break;
+ }
+ j++;
+ }
+
+ if (j >= NA) {
+ return thumbs;
+ }
+
+ ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>();
+ thumbs.subtasks = subtasks;
+ while (j < NA) {
+ ActivityRecord ar = mActivities.get(j);
+ j++;
+ if (ar.finishing) {
+ continue;
+ }
+ if (ar.thumbHolder != holder && holder != null) {
+ thumbs.numSubThumbbails++;
+ holder = ar.thumbHolder;
+ TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask();
+ sub.holder = holder;
+ sub.activity = ar;
+ sub.index = j-1;
+ subtasks.add(sub);
+ }
+ }
+ if (thumbs.numSubThumbbails > 0) {
+ thumbs.retriever = new IThumbnailRetriever.Stub() {
+ @Override
+ public Bitmap getThumbnail(int index) {
+ if (index < 0 || index >= thumbs.subtasks.size()) {
+ return null;
+ }
+ TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index);
+ ActivityRecord resumedActivity = stack.mResumedActivity;
+ if (resumedActivity != null && resumedActivity.thumbHolder == sub.holder) {
+ return stack.screenshotActivities(resumedActivity);
+ }
+ return sub.holder.lastThumbnail;
+ }
+ };
+ }
+ return thumbs;
+ }
+
+ /**
+ * Find the activity in the history stack within the given task. Returns
+ * the index within the history at which it's found, or < 0 if not found.
+ */
+ final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
+ final ComponentName realActivity = r.realActivity;
+ for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord candidate = mActivities.get(activityNdx);
+ if (candidate.finishing) {
+ continue;
+ }
+ if (candidate.realActivity.equals(realActivity)) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+
void dump(PrintWriter pw, String prefix) {
if (numActivities != 0 || rootWasReset || userId != 0) {
pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
pw.print(" rootWasReset="); pw.print(rootWasReset);
- pw.print(" userId="); pw.println(userId);
+ pw.print(" userId="); pw.print(userId);
+ pw.print(" numFullscreen="); pw.println(numFullscreen);
}
if (affinity != null) {
pw.print(prefix); pw.print("affinity="); pw.println(affinity);
@@ -136,6 +435,7 @@
pw.print(prefix); pw.print("realActivity=");
pw.println(realActivity.flattenToShortString());
}
+ pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
if (!askedCompatMode) {
pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
}
@@ -146,30 +446,35 @@
pw.print((getInactiveDuration()/1000)); pw.println("s)");
}
+ @Override
public String toString() {
- if (stringName != null) {
- return stringName;
- }
StringBuilder sb = new StringBuilder(128);
+ if (stringName != null) {
+ sb.append(stringName);
+ sb.append(" U=");
+ sb.append(userId);
+ sb.append(" sz=");
+ sb.append(mActivities.size());
+ sb.append('}');
+ return sb.toString();
+ }
sb.append("TaskRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" #");
sb.append(taskId);
if (affinity != null) {
- sb.append(" A ");
+ sb.append(" A=");
sb.append(affinity);
} else if (intent != null) {
- sb.append(" I ");
+ sb.append(" I=");
sb.append(intent.getComponent().flattenToShortString());
} else if (affinityIntent != null) {
- sb.append(" aI ");
+ sb.append(" aI=");
sb.append(affinityIntent.getComponent().flattenToShortString());
} else {
sb.append(" ??");
}
- sb.append(" U ");
- sb.append(userId);
- sb.append('}');
- return stringName = sb.toString();
+ stringName = sb.toString();
+ return toString();
}
}
diff --git a/services/java/com/android/server/am/TransferPipe.java b/services/java/com/android/server/am/TransferPipe.java
deleted file mode 100644
index c3f4f93..0000000
--- a/services/java/com/android/server/am/TransferPipe.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.Slog;
-
-/**
- * Helper for transferring data through a pipe from a client app.
- */
-class TransferPipe implements Runnable {
- static final String TAG = "TransferPipe";
- static final boolean DEBUG = false;
-
- static final long DEFAULT_TIMEOUT = 5000; // 5 seconds
-
- final Thread mThread;;
- final ParcelFileDescriptor[] mFds;
-
- FileDescriptor mOutFd;
- long mEndTime;
- String mFailure;
- boolean mComplete;
-
- String mBufferPrefix;
-
- interface Caller {
- void go(IInterface iface, FileDescriptor fd, String prefix,
- String[] args) throws RemoteException;
- }
-
- TransferPipe() throws IOException {
- mThread = new Thread(this, "TransferPipe");
- mFds = ParcelFileDescriptor.createPipe();
- }
-
- ParcelFileDescriptor getReadFd() {
- return mFds[0];
- }
-
- ParcelFileDescriptor getWriteFd() {
- return mFds[1];
- }
-
- void setBufferPrefix(String prefix) {
- mBufferPrefix = prefix;
- }
-
- static void go(Caller caller, IInterface iface, FileDescriptor out,
- String prefix, String[] args) throws IOException, RemoteException {
- go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT);
- }
-
- static void go(Caller caller, IInterface iface, FileDescriptor out,
- String prefix, String[] args, long timeout) throws IOException, RemoteException {
- if ((iface.asBinder()) instanceof Binder) {
- // This is a local object... just call it directly.
- try {
- caller.go(iface, out, prefix, args);
- } catch (RemoteException e) {
- }
- return;
- }
-
- TransferPipe tp = new TransferPipe();
- try {
- caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args);
- tp.go(out, timeout);
- } finally {
- tp.kill();
- }
- }
-
- static void goDump(IBinder binder, FileDescriptor out,
- String[] args) throws IOException, RemoteException {
- goDump(binder, out, args, DEFAULT_TIMEOUT);
- }
-
- static void goDump(IBinder binder, FileDescriptor out,
- String[] args, long timeout) throws IOException, RemoteException {
- if (binder instanceof Binder) {
- // This is a local object... just call it directly.
- try {
- binder.dump(out, args);
- } catch (RemoteException e) {
- }
- return;
- }
-
- TransferPipe tp = new TransferPipe();
- try {
- binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args);
- tp.go(out, timeout);
- } finally {
- tp.kill();
- }
- }
-
- void go(FileDescriptor out) throws IOException {
- go(out, DEFAULT_TIMEOUT);
- }
-
- void go(FileDescriptor out, long timeout) throws IOException {
- try {
- synchronized (this) {
- mOutFd = out;
- mEndTime = SystemClock.uptimeMillis() + timeout;
-
- if (DEBUG) Slog.i(TAG, "read=" + getReadFd() + " write=" + getWriteFd()
- + " out=" + out);
-
- // Close the write fd, so we know when the other side is done.
- closeFd(1);
-
- mThread.start();
-
- while (mFailure == null && !mComplete) {
- long waitTime = mEndTime - SystemClock.uptimeMillis();
- if (waitTime <= 0) {
- if (DEBUG) Slog.i(TAG, "TIMEOUT!");
- mThread.interrupt();
- throw new IOException("Timeout");
- }
-
- try {
- wait(waitTime);
- } catch (InterruptedException e) {
- }
- }
-
- if (DEBUG) Slog.i(TAG, "Finished: " + mFailure);
- if (mFailure != null) {
- throw new IOException(mFailure);
- }
- }
- } finally {
- kill();
- }
- }
-
- void closeFd(int num) {
- if (mFds[num] != null) {
- if (DEBUG) Slog.i(TAG, "Closing: " + mFds[num]);
- try {
- mFds[num].close();
- } catch (IOException e) {
- }
- mFds[num] = null;
- }
- }
-
- void kill() {
- closeFd(0);
- closeFd(1);
- }
-
- @Override
- public void run() {
- final byte[] buffer = new byte[1024];
- final FileInputStream fis = new FileInputStream(getReadFd().getFileDescriptor());
- final FileOutputStream fos = new FileOutputStream(mOutFd);
-
- if (DEBUG) Slog.i(TAG, "Ready to read pipe...");
- byte[] bufferPrefix = null;
- boolean needPrefix = true;
- if (mBufferPrefix != null) {
- bufferPrefix = mBufferPrefix.getBytes();
- }
-
- int size;
- try {
- while ((size=fis.read(buffer)) > 0) {
- if (DEBUG) Slog.i(TAG, "Got " + size + " bytes");
- if (bufferPrefix == null) {
- fos.write(buffer, 0, size);
- } else {
- int start = 0;
- for (int i=0; i<size; i++) {
- if (buffer[i] != '\n') {
- if (i > start) {
- fos.write(buffer, start, i-start);
- }
- start = i;
- if (needPrefix) {
- fos.write(bufferPrefix);
- needPrefix = false;
- }
- do {
- i++;
- } while (i<size && buffer[i] != '\n');
- if (i < size) {
- needPrefix = true;
- }
- }
- }
- if (size > start) {
- fos.write(buffer, start, size-start);
- }
- }
- }
- if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size);
- if (mThread.isInterrupted()) {
- if (DEBUG) Slog.i(TAG, "Interrupted!");
- }
- } catch (IOException e) {
- synchronized (this) {
- mFailure = e.toString();
- notifyAll();
- return;
- }
- }
-
- synchronized (this) {
- mComplete = true;
- notifyAll();
- }
- }
-}
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index c5b1c7b..5e2ad009 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -18,6 +18,11 @@
import android.content.Intent;
import android.net.Uri;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.google.android.collect.Sets;
import java.io.PrintWriter;
import java.util.HashSet;
@@ -30,44 +35,172 @@
* Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
* src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
*/
-class UriPermission {
- final int uid;
+final class UriPermission {
+ private static final String TAG = "UriPermission";
+
+ final int userHandle;
+ final String sourcePkg;
+ final String targetPkg;
+
+ /** Cached UID of {@link #targetPkg}; should not be persisted */
+ final int targetUid;
+
final Uri uri;
+
+ /**
+ * Allowed modes. All permission enforcement should use this field. Must
+ * always be a superset of {@link #globalModeFlags},
+ * {@link #persistedModeFlags}, {@link #mReadOwners}, and
+ * {@link #mWriteOwners}. Mutations should only be performed by the owning
+ * class.
+ */
int modeFlags = 0;
+
+ /**
+ * Allowed modes without explicit owner. Must always be a superset of
+ * {@link #persistedModeFlags}. Mutations should only be performed by the
+ * owning class.
+ */
int globalModeFlags = 0;
- final HashSet<UriPermissionOwner> readOwners = new HashSet<UriPermissionOwner>();
- final HashSet<UriPermissionOwner> writeOwners = new HashSet<UriPermissionOwner>();
-
- String stringName;
-
- UriPermission(int _uid, Uri _uri) {
- uid = _uid;
- uri = _uri;
+
+ /**
+ * Allowed modes that should be persisted across device boots. These modes
+ * have no explicit owners. Mutations should only be performed by the owning
+ * class.
+ */
+ int persistedModeFlags = 0;
+
+ private HashSet<UriPermissionOwner> mReadOwners;
+ private HashSet<UriPermissionOwner> mWriteOwners;
+
+ private String stringName;
+
+ UriPermission(String sourcePkg, String targetPkg, int targetUid, Uri uri) {
+ this.userHandle = UserHandle.getUserId(targetUid);
+ this.sourcePkg = sourcePkg;
+ this.targetPkg = targetPkg;
+ this.targetUid = targetUid;
+ this.uri = uri;
+ }
+
+ /**
+ * @return If mode changes should trigger persisting.
+ */
+ boolean grantModes(int modeFlagsToGrant, boolean persist, UriPermissionOwner owner) {
+ boolean persistChanged = false;
+
+ modeFlags |= modeFlagsToGrant;
+
+ if (persist) {
+ final int before = persistedModeFlags;
+ persistedModeFlags |= modeFlagsToGrant;
+ persistChanged = persistedModeFlags != before;
+
+ // Treat persisted grants as global (ownerless)
+ owner = null;
+ }
+
+ if (owner == null) {
+ globalModeFlags |= modeFlags;
+ } else {
+ if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ addReadOwner(owner);
+ owner.addReadPermission(this);
+ }
+ if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ addWriteOwner(owner);
+ owner.addWritePermission(this);
+ }
+ }
+
+ return persistChanged;
}
- void clearModes(int modeFlagsToClear) {
- if ((modeFlagsToClear&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ /**
+ * @return If mode changes should trigger persisting.
+ */
+ boolean clearModes(int modeFlagsToClear, boolean persist) {
+ final int before = persistedModeFlags;
+
+ if ((modeFlagsToClear & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ if (persist) {
+ persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ }
globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- if (readOwners.size() > 0) {
- for (UriPermissionOwner r : readOwners) {
+ if (mReadOwners != null) {
+ for (UriPermissionOwner r : mReadOwners) {
r.removeReadPermission(this);
}
- readOwners.clear();
+ mReadOwners = null;
}
}
- if ((modeFlagsToClear&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if ((modeFlagsToClear & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (persist) {
+ persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ }
globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- if (writeOwners.size() > 0) {
- for (UriPermissionOwner r : writeOwners) {
+ if (mWriteOwners != null) {
+ for (UriPermissionOwner r : mWriteOwners) {
r.removeWritePermission(this);
}
- writeOwners.clear();
+ mWriteOwners = null;
+ }
+ }
+
+ // Mode flags always bubble up
+ globalModeFlags |= persistedModeFlags;
+ modeFlags |= globalModeFlags;
+
+ return persistedModeFlags != before;
+ }
+
+ private void addReadOwner(UriPermissionOwner owner) {
+ if (mReadOwners == null) {
+ mReadOwners = Sets.newHashSet();
+ }
+ mReadOwners.add(owner);
+ }
+
+ /**
+ * Remove given read owner, updating {@Link #modeFlags} as needed.
+ */
+ void removeReadOwner(UriPermissionOwner owner) {
+ if (!mReadOwners.remove(owner)) {
+ Log.wtf(TAG, "Unknown read owner " + owner + " in " + this);
+ }
+ if (mReadOwners.size() == 0) {
+ mReadOwners = null;
+ if ((globalModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+ modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
}
}
}
-
+
+ private void addWriteOwner(UriPermissionOwner owner) {
+ if (mWriteOwners == null) {
+ mWriteOwners = Sets.newHashSet();
+ }
+ mWriteOwners.add(owner);
+ }
+
+ /**
+ * Remove given write owner, updating {@Link #modeFlags} as needed.
+ */
+ void removeWriteOwner(UriPermissionOwner owner) {
+ if (!mWriteOwners.remove(owner)) {
+ Log.wtf(TAG, "Unknown write owner " + owner + " in " + this);
+ }
+ if (mWriteOwners.size() == 0) {
+ mWriteOwners = null;
+ if ((globalModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+ modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ }
+ }
+ }
+
+ @Override
public String toString() {
if (stringName != null) {
return stringName;
@@ -82,22 +215,55 @@
}
void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("modeFlags=0x");
- pw.print(Integer.toHexString(modeFlags));
- pw.print(" uid="); pw.print(uid);
- pw.print(" globalModeFlags=0x");
- pw.println(Integer.toHexString(globalModeFlags));
- if (readOwners.size() != 0) {
- pw.print(prefix); pw.println("readOwners:");
- for (UriPermissionOwner owner : readOwners) {
- pw.print(prefix); pw.print(" * "); pw.println(owner);
+ pw.print(prefix);
+ pw.print("userHandle=" + userHandle);
+ pw.print(" sourcePkg=" + sourcePkg);
+ pw.println(" targetPkg=" + targetPkg);
+
+ pw.print(prefix);
+ pw.print("modeFlags=0x" + Integer.toHexString(modeFlags));
+ pw.print(" globalModeFlags=0x" + Integer.toHexString(globalModeFlags));
+ pw.println(" persistedModeFlags=0x" + Integer.toHexString(persistedModeFlags));
+
+ if (mReadOwners != null) {
+ pw.print(prefix);
+ pw.println("readOwners:");
+ for (UriPermissionOwner owner : mReadOwners) {
+ pw.print(prefix);
+ pw.println(" * " + owner);
}
}
- if (writeOwners.size() != 0) {
- pw.print(prefix); pw.println("writeOwners:");
- for (UriPermissionOwner owner : writeOwners) {
- pw.print(prefix); pw.print(" * "); pw.println(owner);
+ if (mWriteOwners != null) {
+ pw.print(prefix);
+ pw.println("writeOwners:");
+ for (UriPermissionOwner owner : mReadOwners) {
+ pw.print(prefix);
+ pw.println(" * " + owner);
}
}
}
+
+ /**
+ * Snapshot of {@link UriPermission} with frozen
+ * {@link UriPermission#persistedModeFlags} state.
+ */
+ public static class Snapshot {
+ final int userHandle;
+ final String sourcePkg;
+ final String targetPkg;
+ final Uri uri;
+ final int persistedModeFlags;
+
+ private Snapshot(UriPermission perm) {
+ this.userHandle = perm.userHandle;
+ this.sourcePkg = perm.sourcePkg;
+ this.targetPkg = perm.targetPkg;
+ this.uri = perm.uri;
+ this.persistedModeFlags = perm.persistedModeFlags;
+ }
+ }
+
+ public Snapshot snapshot() {
+ return new Snapshot(this);
+ }
}
diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java
index 68a2e0f..7bbd3bc 100644
--- a/services/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -24,7 +24,7 @@
import java.util.HashSet;
import java.util.Iterator;
-class UriPermissionOwner {
+final class UriPermissionOwner {
final ActivityManagerService service;
final Object owner;
@@ -67,24 +67,16 @@
if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
&& readUriPermissions != null) {
for (UriPermission perm : readUriPermissions) {
- perm.readOwners.remove(this);
- if (perm.readOwners.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- service.removeUriPermissionIfNeededLocked(perm);
- }
+ perm.removeReadOwner(this);
+ service.removeUriPermissionIfNeededLocked(perm);
}
readUriPermissions = null;
}
if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
&& writeUriPermissions != null) {
for (UriPermission perm : writeUriPermissions) {
- perm.writeOwners.remove(this);
- if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- service.removeUriPermissionIfNeededLocked(perm);
- }
+ perm.removeWriteOwner(this);
+ service.removeUriPermissionIfNeededLocked(perm);
}
writeUriPermissions = null;
}
@@ -97,12 +89,8 @@
while (it.hasNext()) {
UriPermission perm = it.next();
if (uri.equals(perm.uri)) {
- perm.readOwners.remove(this);
- if (perm.readOwners.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- service.removeUriPermissionIfNeededLocked(perm);
- }
+ perm.removeReadOwner(this);
+ service.removeUriPermissionIfNeededLocked(perm);
it.remove();
}
}
@@ -116,12 +104,8 @@
while (it.hasNext()) {
UriPermission perm = it.next();
if (uri.equals(perm.uri)) {
- perm.writeOwners.remove(this);
- if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- service.removeUriPermissionIfNeededLocked(perm);
- }
+ perm.removeWriteOwner(this);
+ service.removeUriPermissionIfNeededLocked(perm);
it.remove();
}
}
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 6dae4aa..e96d8b1 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -16,8 +16,10 @@
package com.android.server.am;
+import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -25,8 +27,10 @@
import android.os.FileUtils;
import android.os.Parcel;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
@@ -73,7 +77,7 @@
private static final String TAG = "UsageStats";
// Current on-disk Parcel version
- private static final int VERSION = 1007;
+ private static final int VERSION = 1008;
private static final int CHECKIN_VERSION = 4;
@@ -93,10 +97,10 @@
static IUsageStats sService;
private Context mContext;
// structure used to maintain statistics since the last checkin.
- final private Map<String, PkgUsageStatsExtended> mStats;
+ final private ArrayMap<String, PkgUsageStatsExtended> mStats;
// Maintains the last time any component was resumed, for all time.
- final private Map<String, Map<String, Long>> mLastResumeTimes;
+ final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes;
// To remove last-resume time stats when a pacakge is removed.
private PackageMonitor mPackageMonitor;
@@ -162,8 +166,10 @@
}
private class PkgUsageStatsExtended {
- final HashMap<String, TimeStats> mLaunchTimes
- = new HashMap<String, TimeStats>();
+ final ArrayMap<String, TimeStats> mLaunchTimes
+ = new ArrayMap<String, TimeStats>();
+ final ArrayMap<String, TimeStats> mFullyDrawnTimes
+ = new ArrayMap<String, TimeStats>();
int mLaunchCount;
long mUsageTime;
long mPausedTime;
@@ -180,14 +186,25 @@
if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount
+ ", Usage time:" + mUsageTime);
- final int numTimeStats = in.readInt();
- if (localLOGV) Slog.v(TAG, "Reading comps: " + numTimeStats);
- for (int i=0; i<numTimeStats; i++) {
+ final int numLaunchTimeStats = in.readInt();
+ if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats);
+ mLaunchTimes.ensureCapacity(numLaunchTimeStats);
+ for (int i=0; i<numLaunchTimeStats; i++) {
String comp = in.readString();
if (localLOGV) Slog.v(TAG, "Component: " + comp);
TimeStats times = new TimeStats(in);
mLaunchTimes.put(comp, times);
}
+
+ final int numFullyDrawnTimeStats = in.readInt();
+ if (localLOGV) Slog.v(TAG, "Reading fully drawn times: " + numFullyDrawnTimeStats);
+ mFullyDrawnTimes.ensureCapacity(numFullyDrawnTimeStats);
+ for (int i=0; i<numFullyDrawnTimeStats; i++) {
+ String comp = in.readString();
+ if (localLOGV) Slog.v(TAG, "Component: " + comp);
+ TimeStats times = new TimeStats(in);
+ mFullyDrawnTimes.put(comp, times);
+ }
}
void updateResume(String comp, boolean launched) {
@@ -219,31 +236,44 @@
}
times.add(millis);
}
-
+
+ void addFullyDrawnTime(String comp, int millis) {
+ TimeStats times = mFullyDrawnTimes.get(comp);
+ if (times == null) {
+ times = new TimeStats();
+ mFullyDrawnTimes.put(comp, times);
+ }
+ times.add(millis);
+ }
+
void writeToParcel(Parcel out) {
out.writeInt(mLaunchCount);
out.writeLong(mUsageTime);
- final int numTimeStats = mLaunchTimes.size();
- out.writeInt(numTimeStats);
- if (numTimeStats > 0) {
- for (Map.Entry<String, TimeStats> ent : mLaunchTimes.entrySet()) {
- out.writeString(ent.getKey());
- TimeStats times = ent.getValue();
- times.writeToParcel(out);
- }
+ final int numLaunchTimeStats = mLaunchTimes.size();
+ out.writeInt(numLaunchTimeStats);
+ for (int i=0; i<numLaunchTimeStats; i++) {
+ out.writeString(mLaunchTimes.keyAt(i));
+ mLaunchTimes.valueAt(i).writeToParcel(out);
+ }
+ final int numFullyDrawnTimeStats = mFullyDrawnTimes.size();
+ out.writeInt(numFullyDrawnTimeStats);
+ for (int i=0; i<numFullyDrawnTimeStats; i++) {
+ out.writeString(mFullyDrawnTimes.keyAt(i));
+ mFullyDrawnTimes.valueAt(i).writeToParcel(out);
}
}
void clear() {
mLaunchTimes.clear();
+ mFullyDrawnTimes.clear();
mLaunchCount = 0;
mUsageTime = 0;
}
}
UsageStatsService(String dir) {
- mStats = new HashMap<String, PkgUsageStatsExtended>();
- mLastResumeTimes = new HashMap<String, Map<String, Long>>();
+ mStats = new ArrayMap<String, PkgUsageStatsExtended>();
+ mLastResumeTimes = new ArrayMap<String, ArrayMap<String, Long>>();
mStatsLock = new Object();
mFileLock = new Object();
mDir = new File(dir);
@@ -386,9 +416,9 @@
try {
long lastResumeTime = Long.parseLong(lastResumeTimeStr);
synchronized (mStatsLock) {
- Map<String, Long> lrt = mLastResumeTimes.get(pkg);
+ ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg);
if (lrt == null) {
- lrt = new HashMap<String, Long>();
+ lrt = new ArrayMap<String, Long>();
mLastResumeTimes.put(pkg, lrt);
}
lrt.put(comp, lastResumeTime);
@@ -591,14 +621,15 @@
/** Filter out stats for any packages which aren't present anymore. */
private void filterHistoryStats() {
synchronized (mStatsLock) {
- // Copy and clear the last resume times map, then copy back stats
- // for all installed packages.
- Map<String, Map<String, Long>> tmpLastResumeTimes =
- new HashMap<String, Map<String, Long>>(mLastResumeTimes);
- mLastResumeTimes.clear();
- for (PackageInfo info : mContext.getPackageManager().getInstalledPackages(0)) {
- if (tmpLastResumeTimes.containsKey(info.packageName)) {
- mLastResumeTimes.put(info.packageName, tmpLastResumeTimes.get(info.packageName));
+ IPackageManager pm = AppGlobals.getPackageManager();
+ for (int i=0; i<mLastResumeTimes.size(); i++) {
+ String pkg = mLastResumeTimes.keyAt(i);
+ try {
+ if (pm.getPackageUid(pkg, 0) < 0) {
+ mLastResumeTimes.removeAt(i);
+ i--;
+ }
+ } catch (RemoteException e) {
}
}
}
@@ -614,13 +645,14 @@
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, "usage-history");
synchronized (mStatsLock) {
- for (Map.Entry<String, Map<String, Long>> pkgEntry : mLastResumeTimes.entrySet()) {
+ for (int i=0; i<mLastResumeTimes.size(); i++) {
out.startTag(null, "pkg");
- out.attribute(null, "name", pkgEntry.getKey());
- for (Map.Entry<String, Long> compEntry : pkgEntry.getValue().entrySet()) {
+ out.attribute(null, "name", mLastResumeTimes.keyAt(i));
+ ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i);
+ for (int j=0; j<comp.size(); j++) {
out.startTag(null, "comp");
- out.attribute(null, "name", compEntry.getKey());
- out.attribute(null, "lrt", compEntry.getValue().toString());
+ out.attribute(null, "name", comp.keyAt(j));
+ out.attribute(null, "lrt", comp.valueAt(j).toString());
out.endTag(null, "comp");
}
out.endTag(null, "pkg");
@@ -718,9 +750,9 @@
pus.addLaunchCount(mLastResumedComp);
}
- Map<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
+ ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
if (componentResumeTimes == null) {
- componentResumeTimes = new HashMap<String, Long>();
+ componentResumeTimes = new ArrayMap<String, Long>();
mLastResumeTimes.put(pkgName, componentResumeTimes);
}
componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
@@ -777,6 +809,25 @@
}
}
+ public void noteFullyDrawnTime(ComponentName componentName, int millis) {
+ enforceCallingPermission();
+ String pkgName;
+ if ((componentName == null) ||
+ ((pkgName = componentName.getPackageName()) == null)) {
+ return;
+ }
+
+ // Persist current data to file if needed.
+ writeStatsToFile(false, false);
+
+ synchronized (mStatsLock) {
+ PkgUsageStatsExtended pus = mStats.get(pkgName);
+ if (pus != null) {
+ pus.addFullyDrawnTime(componentName.getClassName(), millis);
+ }
+ }
+ }
+
public void enforceCallingPermission() {
if (Binder.getCallingPid() == Process.myPid()) {
return;
@@ -814,9 +865,8 @@
return null;
}
PkgUsageStats retArr[] = new PkgUsageStats[size];
- int i = 0;
- for (Map.Entry<String, Map<String, Long>> entry : mLastResumeTimes.entrySet()) {
- String pkg = entry.getKey();
+ for (int i=0; i<size; i++) {
+ String pkg = mLastResumeTimes.keyAt(i);
long usageTime = 0;
int launchCount = 0;
@@ -825,8 +875,8 @@
usageTime = pus.mUsageTime;
launchCount = pus.mLaunchCount;
}
- retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime, entry.getValue());
- i++;
+ retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime,
+ mLastResumeTimes.valueAt(i));
}
return retArr;
}
@@ -866,6 +916,11 @@
}
File dFile = new File(mDir, file);
String dateStr = file.substring(FILE_PREFIX.length());
+ if (dateStr.length() > 0 && (dateStr.charAt(0) <= '0' || dateStr.charAt(0) >= '9')) {
+ // If the remainder does not start with a number, it is not a date,
+ // so we should ignore it for purposes here.
+ continue;
+ }
try {
Parcel in = getParcelForFile(dFile);
collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCompactOutput,
@@ -925,29 +980,33 @@
sb.append(',');
sb.append(pus.mUsageTime);
sb.append('\n');
- final int NC = pus.mLaunchTimes.size();
- if (NC > 0) {
- for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) {
- sb.append("A:");
- String activity = ent.getKey();
- if (activity.startsWith(pkgName)) {
- sb.append('*');
- sb.append(activity.substring(
- pkgName.length(), activity.length()));
- } else {
- sb.append(activity);
- }
- TimeStats times = ent.getValue();
- sb.append(',');
- sb.append(times.count);
- for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
- sb.append(",");
- sb.append(times.times[i]);
- }
- sb.append('\n');
+ final int NLT = pus.mLaunchTimes.size();
+ for (int i=0; i<NLT; i++) {
+ sb.append("A:");
+ String activity = pus.mLaunchTimes.keyAt(i);
+ sb.append(activity);
+ TimeStats times = pus.mLaunchTimes.valueAt(i);
+ sb.append(',');
+ sb.append(times.count);
+ for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
+ sb.append(",");
+ sb.append(times.times[j]);
}
+ sb.append('\n');
}
-
+ final int NFDT = pus.mFullyDrawnTimes.size();
+ for (int i=0; i<NFDT; i++) {
+ sb.append("A:");
+ String activity = pus.mFullyDrawnTimes.keyAt(i);
+ sb.append(activity);
+ TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
+ for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
+ sb.append(",");
+ sb.append(times.times[j]);
+ }
+ sb.append('\n');
+ }
+
} else {
sb.append(" ");
sb.append(pkgName);
@@ -957,36 +1016,68 @@
sb.append(pus.mUsageTime);
sb.append(" ms");
sb.append('\n');
- final int NC = pus.mLaunchTimes.size();
- if (NC > 0) {
- for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) {
- sb.append(" ");
- sb.append(ent.getKey());
- TimeStats times = ent.getValue();
- sb.append(": ");
- sb.append(times.count);
- sb.append(" starts");
- int lastBin = 0;
- for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
- if (times.times[i] != 0) {
- sb.append(", ");
- sb.append(lastBin);
- sb.append('-');
- sb.append(LAUNCH_TIME_BINS[i]);
- sb.append("ms=");
- sb.append(times.times[i]);
- }
- lastBin = LAUNCH_TIME_BINS[i];
- }
- if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
+ final int NLT = pus.mLaunchTimes.size();
+ for (int i=0; i<NLT; i++) {
+ sb.append(" ");
+ sb.append(pus.mLaunchTimes.keyAt(i));
+ TimeStats times = pus.mLaunchTimes.valueAt(i);
+ sb.append(": ");
+ sb.append(times.count);
+ sb.append(" starts");
+ int lastBin = 0;
+ for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
+ if (times.times[j] != 0) {
sb.append(", ");
- sb.append(">=");
sb.append(lastBin);
+ sb.append('-');
+ sb.append(LAUNCH_TIME_BINS[j]);
sb.append("ms=");
- sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
+ sb.append(times.times[j]);
}
- sb.append('\n');
+ lastBin = LAUNCH_TIME_BINS[j];
}
+ if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
+ sb.append(", ");
+ sb.append(">=");
+ sb.append(lastBin);
+ sb.append("ms=");
+ sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
+ }
+ sb.append('\n');
+ }
+ final int NFDT = pus.mFullyDrawnTimes.size();
+ for (int i=0; i<NFDT; i++) {
+ sb.append(" ");
+ sb.append(pus.mFullyDrawnTimes.keyAt(i));
+ TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
+ sb.append(": fully drawn ");
+ boolean needComma = false;
+ int lastBin = 0;
+ for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
+ if (times.times[j] != 0) {
+ if (needComma) {
+ sb.append(", ");
+ } else {
+ needComma = true;
+ }
+ sb.append(lastBin);
+ sb.append('-');
+ sb.append(LAUNCH_TIME_BINS[j]);
+ sb.append("ms=");
+ sb.append(times.times[j]);
+ }
+ lastBin = LAUNCH_TIME_BINS[j];
+ }
+ if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
+ if (needComma) {
+ sb.append(", ");
+ }
+ sb.append(">=");
+ sb.append(lastBin);
+ sb.append("ms=");
+ sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
+ }
+ sb.append('\n');
}
}
diff --git a/services/java/com/android/server/am/UserStartedState.java b/services/java/com/android/server/am/UserStartedState.java
index 0e71f81..d3e73d5 100644
--- a/services/java/com/android/server/am/UserStartedState.java
+++ b/services/java/com/android/server/am/UserStartedState.java
@@ -22,7 +22,7 @@
import android.app.IStopUserCallback;
import android.os.UserHandle;
-public class UserStartedState {
+public final class UserStartedState {
// User is first coming up.
public final static int STATE_BOOTING = 0;
// User is in the normal running state.
diff --git a/services/java/com/android/server/connectivity/DataConnectionStats.java b/services/java/com/android/server/connectivity/DataConnectionStats.java
new file mode 100644
index 0000000..227ab23
--- /dev/null
+++ b/services/java/com/android/server/connectivity/DataConnectionStats.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.os.RemoteException;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.server.am.BatteryStatsService;
+
+public class DataConnectionStats extends BroadcastReceiver {
+ private static final String TAG = "DataConnectionStats";
+ private static final boolean DEBUG = false;
+
+ private final Context mContext;
+ private final IBatteryStats mBatteryStats;
+
+ private IccCardConstants.State mSimState = IccCardConstants.State.READY;
+ private SignalStrength mSignalStrength;
+ private ServiceState mServiceState;
+ private int mDataState = TelephonyManager.DATA_DISCONNECTED;
+
+ public DataConnectionStats(Context context) {
+ mContext = context;
+ mBatteryStats = BatteryStatsService.getService();
+ }
+
+ public void startMonitoring() {
+ TelephonyManager phone =
+ (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ phone.listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_SERVICE_STATE
+ | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+ | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
+ mContext.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+ updateSimState(intent);
+ notePhoneDataConnectionState();
+ } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
+ action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
+ notePhoneDataConnectionState();
+ }
+ }
+
+ private void notePhoneDataConnectionState() {
+ if (mServiceState == null) {
+ return;
+ }
+ boolean simReadyOrUnknown = mSimState == IccCardConstants.State.READY
+ || mSimState == IccCardConstants.State.UNKNOWN;
+ boolean visible = (simReadyOrUnknown || isCdma()) // we only check the sim state for GSM
+ && hasService()
+ && mDataState == TelephonyManager.DATA_CONNECTED;
+ int networkType = mServiceState.getDataNetworkType();
+ if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
+ networkType, visible ? "" : "not "));
+ try {
+ mBatteryStats.notePhoneDataConnectionState(networkType, visible);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error noting data connection state", e);
+ }
+ }
+
+ private final void updateSimState(Intent intent) {
+ String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+ if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+ mSimState = IccCardConstants.State.ABSENT;
+ } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+ mSimState = IccCardConstants.State.READY;
+ } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+ final String lockedReason =
+ intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
+ if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+ mSimState = IccCardConstants.State.PIN_REQUIRED;
+ } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+ mSimState = IccCardConstants.State.PUK_REQUIRED;
+ } else {
+ mSimState = IccCardConstants.State.NETWORK_LOCKED;
+ }
+ } else {
+ mSimState = IccCardConstants.State.UNKNOWN;
+ }
+ }
+
+ private boolean isCdma() {
+ return mSignalStrength != null && !mSignalStrength.isGsm();
+ }
+
+ private boolean hasService() {
+ return mServiceState != null
+ && mServiceState.getState() != ServiceState.STATE_OUT_OF_SERVICE
+ && mServiceState.getState() != ServiceState.STATE_POWER_OFF;
+ }
+
+ private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ mSignalStrength = signalStrength;
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState state) {
+ mServiceState = state;
+ notePhoneDataConnectionState();
+ }
+
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ mDataState = state;
+ notePhoneDataConnectionState();
+ }
+
+ @Override
+ public void onDataActivity(int direction) {
+ notePhoneDataConnectionState();
+ }
+ };
+}
diff --git a/services/java/com/android/server/connectivity/Nat464Xlat.java b/services/java/com/android/server/connectivity/Nat464Xlat.java
index 59403c5..a15d678 100644
--- a/services/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/java/com/android/server/connectivity/Nat464Xlat.java
@@ -147,17 +147,24 @@
" added, mIsRunning = " + mIsRunning + " -> true");
mIsRunning = true;
- // Get the network configuration of the clat interface, store it
- // in our link properties, and stack it on top of the interface
- // it's running on.
+ // Create the LinkProperties for the clat interface by fetching the
+ // IPv4 address for the interface and adding an IPv4 default route,
+ // then stack the LinkProperties on top of the link it's running on.
+ // Although the clat interface is a point-to-point tunnel, we don't
+ // point the route directly at the interface because some apps don't
+ // understand routes without gateways (see, e.g., http://b/9597256
+ // http://b/9597516). Instead, set the next hop of the route to the
+ // clat IPv4 address itself (for those apps, it doesn't matter what
+ // the IP of the gateway is, only that there is one).
try {
InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
+ LinkAddress clatAddress = config.getLinkAddress();
mLP.clear();
mLP.setInterfaceName(iface);
- RouteInfo ipv4Default = new RouteInfo(new LinkAddress(Inet4Address.ANY, 0), null,
- iface);
+ RouteInfo ipv4Default = new RouteInfo(new LinkAddress(Inet4Address.ANY, 0),
+ clatAddress.getAddress(), iface);
mLP.addRoute(ipv4Default);
- mLP.addLinkAddress(config.getLinkAddress());
+ mLP.addLinkAddress(clatAddress);
mTracker.addStackedLink(mLP);
Slog.i(TAG, "Adding stacked link. tracker LP: " +
mTracker.getLinkProperties());
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 32f39b7..87263b30 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -37,13 +37,10 @@
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.os.Binder;
-import android.os.HandlerThread;
-import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -53,6 +50,7 @@
import com.android.internal.util.IState;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.server.IoThread;
import com.google.android.collect.Lists;
import java.io.FileDescriptor;
@@ -100,7 +98,6 @@
private final INetworkStatsService mStatsService;
private final IConnectivityManager mConnService;
private Looper mLooper;
- private HandlerThread mThread;
private HashMap<String, TetherInterfaceSM> mIfaces; // all tethered/tetherable ifaces
@@ -147,9 +144,7 @@
mIfaces = new HashMap<String, TetherInterfaceSM>();
// make our own thread so we don't anr the system
- mThread = new HandlerThread("Tethering");
- mThread.start();
- mLooper = mThread.getLooper();
+ mLooper = IoThread.get().getLooper();
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
mTetherMasterSM.start();
diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java
index f82cf01..4a5c0d5 100644
--- a/services/java/com/android/server/content/ContentService.java
+++ b/services/java/com/android/server/content/ContentService.java
@@ -36,6 +36,7 @@
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
@@ -61,6 +62,10 @@
private final Object mSyncManagerLock = new Object();
private SyncManager getSyncManager() {
+ if (SystemProperties.getBoolean("config.disable_network", false)) {
+ return null;
+ }
+
synchronized(mSyncManagerLock) {
try {
// Try to create the SyncManager, return null if it fails (e.g. the disk is full).
diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java
index ff1281e..9e2e841 100644
--- a/services/java/com/android/server/content/SyncManager.java
+++ b/services/java/com/android/server/content/SyncManager.java
@@ -53,12 +53,10 @@
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
-import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -74,15 +72,16 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.accounts.AccountManagerService;
+import com.android.server.content.SyncStorageEngine.AuthorityInfo;
import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
import java.io.FileDescriptor;
-import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -114,7 +113,7 @@
private static final long MAX_TIME_PER_SYNC;
static {
- final boolean isLargeRAM = ActivityManager.isLargeRAM();
+ final boolean isLargeRAM = !ActivityManager.isLowRamDeviceStatic();
int defaultMaxInitSyncs = isLargeRAM ? 5 : 2;
int defaultMaxRegularSyncs = isLargeRAM ? 2 : 1;
MAX_SIMULTANEOUS_INITIALIZATION_SYNCS =
@@ -381,10 +380,7 @@
mSyncAdapters = new SyncAdaptersCache(mContext);
mSyncQueue = new SyncQueue(mContext.getPackageManager(), mSyncStorageEngine, mSyncAdapters);
- HandlerThread syncThread = new HandlerThread("SyncHandlerThread",
- Process.THREAD_PRIORITY_BACKGROUND);
- syncThread.start();
- mSyncHandler = new SyncHandler(syncThread.getLooper());
+ mSyncHandler = new SyncHandler(BackgroundThread.get().getLooper());
mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
@Override
@@ -1253,10 +1249,11 @@
continue;
}
int row = table.getNumRows();
- SyncStorageEngine.AuthorityInfo settings =
- mSyncStorageEngine.getOrCreateAuthority(
+ Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus =
+ mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus(
account.account, account.userId, syncAdapterType.type.authority);
- SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings);
+ SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first;
+ SyncStatusInfo status = syncAuthoritySyncStatus.second;
String authority = settings.authority;
if (authority.length() > 50) {
@@ -1274,10 +1271,10 @@
for (int i = 0; i < settings.periodicSyncs.size(); i++) {
- final Pair<Bundle, Long> pair = settings.periodicSyncs.get(0);
+ final Pair<Bundle, Long> pair = settings.periodicSyncs.get(i);
final String period = String.valueOf(pair.second);
final String extras = pair.first.size() > 0 ? pair.first.toString() : "";
- final String next = formatTime(status.getPeriodicSyncTime(0)
+ final String next = formatTime(status.getPeriodicSyncTime(i)
+ pair.second * 1000);
table.set(row + i * 2, 12, period + extras);
table.set(row + i * 2 + 1, 12, next);
@@ -1763,7 +1760,8 @@
final Pair<Account, String> wakeLockKey = Pair.create(account, authority);
PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey);
if (wakeLock == null) {
- final String name = SYNC_WAKE_LOCK_PREFIX + "_" + authority + "_" + account;
+ final String name = SYNC_WAKE_LOCK_PREFIX + "/" + authority + "/" + account.type
+ + "/" + account.name;
wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
wakeLock.setReferenceCounted(false);
mWakeLocks.put(wakeLockKey, wakeLock);
@@ -1947,30 +1945,36 @@
final long nowAbsolute = System.currentTimeMillis();
final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
- ? (nowAbsolute - mSyncRandomOffsetMillis) : 0;
+ ? (nowAbsolute - mSyncRandomOffsetMillis) : 0;
- ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities();
- for (SyncStorageEngine.AuthorityInfo info : infos) {
- // skip the sync if the account of this operation no longer exists
- if (!containsAccountAndUser(accounts, info.account, info.userId)) {
+ ArrayList<Pair<AuthorityInfo, SyncStatusInfo>> infos = mSyncStorageEngine
+ .getCopyOfAllAuthoritiesWithSyncStatus();
+ for (Pair<AuthorityInfo, SyncStatusInfo> info : infos) {
+ final AuthorityInfo authorityInfo = info.first;
+ final SyncStatusInfo status = info.second;
+ // skip the sync if the account of this operation no longer
+ // exists
+ if (!containsAccountAndUser(
+ accounts, authorityInfo.account, authorityInfo.userId)) {
continue;
}
- if (!mSyncStorageEngine.getMasterSyncAutomatically(info.userId)
- || !mSyncStorageEngine.getSyncAutomatically(info.account, info.userId,
- info.authority)) {
+ if (!mSyncStorageEngine.getMasterSyncAutomatically(authorityInfo.userId)
+ || !mSyncStorageEngine.getSyncAutomatically(
+ authorityInfo.account, authorityInfo.userId,
+ authorityInfo.authority)) {
continue;
}
- if (getIsSyncable(info.account, info.userId, info.authority)
+ if (getIsSyncable(
+ authorityInfo.account, authorityInfo.userId, authorityInfo.authority)
== 0) {
continue;
}
- SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(info);
- for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) {
- final Bundle extras = info.periodicSyncs.get(i).first;
- final Long periodInMillis = info.periodicSyncs.get(i).second * 1000;
+ for (int i = 0, N = authorityInfo.periodicSyncs.size(); i < N; i++) {
+ final Bundle extras = authorityInfo.periodicSyncs.get(i).first;
+ final Long periodInMillis = authorityInfo.periodicSyncs.get(i).second * 1000;
// Skip if the period is invalid
if (periodInMillis <= 0) {
continue;
@@ -1979,51 +1983,56 @@
final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i);
long remainingMillis
- = periodInMillis - (shiftedNowAbsolute % periodInMillis);
+ = periodInMillis - (shiftedNowAbsolute % periodInMillis);
/*
- * Sync scheduling strategy:
- * Set the next periodic sync based on a random offset (in seconds).
- *
- * Also sync right now if any of the following cases hold
- * and mark it as having been scheduled
- *
- * Case 1: This sync is ready to run now.
- * Case 2: If the lastPollTimeAbsolute is in the future,
- * sync now and reinitialize. This can happen for
- * example if the user changed the time, synced and
- * changed back.
- * Case 3: If we failed to sync at the last scheduled time
+ * Sync scheduling strategy: Set the next periodic sync
+ * based on a random offset (in seconds). Also sync right
+ * now if any of the following cases hold and mark it as
+ * having been scheduled
+ * Case 1: This sync is ready to run
+ * now.
+ * Case 2: If the lastPollTimeAbsolute is in the
+ * future, sync now and reinitialize. This can happen for
+ * example if the user changed the time, synced and changed
+ * back.
+ * Case 3: If we failed to sync at the last scheduled
+ * time
*/
- if (remainingMillis == periodInMillis // Case 1
+ if (remainingMillis == periodInMillis // Case 1
|| lastPollTimeAbsolute > nowAbsolute // Case 2
|| (nowAbsolute - lastPollTimeAbsolute
>= periodInMillis)) { // Case 3
// Sync now
final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
- info.account, info.userId, info.authority);
+ authorityInfo.account, authorityInfo.userId,
+ authorityInfo.authority);
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
syncAdapterInfo = mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(info.authority, info.account.type),
- info.userId);
+ SyncAdapterType.newKey(
+ authorityInfo.authority, authorityInfo.account.type),
+ authorityInfo.userId);
if (syncAdapterInfo == null) {
continue;
}
scheduleSyncOperation(
- new SyncOperation(info.account, info.userId,
+ new SyncOperation(authorityInfo.account, authorityInfo.userId,
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_PERIODIC,
- info.authority, extras, 0 /* delay */,
- backoff != null ? backoff.first : 0,
+ authorityInfo.authority, extras, 0 /* delay */,
+ backoff != null ? backoff.first : 0,
mSyncStorageEngine.getDelayUntilTime(
- info.account, info.userId, info.authority),
+ authorityInfo.account, authorityInfo.userId,
+ authorityInfo.authority),
syncAdapterInfo.type.allowParallelSyncs()));
- status.setPeriodicSyncTime(i, nowAbsolute);
+ mSyncStorageEngine.setPeriodicSyncTime(authorityInfo.ident,
+ authorityInfo.periodicSyncs.get(i), nowAbsolute);
}
// Compute when this periodic sync should next run
final long nextPollTimeAbsolute = nowAbsolute + remainingMillis;
- // remember this time if it is earlier than earliestFuturePollTime
+ // remember this time if it is earlier than
+ // earliestFuturePollTime
if (nextPollTimeAbsolute < earliestFuturePollTime) {
earliestFuturePollTime = nextPollTimeAbsolute;
}
@@ -2037,8 +2046,8 @@
// convert absolute time to elapsed time
return SystemClock.elapsedRealtime()
+ ((earliestFuturePollTime < nowAbsolute)
- ? 0
- : (earliestFuturePollTime - nowAbsolute));
+ ? 0
+ : (earliestFuturePollTime - nowAbsolute));
}
private long maybeStartNextSyncLocked() {
diff --git a/services/java/com/android/server/content/SyncStorageEngine.java b/services/java/com/android/server/content/SyncStorageEngine.java
index 5b8d26f..c4dc575 100644
--- a/services/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/java/com/android/server/content/SyncStorageEngine.java
@@ -44,6 +44,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.content.SyncStorageEngine.AuthorityInfo;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -813,14 +814,6 @@
}
}
- public AuthorityInfo getOrCreateAuthority(Account account, int userId, String authority) {
- synchronized (mAuthorities) {
- return getOrCreateAuthorityLocked(account, userId, authority,
- -1 /* assign a new identifier if creating a new authority */,
- true /* write to storage if this results in a change */);
- }
- }
-
public void removeAuthority(Account account, int userId, String authority) {
synchronized (mAuthorities) {
removeAuthorityLocked(account, userId, authority, true /* doWrite */);
@@ -1238,17 +1231,27 @@
}
/**
- * Return an array of the current authorities. Note
- * that the objects inside the array are the real, live objects,
- * so be careful what you do with them.
+ * Return a copy of the specified authority with the corresponding sync status
*/
- public ArrayList<AuthorityInfo> getAuthorities() {
+ public Pair<AuthorityInfo, SyncStatusInfo> getCopyOfAuthorityWithSyncStatus(
+ Account account, int userId, String authority) {
synchronized (mAuthorities) {
- final int N = mAuthorities.size();
- ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N);
- for (int i=0; i<N; i++) {
- // Make deep copy because AuthorityInfo syncs are liable to change.
- infos.add(new AuthorityInfo(mAuthorities.valueAt(i)));
+ AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(account, userId, authority,
+ -1 /* assign a new identifier if creating a new authority */,
+ true /* write to storage if this results in a change */);
+ return createCopyPairOfAuthorityWithSyncStatusLocked(authorityInfo);
+ }
+ }
+
+ /**
+ * Return a copy of all authorities with their corresponding sync status
+ */
+ public ArrayList<Pair<AuthorityInfo, SyncStatusInfo>> getCopyOfAllAuthoritiesWithSyncStatus() {
+ synchronized (mAuthorities) {
+ ArrayList<Pair<AuthorityInfo, SyncStatusInfo>> infos =
+ new ArrayList<Pair<AuthorityInfo, SyncStatusInfo>>(mAuthorities.size());
+ for (int i = 0; i < mAuthorities.size(); i++) {
+ infos.add(createCopyPairOfAuthorityWithSyncStatusLocked(mAuthorities.valueAt(i)));
}
return infos;
}
@@ -1337,6 +1340,12 @@
}
}
+ private Pair<AuthorityInfo, SyncStatusInfo> createCopyPairOfAuthorityWithSyncStatusLocked(
+ AuthorityInfo authorityInfo) {
+ SyncStatusInfo syncStatusInfo = getOrCreateSyncStatusLocked(authorityInfo.ident);
+ return Pair.create(new AuthorityInfo(authorityInfo), new SyncStatusInfo(syncStatusInfo));
+ }
+
private int getCurrentDayLocked() {
mCal.setTimeInMillis(System.currentTimeMillis());
final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
@@ -1427,9 +1436,30 @@
}
}
- public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) {
+ /**
+ * Updates (in a synchronized way) the periodic sync time of the specified
+ * authority id and target periodic sync
+ */
+ public void setPeriodicSyncTime(
+ int authorityId, Pair<Bundle, Long> targetPeriodicSync, long when) {
+ boolean found = false;
+ final AuthorityInfo authorityInfo;
synchronized (mAuthorities) {
- return getOrCreateSyncStatusLocked(authority.ident);
+ authorityInfo = mAuthorities.get(authorityId);
+ for (int i = 0; i < authorityInfo.periodicSyncs.size(); i++) {
+ Pair<Bundle, Long> periodicSync = authorityInfo.periodicSyncs.get(i);
+ if (PeriodicSync.syncExtrasEquals(periodicSync.first, targetPeriodicSync.first)
+ && periodicSync.second == targetPeriodicSync.second) {
+ mSyncStatus.get(authorityId).setPeriodicSyncTime(i, when);
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ Log.w(TAG, "Ignoring setPeriodicSyncTime request for a sync that does not exist. " +
+ "Authority: " + authorityInfo.authority);
}
}
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index 247d8a0..11f8d6a 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -61,6 +61,18 @@
public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1 << 3;
/**
+ * Flag: Indicates that the display device is owned by a particular application
+ * and that no other application should be able to interact with it.
+ */
+ public static final int FLAG_PRIVATE = 1 << 4;
+
+ /**
+ * Flag: Indicates that the display device is not blanked automatically by
+ * the power manager.
+ */
+ public static final int FLAG_NEVER_BLANK = 1 << 5;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
@@ -150,6 +162,23 @@
*/
public String address;
+ /**
+ * The UID of the application that owns this display, or zero if it is owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ */
+ public int ownerUid;
+
+ /**
+ * The package name of the application that owns this display, or null if it is
+ * owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ */
+ public String ownerPackageName;
+
public void setAssumedDensityForExternalDisplay(int width, int height) {
densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
// Technically, these values should be smaller than the apparent density
@@ -176,7 +205,9 @@
&& touch == other.touch
&& rotation == other.rotation
&& type == other.type
- && Objects.equal(address, other.address);
+ && Objects.equal(address, other.address)
+ && ownerUid == other.ownerUid
+ && Objects.equal(ownerPackageName, other.ownerPackageName);
}
@Override
@@ -197,19 +228,32 @@
rotation = other.rotation;
type = other.type;
address = other.address;
+ ownerUid = other.ownerUid;
+ ownerPackageName = other.ownerPackageName;
}
// For debugging purposes
@Override
public String toString() {
- return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", "
- + refreshRate + " fps, "
- + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
- + ", touch " + touchToString(touch) + flagsToString(flags)
- + ", rotation " + rotation
- + ", type " + Display.typeToString(type)
- + ", address " + address
- + "}";
+ StringBuilder sb = new StringBuilder();
+ sb.append("DisplayDeviceInfo{\"");
+ sb.append(name).append("\": ").append(width).append(" x ").append(height);
+ sb.append(", ").append(refreshRate).append(" fps, ");
+ sb.append("density ").append(densityDpi);
+ sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
+ sb.append(", touch ").append(touchToString(touch));
+ sb.append(", rotation ").append(rotation);
+ sb.append(", type ").append(Display.typeToString(type));
+ if (address != null) {
+ sb.append(", address ").append(address);
+ }
+ if (ownerUid != 0 || ownerPackageName != null) {
+ sb.append(", owner ").append(ownerPackageName);
+ sb.append(" (uid ").append(ownerUid).append(")");
+ }
+ sb.append(flagsToString(flags));
+ sb.append("}");
+ return sb.toString();
}
private static String touchToString(int touch) {
@@ -239,6 +283,12 @@
if ((flags & FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
msg.append(", FLAG_SUPPORTS_PROTECTED_BUFFERS");
}
+ if ((flags & FLAG_PRIVATE) != 0) {
+ msg.append(", FLAG_PRIVATE");
+ }
+ if ((flags & FLAG_NEVER_BLANK) != 0) {
+ msg.append(", FLAG_NEVER_BLANK");
+ }
return msg.toString();
}
}
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 17b0662..c339c26 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -33,14 +33,19 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
+
+import com.android.server.UiThread;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -170,6 +175,9 @@
// The Wifi display adapter, or null if not registered.
private WifiDisplayAdapter mWifiDisplayAdapter;
+ // The virtual display adapter, or null if not registered.
+ private VirtualDisplayAdapter mVirtualDisplayAdapter;
+
// Viewports of the default display and the display that should receive touch
// input from an external source. Used by the input system.
private final DisplayViewport mDefaultViewport = new DisplayViewport();
@@ -190,12 +198,12 @@
private final DisplayViewport mTempDefaultViewport = new DisplayViewport();
private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport();
- public DisplayManagerService(Context context, Handler mainHandler, Handler uiHandler) {
+ public DisplayManagerService(Context context, Handler mainHandler) {
mContext = context;
mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
mHandler = new DisplayManagerHandler(mainHandler.getLooper());
- mUiHandler = uiHandler;
+ mUiHandler = UiThread.getHandler();
mDisplayAdapterListener = new DisplayAdapterListener();
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
@@ -362,13 +370,7 @@
synchronized (mSyncRoot) {
if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_BLANKED) {
mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_BLANKED;
-
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- DisplayDevice device = mDisplayDevices.get(i);
- device.blankLocked();
- }
-
+ updateAllDisplayBlankingLocked();
scheduleTraversalLocked(false);
}
}
@@ -381,13 +383,7 @@
synchronized (mSyncRoot) {
if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_UNBLANKED) {
mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_UNBLANKED;
-
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- DisplayDevice device = mDisplayDevices.get(i);
- device.unblankLocked();
- }
-
+ updateAllDisplayBlankingLocked();
scheduleTraversalLocked(false);
}
}
@@ -402,12 +398,21 @@
*/
@Override // Binder call
public DisplayInfo getDisplayInfo(int displayId) {
- synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplays.get(displayId);
- if (display != null) {
- return display.getDisplayInfoLocked();
+ final int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ DisplayInfo info = display.getDisplayInfoLocked();
+ if (info.hasAccess(callingUid)) {
+ return info;
+ }
+ }
+ return null;
}
- return null;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -416,13 +421,27 @@
*/
@Override // Binder call
public int[] getDisplayIds() {
- synchronized (mSyncRoot) {
- final int count = mLogicalDisplays.size();
- int[] displayIds = new int[count];
- for (int i = 0; i < count; i++) {
- displayIds[i] = mLogicalDisplays.keyAt(i);
+ final int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ final int count = mLogicalDisplays.size();
+ int[] displayIds = new int[count];
+ int n = 0;
+ for (int i = 0; i < count; i++) {
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ DisplayInfo info = display.getDisplayInfoLocked();
+ if (info.hasAccess(callingUid)) {
+ displayIds[n++] = mLogicalDisplays.keyAt(i);
+ }
+ }
+ if (n != count) {
+ displayIds = Arrays.copyOfRange(displayIds, 0, n);
+ }
+ return displayIds;
}
- return displayIds;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -569,6 +588,95 @@
== PackageManager.PERMISSION_GRANTED;
}
+ @Override // Binder call
+ public int createPrivateVirtualDisplay(IBinder appToken, String packageName,
+ String name, int width, int height, int densityDpi, Surface surface) {
+ final int callingUid = Binder.getCallingUid();
+ if (!validatePackageName(callingUid, packageName)) {
+ throw new SecurityException("packageName must match the calling uid");
+ }
+ if (appToken == null) {
+ throw new IllegalArgumentException("appToken must not be null");
+ }
+ if (TextUtils.isEmpty(name)) {
+ throw new IllegalArgumentException("name must be non-null and non-empty");
+ }
+ if (width <= 0 || height <= 0 || densityDpi <= 0) {
+ throw new IllegalArgumentException("width, height, and densityDpi must be "
+ + "greater than 0");
+ }
+ if (surface == null) {
+ throw new IllegalArgumentException("surface must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mVirtualDisplayAdapter == null) {
+ Slog.w(TAG, "Rejecting request to create private virtual display "
+ + "because the virtual display adapter is not available.");
+ return -1;
+ }
+
+ DisplayDevice device = mVirtualDisplayAdapter.createPrivateVirtualDisplayLocked(
+ appToken, callingUid, packageName, name, width, height, densityDpi,
+ surface);
+ if (device == null) {
+ return -1;
+ }
+
+ handleDisplayDeviceAddedLocked(device);
+ LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
+ if (display != null) {
+ return display.getDisplayIdLocked();
+ }
+
+ // Something weird happened and the logical display was not created.
+ Slog.w(TAG, "Rejecting request to create private virtual display "
+ + "because the logical display was not created.");
+ mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
+ handleDisplayDeviceRemovedLocked(device);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return -1;
+ }
+
+ @Override // Binder call
+ public void releaseVirtualDisplay(IBinder appToken) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mVirtualDisplayAdapter == null) {
+ return;
+ }
+
+ DisplayDevice device =
+ mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
+ if (device != null) {
+ handleDisplayDeviceRemovedLocked(device);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private boolean validatePackageName(int uid, String packageName) {
+ if (packageName != null) {
+ String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packageNames != null) {
+ for (String n : packageNames) {
+ if (n.equals(packageName)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
private void registerDefaultDisplayAdapter() {
// Register default display adapter.
synchronized (mSyncRoot) {
@@ -587,6 +695,7 @@
if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
registerOverlayDisplayAdapterLocked();
registerWifiDisplayAdapterLocked();
+ registerVirtualDisplayAdapterLocked();
}
}
}
@@ -607,6 +716,12 @@
}
}
+ private void registerVirtualDisplayAdapterLocked() {
+ mVirtualDisplayAdapter = new VirtualDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener);
+ registerDisplayAdapterLocked(mVirtualDisplayAdapter);
+ }
+
private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() {
// In safe mode, we disable non-essential display adapters to give the user
// an opportunity to fix broken settings or other problems that might affect
@@ -624,31 +739,25 @@
private void handleDisplayDeviceAdded(DisplayDevice device) {
synchronized (mSyncRoot) {
- if (mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to add already added display device: "
- + device.getDisplayDeviceInfoLocked());
- return;
- }
-
- Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
-
- mDisplayDevices.add(device);
- addLogicalDisplayLocked(device);
- scheduleTraversalLocked(false);
-
- // Blank or unblank the display immediately to match the state requested
- // by the power manager (if known).
- switch (mAllDisplayBlankStateFromPowerManager) {
- case DISPLAY_BLANK_STATE_BLANKED:
- device.blankLocked();
- break;
- case DISPLAY_BLANK_STATE_UNBLANKED:
- device.unblankLocked();
- break;
- }
+ handleDisplayDeviceAddedLocked(device);
}
}
+ private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
+ if (mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to add already added display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
+
+ Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
+
+ mDisplayDevices.add(device);
+ addLogicalDisplayLocked(device);
+ updateDisplayBlankingLocked(device);
+ scheduleTraversalLocked(false);
+ }
+
private void handleDisplayDeviceChanged(DisplayDevice device) {
synchronized (mSyncRoot) {
if (!mDisplayDevices.contains(device)) {
@@ -668,17 +777,44 @@
private void handleDisplayDeviceRemoved(DisplayDevice device) {
synchronized (mSyncRoot) {
- if (!mDisplayDevices.remove(device)) {
- Slog.w(TAG, "Attempted to remove non-existent display device: "
- + device.getDisplayDeviceInfoLocked());
- return;
+ handleDisplayDeviceRemovedLocked(device);
+ }
+ }
+ private void handleDisplayDeviceRemovedLocked(DisplayDevice device) {
+ if (!mDisplayDevices.remove(device)) {
+ Slog.w(TAG, "Attempted to remove non-existent display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
+
+ Slog.i(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
+
+ mRemovedDisplayDevices.add(device);
+ updateLogicalDisplaysLocked();
+ scheduleTraversalLocked(false);
+ }
+
+ private void updateAllDisplayBlankingLocked() {
+ final int count = mDisplayDevices.size();
+ for (int i = 0; i < count; i++) {
+ DisplayDevice device = mDisplayDevices.get(i);
+ updateDisplayBlankingLocked(device);
+ }
+ }
+
+ private void updateDisplayBlankingLocked(DisplayDevice device) {
+ // Blank or unblank the display immediately to match the state requested
+ // by the power manager (if known).
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
+ switch (mAllDisplayBlankStateFromPowerManager) {
+ case DISPLAY_BLANK_STATE_BLANKED:
+ device.blankLocked();
+ break;
+ case DISPLAY_BLANK_STATE_UNBLANKED:
+ device.unblankLocked();
+ break;
}
-
- Slog.i(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
-
- mRemovedDisplayDevices.add(device);
- updateLogicalDisplaysLocked();
- scheduleTraversalLocked(false);
}
}
@@ -811,13 +947,21 @@
}
private void configureDisplayInTransactionLocked(DisplayDevice device) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ boolean isPrivate = (info.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0;
+
// Find the logical display that the display device is showing.
+ // Private displays never mirror other displays.
LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
- if (display != null && !display.hasContentLocked()) {
- display = null;
- }
- if (display == null) {
- display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ if (!isPrivate) {
+ if (display != null && !display.hasContentLocked()) {
+ // If the display does not have any content of its own, then
+ // automatically mirror the default logical display contents.
+ display = null;
+ }
+ if (display == null) {
+ display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ }
}
// Apply the logical display configuration to the display device.
@@ -827,11 +971,11 @@
+ device.getDisplayDeviceInfoLocked());
return;
}
- boolean isBlanked = (mAllDisplayBlankStateFromPowerManager == DISPLAY_BLANK_STATE_BLANKED);
+ boolean isBlanked = (mAllDisplayBlankStateFromPowerManager == DISPLAY_BLANK_STATE_BLANKED)
+ && (info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0;
display.configureDisplayInTransactionLocked(device, isBlanked);
// Update the viewports if needed.
- DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if (!mDefaultViewport.valid
&& (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
setViewportLocked(mDefaultViewport, display, device);
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index 424ec36..775ebb2 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -202,6 +202,9 @@
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SECURE) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_SECURE;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_PRIVATE;
+ }
mBaseDisplayInfo.type = deviceInfo.type;
mBaseDisplayInfo.address = deviceInfo.address;
mBaseDisplayInfo.name = deviceInfo.name;
@@ -218,6 +221,8 @@
mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width;
mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;
+ mBaseDisplayInfo.ownerUid = deviceInfo.ownerUid;
+ mBaseDisplayInfo.ownerPackageName = deviceInfo.ownerPackageName;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo = null;
diff --git a/services/java/com/android/server/display/PersistentDataStore.java b/services/java/com/android/server/display/PersistentDataStore.java
index 105c253..67b3695 100644
--- a/services/java/com/android/server/display/PersistentDataStore.java
+++ b/services/java/com/android/server/display/PersistentDataStore.java
@@ -105,7 +105,8 @@
alias = mRememberedWifiDisplays.get(index).getDeviceAlias();
}
if (!Objects.equal(display.getDeviceAlias(), alias)) {
- return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(), alias);
+ return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(),
+ alias, display.isAvailable(), display.canConnect(), display.isRemembered());
}
}
return display;
@@ -259,7 +260,8 @@
}
mRememberedWifiDisplays.add(
- new WifiDisplay(deviceAddress, deviceName, deviceAlias));
+ new WifiDisplay(deviceAddress, deviceName, deviceAlias,
+ false, false, false));
}
}
}
diff --git a/services/java/com/android/server/display/VirtualDisplayAdapter.java b/services/java/com/android/server/display/VirtualDisplayAdapter.java
new file mode 100644
index 0000000..634fba7
--- /dev/null
+++ b/services/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+/**
+ * A display adapter that provides virtual displays on behalf of applications.
+ * <p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class VirtualDisplayAdapter extends DisplayAdapter {
+ static final String TAG = "VirtualDisplayAdapter";
+ static final boolean DEBUG = false;
+
+ private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
+ new ArrayMap<IBinder, VirtualDisplayDevice>();
+
+ // Called with SyncRoot lock held.
+ public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener) {
+ super(syncRoot, context, handler, listener, TAG);
+ }
+
+ public DisplayDevice createPrivateVirtualDisplayLocked(IBinder appToken,
+ int ownerUid, String ownerPackageName,
+ String name, int width, int height, int densityDpi, Surface surface) {
+ IBinder displayToken = SurfaceControl.createDisplay(name, false /*secure*/);
+ VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
+ ownerUid, ownerPackageName, name, width, height, densityDpi, surface);
+
+ try {
+ appToken.linkToDeath(device, 0);
+ } catch (RemoteException ex) {
+ device.releaseLocked();
+ return null;
+ }
+
+ mVirtualDisplayDevices.put(appToken, device);
+
+ // Return the display device without actually sending the event indicating
+ // that it was added. The caller will handle it.
+ return device;
+ }
+
+ public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
+ VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
+ if (device != null) {
+ appToken.unlinkToDeath(device, 0);
+ }
+
+ // Return the display device that was removed without actually sending the
+ // event indicating that it was removed. The caller will handle it.
+ return device;
+ }
+
+ private void handleBinderDiedLocked(IBinder appToken) {
+ VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
+ if (device != null) {
+ Slog.i(TAG, "Virtual display device released because application token died: "
+ + device.mOwnerPackageName);
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+
+ private final class VirtualDisplayDevice extends DisplayDevice
+ implements DeathRecipient {
+ private final IBinder mAppToken;
+ private final int mOwnerUid;
+ final String mOwnerPackageName;
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+
+ private boolean mReleased;
+ private Surface mSurface;
+ private DisplayDeviceInfo mInfo;
+
+ public VirtualDisplayDevice(IBinder displayToken,
+ IBinder appToken, int ownerUid, String ownerPackageName,
+ String name, int width, int height, int densityDpi, Surface surface) {
+ super(VirtualDisplayAdapter.this, displayToken);
+ mAppToken = appToken;
+ mOwnerUid = ownerUid;
+ mOwnerPackageName = ownerPackageName;
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ mSurface = surface;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (getSyncRoot()) {
+ if (!mReleased) {
+ handleBinderDiedLocked(mAppToken);
+ }
+ }
+ }
+
+ public void releaseLocked() {
+ mReleased = true;
+ sendTraversalRequestLocked();
+ }
+
+ @Override
+ public void performTraversalInTransactionLocked() {
+ if (mReleased && mSurface != null) {
+ mSurface.destroy();
+ mSurface = null;
+ }
+ setSurfaceInTransactionLocked(mSurface);
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.name = mName;
+ mInfo.width = mWidth;
+ mInfo.height = mHeight;
+ mInfo.refreshRate = 60;
+ mInfo.densityDpi = mDensityDpi;
+ mInfo.xDpi = mDensityDpi;
+ mInfo.yDpi = mDensityDpi;
+ mInfo.flags = DisplayDeviceInfo.FLAG_PRIVATE | DisplayDeviceInfo.FLAG_NEVER_BLANK;
+ mInfo.type = Display.TYPE_VIRTUAL;
+ mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
+ mInfo.ownerUid = mOwnerUid;
+ mInfo.ownerPackageName = mOwnerPackageName;
+ }
+ return mInfo;
+ }
+ }
+}
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index b655b3a..4c80cf5 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -45,6 +45,8 @@
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
import libcore.util.Objects;
@@ -88,6 +90,7 @@
private int mScanState;
private int mActiveDisplayState;
private WifiDisplay mActiveDisplay;
+ private WifiDisplay[] mDisplays = WifiDisplay.EMPTY_ARRAY;
private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY;
private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY;
@@ -116,6 +119,7 @@
pw.println("mScanState=" + mScanState);
pw.println("mActiveDisplayState=" + mActiveDisplayState);
pw.println("mActiveDisplay=" + mActiveDisplay);
+ pw.println("mDisplays=" + Arrays.toString(mDisplays));
pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays));
pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays));
pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
@@ -229,7 +233,8 @@
WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address);
if (display != null && !Objects.equal(display.getDeviceAlias(), alias)) {
- display = new WifiDisplay(address, display.getDeviceName(), alias);
+ display = new WifiDisplay(address, display.getDeviceName(), alias,
+ false, false, false);
if (mPersistentDataStore.rememberWifiDisplay(display)) {
mPersistentDataStore.saveIfNeeded();
updateRememberedDisplaysLocked();
@@ -262,7 +267,7 @@
if (mCurrentStatus == null) {
mCurrentStatus = new WifiDisplayStatus(
mFeatureState, mScanState, mActiveDisplayState,
- mActiveDisplay, mAvailableDisplays, mRememberedDisplays);
+ mActiveDisplay, mDisplays);
}
if (DEBUG) {
@@ -271,10 +276,36 @@
return mCurrentStatus;
}
+ private void updateDisplaysLocked() {
+ List<WifiDisplay> displays = new ArrayList<WifiDisplay>(
+ mAvailableDisplays.length + mRememberedDisplays.length);
+ boolean[] remembered = new boolean[mAvailableDisplays.length];
+ for (WifiDisplay d : mRememberedDisplays) {
+ boolean available = false;
+ for (int i = 0; i < mAvailableDisplays.length; i++) {
+ if (d.equals(mAvailableDisplays[i])) {
+ remembered[i] = available = true;
+ break;
+ }
+ }
+ if (!available) {
+ displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
+ d.getDeviceAlias(), false, false, true));
+ }
+ }
+ for (int i = 0; i < mAvailableDisplays.length; i++) {
+ WifiDisplay d = mAvailableDisplays[i];
+ displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
+ d.getDeviceAlias(), true, d.canConnect(), remembered[i]));
+ }
+ mDisplays = displays.toArray(WifiDisplay.EMPTY_ARRAY);
+ }
+
private void updateRememberedDisplaysLocked() {
mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays();
mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay);
mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
+ updateDisplaysLocked();
}
private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() {
@@ -487,11 +518,18 @@
availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
availableDisplays);
- if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING
- || !Arrays.equals(mAvailableDisplays, availableDisplays)) {
+ // check if any of the available displays changed canConnect status
+ boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays);
+ for (int i = 0; !changed && i<availableDisplays.length; i++) {
+ changed = availableDisplays[i].canConnect()
+ != mAvailableDisplays[i].canConnect();
+ }
+
+ if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING || changed) {
mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
mAvailableDisplays = availableDisplays;
fixRememberedDisplayNamesFromAvailableDisplaysLocked();
+ updateDisplaysLocked();
scheduleStatusChangedBroadcastLocked();
}
}
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
index 8c8b360..f89917c3 100644
--- a/services/java/com/android/server/display/WifiDisplayController.java
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -897,7 +897,8 @@
}
private static WifiDisplay createWifiDisplay(WifiP2pDevice device) {
- return new WifiDisplay(device.deviceAddress, device.deviceName, null);
+ return new WifiDisplay(device.deviceAddress, device.deviceName, null,
+ true, device.wfdInfo.isSessionAvailable(), false);
}
private final BroadcastReceiver mWifiP2pReceiver = new BroadcastReceiver() {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 8c88cab..4791ec0 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -41,6 +41,7 @@
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -456,7 +457,8 @@
Context.APP_OPS_SERVICE));
// Battery statistics service to be notified when GPS turns on or off
- mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME));
mProperties = new Properties();
try {
diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java
index e251925..13e400f 100644
--- a/services/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/java/com/android/server/net/LockdownVpnTracker.java
@@ -148,8 +148,13 @@
showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
mAcceptedEgressIface = egressProp.getInterfaceName();
- mVpn.startLegacyVpn(mProfile, KeyStore.getInstance(), egressProp);
-
+ try {
+ mVpn.startLegacyVpn(mProfile, KeyStore.getInstance(), egressProp);
+ } catch (IllegalStateException e) {
+ mAcceptedEgressIface = null;
+ Slog.e(TAG, "Failed to start VPN", e);
+ showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
+ }
} else {
Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index a82f421..5ca7242 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -106,7 +106,6 @@
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.INetworkManagementService;
import android.os.IPowerManager;
import android.os.Message;
@@ -134,6 +133,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Objects;
+import com.android.server.IoThread;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
@@ -274,7 +274,6 @@
private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
INetworkPolicyListener>();
- private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final AtomicFile mPolicyFile;
@@ -306,9 +305,7 @@
mNetworkManager = checkNotNull(networkManagement, "missing networkManagement");
mTime = checkNotNull(time, "missing TrustedTime");
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
+ mHandler = new Handler(IoThread.get().getLooper(), mHandlerCallback);
mSuppressDefaultPolicy = suppressDefaultPolicy;
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 74be472..5074409 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -96,7 +96,6 @@
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.PowerManager;
@@ -120,6 +119,7 @@
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
+import com.android.server.IoThread;
import com.android.server.connectivity.Tethering;
import com.google.android.collect.Maps;
@@ -240,7 +240,6 @@
/** Data layer operation counters for splicing into other structures. */
private NetworkStats mUidOperations = new NetworkStats(0L, 10);
- private final HandlerThread mHandlerThread;
private final Handler mHandler;
private boolean mSystemReady;
@@ -271,9 +270,7 @@
Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
+ mHandler = new Handler(IoThread.get().getLooper(), mHandlerCallback);
mSystemDir = checkNotNull(systemDir);
mBaseDir = new File(systemDir, "netstats");
diff --git a/services/java/com/android/server/pm/KeySetManager.java b/services/java/com/android/server/pm/KeySetManager.java
new file mode 100644
index 0000000..93992c2
--- /dev/null
+++ b/services/java/com/android/server/pm/KeySetManager.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.KeySet;
+import android.content.pm.PackageParser;
+import android.os.Binder;
+import android.util.Base64;
+import android.util.Log;
+import android.util.LongSparseArray;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+/*
+ * Manages system-wide KeySet state.
+ */
+public class KeySetManager {
+
+ static final String TAG = "KeySetManager";
+
+ /** Sentinel value returned when a {@code KeySet} is not found. */
+ public static final long KEYSET_NOT_FOUND = -1;
+
+ /** Sentinel value returned when public key is not found. */
+ private static final long PUBLIC_KEY_NOT_FOUND = -1;
+
+ private final Object mLockObject = new Object();
+
+ private final LongSparseArray<KeySet> mKeySets;
+
+ private final LongSparseArray<PublicKey> mPublicKeys;
+
+ private final LongSparseArray<Set<Long>> mKeySetMapping;
+
+ private final Map<String, PackageSetting> mPackages;
+
+ private static long lastIssuedKeySetId = 0;
+
+ private static long lastIssuedKeyId = 0;
+
+ public KeySetManager(Map<String, PackageSetting> packages) {
+ mKeySets = new LongSparseArray<KeySet>();
+ mPublicKeys = new LongSparseArray<PublicKey>();
+ mKeySetMapping = new LongSparseArray<Set<Long>>();
+ mPackages = packages;
+ }
+
+ /**
+ * Determine if a package is signed by the given KeySet.
+ *
+ * Returns false if the package was not signed by all the
+ * keys in the KeySet.
+ *
+ * Returns true if the package was signed by at least the
+ * keys in the given KeySet.
+ *
+ * Note that this can return true for multiple KeySets.
+ */
+ public boolean packageIsSignedBy(String packageName, KeySet ks) {
+ synchronized (mLockObject) {
+ PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new NullPointerException("Invalid package name");
+ }
+ if (pkg.keySetData == null) {
+ throw new NullPointerException("Package has no KeySet data");
+ }
+ long id = getIdByKeySetLocked(ks);
+ return pkg.keySetData.packageIsSignedBy(id);
+ }
+ }
+
+ /**
+ * This informs the system that the given package has defined a KeySet
+ * in its manifest that a) contains the given keys and b) is named
+ * alias by that package.
+ */
+ public void addDefinedKeySetToPackage(String packageName,
+ Set<PublicKey> keys, String alias) {
+ if ((packageName == null) || (keys == null) || (alias == null)) {
+ Log.d(TAG, "Got null argument for a defined keyset, ignoring!");
+ return;
+ }
+ synchronized (mLockObject) {
+ KeySet ks = addKeySetLocked(keys);
+ PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new NullPointerException("Unknown package");
+ }
+ long id = getIdByKeySetLocked(ks);
+ pkg.keySetData.addDefinedKeySet(id, alias);
+ }
+ }
+
+ /**
+ * Similar to the above, this informs the system that the given package
+ * was signed by the provided KeySet.
+ */
+ public void addSigningKeySetToPackage(String packageName,
+ Set<PublicKey> signingKeys) {
+ if ((packageName == null) || (signingKeys == null)) {
+ Log.d(TAG, "Got null argument for a signing keyset, ignoring!");
+ return;
+ }
+ synchronized (mLockObject) {
+ // add the signing KeySet
+ KeySet ks = addKeySetLocked(signingKeys);
+ long id = getIdByKeySetLocked(ks);
+ Set<Long> publicKeyIds = mKeySetMapping.get(id);
+ if (publicKeyIds == null) {
+ throw new NullPointerException("Got invalid KeySet id");
+ }
+
+ // attach it to the package
+ PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new NullPointerException("No such package!");
+ }
+ pkg.keySetData.addSigningKeySet(id);
+
+ // for each KeySet the package defines which is a subset of
+ // the one above, add the KeySet id to the package's signing KeySets
+ for (Long keySetID : pkg.keySetData.getDefinedKeySets()) {
+ Set<Long> definedKeys = mKeySetMapping.get(keySetID);
+ if (publicKeyIds.contains(definedKeys)) {
+ pkg.keySetData.addSigningKeySet(keySetID);
+ }
+ }
+ }
+ }
+
+ /**
+ * Fetches the stable identifier associated with the given KeySet. Returns
+ * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found.
+ */
+ public long getIdByKeySet(KeySet ks) {
+ synchronized (mLockObject) {
+ return getIdByKeySetLocked(ks);
+ }
+ }
+
+ private long getIdByKeySetLocked(KeySet ks) {
+ for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) {
+ KeySet value = mKeySets.valueAt(keySetIndex);
+ if (ks.equals(value)) {
+ return mKeySets.keyAt(keySetIndex);
+ }
+ }
+ return KEYSET_NOT_FOUND;
+ }
+
+ /**
+ * Fetches the KeySet corresponding to the given stable identifier.
+ *
+ * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't
+ * identify a {@link KeySet}.
+ */
+ public KeySet getKeySetById(long id) {
+ synchronized (mLockObject) {
+ return mKeySets.get(id);
+ }
+ }
+
+ /**
+ * Fetches the KeySet that a given package refers to by the provided alias.
+ *
+ * If the package isn't known to us, throws an IllegalArgumentException.
+ * Returns null if the alias isn't known to us.
+ */
+ public KeySet getKeySetByAliasAndPackageName(String packageName, String alias) {
+ synchronized (mLockObject) {
+ PackageSetting p = mPackages.get(packageName);
+ if (p == null) {
+ throw new NullPointerException("Unknown package");
+ }
+ if (p.keySetData == null) {
+ throw new IllegalArgumentException("Package has no keySet data");
+ }
+ long keySetId = p.keySetData.getAliases().get(alias);
+ return mKeySets.get(keySetId);
+ }
+ }
+
+ /**
+ * Fetches all the known {@link KeySet KeySets} that signed the given
+ * package. Returns {@code null} if package is unknown.
+ */
+ public Set<KeySet> getSigningKeySetsByPackageName(String packageName) {
+ synchronized (mLockObject) {
+ Set<KeySet> signingKeySets = new HashSet<KeySet>();
+ PackageSetting p = mPackages.get(packageName);
+ if (p == null) {
+ throw new NullPointerException("Unknown package");
+ }
+ if (p.keySetData == null) {
+ throw new IllegalArgumentException("Package has no keySet data");
+ }
+ for (long l : p.keySetData.getSigningKeySets()) {
+ signingKeySets.add(mKeySets.get(l));
+ }
+ return signingKeySets;
+ }
+ }
+
+ /**
+ * Creates a new KeySet corresponding to the given keys.
+ *
+ * If the {@link PublicKey PublicKeys} aren't known to the system, this
+ * adds them. Otherwise, they're deduped.
+ *
+ * If the KeySet isn't known to the system, this adds that and creates the
+ * mapping to the PublicKeys. If it is known, then it's deduped.
+ *
+ * Throws if the provided set is {@code null}.
+ */
+ private KeySet addKeySetLocked(Set<PublicKey> keys) {
+ if (keys == null) {
+ throw new NullPointerException("Provided keys cannot be null");
+ }
+ // add each of the keys in the provided set
+ Set<Long> addedKeyIds = new HashSet<Long>(keys.size());
+ for (PublicKey k : keys) {
+ long id = addPublicKeyLocked(k);
+ addedKeyIds.add(id);
+ }
+
+ // check to see if the resulting keyset is new
+ long existingKeySetId = getIdFromKeyIdsLocked(addedKeyIds);
+ if (existingKeySetId != KEYSET_NOT_FOUND) {
+ return mKeySets.get(existingKeySetId);
+ }
+
+ // create the KeySet object
+ KeySet ks = new KeySet(new Binder());
+ // get the first unoccupied slot in mKeySets
+ long id = getFreeKeySetIDLocked();
+ // add the KeySet object to it
+ mKeySets.put(id, ks);
+ // add the stable key ids to the mapping
+ mKeySetMapping.put(id, addedKeyIds);
+ // go home
+ return ks;
+ }
+
+ /**
+ * Adds the given PublicKey to the system, deduping as it goes.
+ */
+ private long addPublicKeyLocked(PublicKey key) {
+ // check if the public key is new
+ long existingKeyId = getIdForPublicKeyLocked(key);
+ if (existingKeyId != PUBLIC_KEY_NOT_FOUND) {
+ return existingKeyId;
+ }
+ // if it's new find the first unoccupied slot in the public keys
+ long id = getFreePublicKeyIdLocked();
+ // add the public key to it
+ mPublicKeys.put(id, key);
+ // return the stable identifier
+ return id;
+ }
+
+ /**
+ * Finds the stable identifier for a KeySet based on a set of PublicKey stable IDs.
+ *
+ * Returns KEYSET_NOT_FOUND if there isn't one.
+ */
+ private long getIdFromKeyIdsLocked(Set<Long> publicKeyIds) {
+ for (int keyMapIndex = 0; keyMapIndex < mKeySetMapping.size(); keyMapIndex++) {
+ Set<Long> value = mKeySetMapping.valueAt(keyMapIndex);
+ if (value.equals(publicKeyIds)) {
+ return mKeySetMapping.keyAt(keyMapIndex);
+ }
+ }
+ return KEYSET_NOT_FOUND;
+ }
+
+ /**
+ * Finds the stable identifier for a PublicKey or PUBLIC_KEY_NOT_FOUND.
+ */
+ private long getIdForPublicKeyLocked(PublicKey k) {
+ String encodedPublicKey = new String(k.getEncoded());
+ for (int publicKeyIndex = 0; publicKeyIndex < mPublicKeys.size(); publicKeyIndex++) {
+ PublicKey value = mPublicKeys.valueAt(publicKeyIndex);
+ String encodedExistingKey = new String(value.getEncoded());
+ if (encodedPublicKey.equals(encodedExistingKey)) {
+ return mPublicKeys.keyAt(publicKeyIndex);
+ }
+ }
+ return PUBLIC_KEY_NOT_FOUND;
+ }
+
+ /**
+ * Gets an unused stable identifier for a KeySet.
+ */
+ private long getFreeKeySetIDLocked() {
+ lastIssuedKeySetId += 1;
+ return lastIssuedKeySetId;
+ }
+
+ /**
+ * Same as above, but for public keys.
+ */
+ private long getFreePublicKeyIdLocked() {
+ lastIssuedKeyId += 1;
+ return lastIssuedKeyId;
+ }
+
+ public void removeAppKeySetData(String packageName) {
+ synchronized (mLockObject) {
+ // Get the package's known keys and KeySets
+ Set<Long> deletableKeySets = getKnownKeySetsByPackageNameLocked(packageName);
+ Set<Long> deletableKeys = new HashSet<Long>();
+ Set<Long> knownKeys = null;
+ for (Long ks : deletableKeySets) {
+ knownKeys = mKeySetMapping.get(ks);
+ if (knownKeys != null) {
+ deletableKeys.addAll(knownKeys);
+ }
+ }
+
+ // Now remove the keys and KeySets known to any other package
+ for (String pkgName : mPackages.keySet()) {
+ if (pkgName.equals(packageName)) {
+ continue;
+ }
+ Set<Long> knownKeySets = getKnownKeySetsByPackageNameLocked(pkgName);
+ deletableKeySets.removeAll(knownKeySets);
+ knownKeys = new HashSet<Long>();
+ for (Long ks : knownKeySets) {
+ knownKeys = mKeySetMapping.get(ks);
+ if (knownKeys != null) {
+ deletableKeys.removeAll(knownKeys);
+ }
+ }
+ }
+
+ // The remaining keys and KeySets are not known to any other
+ // application and so can be safely deleted.
+ for (Long ks : deletableKeySets) {
+ mKeySets.delete(ks);
+ mKeySetMapping.delete(ks);
+ }
+ for (Long keyId : deletableKeys) {
+ mPublicKeys.delete(keyId);
+ }
+
+ // Now remove them from the KeySets known to each package
+ for (String pkgName : mPackages.keySet()) {
+ PackageSetting p = mPackages.get(pkgName);
+ for (Long ks : deletableKeySets) {
+ p.keySetData.removeSigningKeySet(ks);
+ p.keySetData.removeDefinedKeySet(ks);
+ }
+ }
+ }
+ }
+
+ private Set<Long> getKnownKeySetsByPackageNameLocked(String packageName) {
+ PackageSetting p = mPackages.get(packageName);
+ if (p == null) {
+ throw new NullPointerException("Unknown package");
+ }
+ if (p.keySetData == null) {
+ throw new IllegalArgumentException("Package has no keySet data");
+ }
+ Set<Long> knownKeySets = new HashSet<Long>();
+ for (long ks : p.keySetData.getSigningKeySets()) {
+ knownKeySets.add(ks);
+ }
+ for (long ks : p.keySetData.getDefinedKeySets()) {
+ knownKeySets.add(ks);
+ }
+ return knownKeySets;
+ }
+
+ public String encodePublicKey(PublicKey k) throws IOException {
+ return new String(Base64.encode(k.getEncoded(), 0));
+ }
+
+ public void dump(PrintWriter pw, String packageName,
+ PackageManagerService.DumpState dumpState) {
+ synchronized (mLockObject) {
+ boolean printedHeader = false;
+ for (Map.Entry<String, PackageSetting> e : mPackages.entrySet()) {
+ String keySetPackage = e.getKey();
+ if (packageName != null && !packageName.equals(keySetPackage)) {
+ continue;
+ }
+ if (!printedHeader) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Key Set Manager:");
+ printedHeader = true;
+ }
+ PackageSetting pkg = e.getValue();
+ pw.print(" ["); pw.print(keySetPackage); pw.println("]");
+ if (pkg.keySetData != null) {
+ boolean printedLabel = false;
+ for (Map.Entry<String, Long> entry : pkg.keySetData.getAliases().entrySet()) {
+ if (!printedLabel) {
+ pw.print(" KeySets Aliases: ");
+ printedLabel = true;
+ } else {
+ pw.print(", ");
+ }
+ pw.print(entry.getKey());
+ pw.print('=');
+ pw.print(Long.toString(entry.getValue()));
+ }
+ if (printedLabel) {
+ pw.println("");
+ }
+ printedLabel = false;
+ for (long keySetId : pkg.keySetData.getDefinedKeySets()) {
+ if (!printedLabel) {
+ pw.print(" Defined KeySets: ");
+ printedLabel = true;
+ } else {
+ pw.print(", ");
+ }
+ pw.print(Long.toString(keySetId));
+ }
+ if (printedLabel) {
+ pw.println("");
+ }
+ printedLabel = false;
+ for (long keySetId : pkg.keySetData.getSigningKeySets()) {
+ if (!printedLabel) {
+ pw.print(" Signing KeySets: ");
+ printedLabel = true;
+ } else {
+ pw.print(", ");
+ }
+ pw.print(Long.toString(keySetId));
+ }
+ if (printedLabel) {
+ pw.println("");
+ }
+ }
+ }
+ }
+ }
+
+ void writeKeySetManagerLPr(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, "keyset-settings");
+ writePublicKeysLPr(serializer);
+ writeKeySetsLPr(serializer);
+ serializer.startTag(null, "lastIssuedKeyId");
+ serializer.attribute(null, "value", Long.toString(lastIssuedKeyId));
+ serializer.endTag(null, "lastIssuedKeyId");
+ serializer.startTag(null, "lastIssuedKeySetId");
+ serializer.attribute(null, "value", Long.toString(lastIssuedKeySetId));
+ serializer.endTag(null, "lastIssuedKeySetId");
+ serializer.endTag(null, "keyset-settings");
+ }
+
+ void writePublicKeysLPr(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, "keys");
+ for (int pKeyIndex = 0; pKeyIndex < mPublicKeys.size(); pKeyIndex++) {
+ long id = mPublicKeys.keyAt(pKeyIndex);
+ PublicKey key = mPublicKeys.valueAt(pKeyIndex);
+ String encodedKey = encodePublicKey(key);
+ serializer.startTag(null, "public-key");
+ serializer.attribute(null, "identifier", Long.toString(id));
+ serializer.attribute(null, "value", encodedKey);
+ serializer.endTag(null, "public-key");
+ }
+ serializer.endTag(null, "keys");
+ }
+
+ void writeKeySetsLPr(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, "keysets");
+ for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) {
+ long id = mKeySetMapping.keyAt(keySetIndex);
+ Set<Long> keys = mKeySetMapping.valueAt(keySetIndex);
+ serializer.startTag(null, "keyset");
+ serializer.attribute(null, "identifier", Long.toString(id));
+ for (long keyId : keys) {
+ serializer.startTag(null, "key-id");
+ serializer.attribute(null, "identifier", Long.toString(keyId));
+ serializer.endTag(null, "key-id");
+ }
+ serializer.endTag(null, "keyset");
+ }
+ serializer.endTag(null, "keysets");
+ }
+
+ void readKeySetsLPw(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
+ long currentKeySetId = 0;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ final String tagName = parser.getName();
+ if (tagName.equals("keys")) {
+ readKeysLPw(parser);
+ } else if (tagName.equals("keysets")) {
+ readKeySetListLPw(parser);
+ }
+ }
+ }
+
+ void readKeysLPw(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ final String tagName = parser.getName();
+ if (tagName.equals("public-key")) {
+ readPublicKeyLPw(parser);
+ } else if (tagName.equals("lastIssuedKeyId")) {
+ lastIssuedKeyId = Long.parseLong(parser.getAttributeValue(null, "value"));
+ } else if (tagName.equals("lastIssuedKeySetId")) {
+ lastIssuedKeySetId = Long.parseLong(parser.getAttributeValue(null, "value"));
+ }
+ }
+ }
+
+ void readKeySetListLPw(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ long currentKeySetId = 0;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ final String tagName = parser.getName();
+ if (tagName.equals("keyset")) {
+ currentKeySetId = readIdentifierLPw(parser);
+ mKeySets.put(currentKeySetId, new KeySet(new Binder()));
+ mKeySetMapping.put(currentKeySetId, new HashSet<Long>());
+ } else if (tagName.equals("key-id")) {
+ long id = readIdentifierLPw(parser);
+ mKeySetMapping.get(currentKeySetId).add(id);
+ }
+ }
+ }
+
+ long readIdentifierLPw(XmlPullParser parser)
+ throws XmlPullParserException {
+ return Long.parseLong(parser.getAttributeValue(null, "identifier"));
+ }
+
+ void readPublicKeyLPw(XmlPullParser parser)
+ throws XmlPullParserException {
+ String encodedID = parser.getAttributeValue(null, "identifier");
+ long identifier = Long.parseLong(encodedID);
+ String encodedPublicKey = parser.getAttributeValue(null, "value");
+ PublicKey pub = PackageParser.parsePublicKey(encodedPublicKey);
+ if (pub != null) {
+ mPublicKeys.put(identifier, pub);
+ }
+ }
+}
diff --git a/services/java/com/android/server/pm/PackageKeySetData.java b/services/java/com/android/server/pm/PackageKeySetData.java
new file mode 100644
index 0000000..cb60621
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageKeySetData.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class PackageKeySetData {
+
+ private long[] mSigningKeySets;
+
+ private long[] mDefinedKeySets;
+
+ private final Map<String, Long> mKeySetAliases;
+
+ PackageKeySetData() {
+ mSigningKeySets = new long[0];
+ mDefinedKeySets = new long[0];
+ mKeySetAliases = new HashMap<String, Long>();
+ }
+
+ PackageKeySetData(PackageKeySetData original) {
+ mSigningKeySets = original.getSigningKeySets().clone();
+ mDefinedKeySets = original.getDefinedKeySets().clone();
+ mKeySetAliases = new HashMap<String, Long>();
+ mKeySetAliases.putAll(original.getAliases());
+ }
+
+ public void addSigningKeySet(long ks) {
+ // deduplicate
+ for (long knownKeySet : mSigningKeySets) {
+ if (ks == knownKeySet) {
+ return;
+ }
+ }
+ int end = mSigningKeySets.length;
+ mSigningKeySets = Arrays.copyOf(mSigningKeySets, end + 1);
+ mSigningKeySets[end] = ks;
+ }
+
+ public void removeSigningKeySet(long ks) {
+ if (packageIsSignedBy(ks)) {
+ long[] keysets = new long[mSigningKeySets.length - 1];
+ int index = 0;
+ for (long signingKeySet : mSigningKeySets) {
+ if (signingKeySet != ks) {
+ keysets[index] = signingKeySet;
+ index += 1;
+ }
+ }
+ mSigningKeySets = keysets;
+ }
+ }
+
+ public void addDefinedKeySet(long ks, String alias) {
+ // deduplicate
+ for (long knownKeySet : mDefinedKeySets) {
+ if (ks == knownKeySet) {
+ return;
+ }
+ }
+ int end = mDefinedKeySets.length;
+ mDefinedKeySets = Arrays.copyOf(mDefinedKeySets, end + 1);
+ mDefinedKeySets[end] = ks;
+ mKeySetAliases.put(alias, ks);
+ }
+
+ public void removeDefinedKeySet(long ks) {
+ if (mKeySetAliases.containsValue(ks)) {
+ long[] keysets = new long[mDefinedKeySets.length - 1];
+ int index = 0;
+ for (long definedKeySet : mDefinedKeySets) {
+ if (definedKeySet != ks) {
+ keysets[index] = definedKeySet;
+ index += 1;
+ }
+ }
+ mDefinedKeySets = keysets;
+ for (String alias : mKeySetAliases.keySet()) {
+ if (mKeySetAliases.get(alias) == ks) {
+ mKeySetAliases.remove(alias);
+ break;
+ }
+ }
+ }
+ }
+
+ public boolean packageIsSignedBy(long ks) {
+ for (long signingKeySet : mSigningKeySets) {
+ if (ks == signingKeySet) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public long[] getSigningKeySets() {
+ return mSigningKeySets;
+ }
+
+ public long[] getDefinedKeySets() {
+ return mDefinedKeySets;
+ }
+
+ public Map<String, Long> getAliases() {
+ return mKeySetAliases;
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 8cdca1f..8fde010 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -35,12 +35,14 @@
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
+import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.server.DeviceStorageMonitorService;
import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
+import com.android.server.Watchdog;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -112,7 +114,6 @@
import android.os.UserHandle;
import android.os.Environment.UserEnvironment;
import android.os.UserManager;
-import android.provider.Settings.Secure;
import android.security.KeyStore;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
@@ -276,6 +277,9 @@
// This is the object monitoring the system app dir.
final FileObserver mSystemInstallObserver;
+ // This is the object monitoring the privileged system app dir.
+ final FileObserver mPrivilegedInstallObserver;
+
// This is the object monitoring the system app dir.
final FileObserver mVendorInstallObserver;
@@ -291,6 +295,7 @@
final File mFrameworkDir;
final File mSystemAppDir;
+ final File mPrivilegedAppDir;
final File mVendorAppDir;
final File mAppInstallDir;
final File mDalvikCacheDir;
@@ -427,7 +432,7 @@
final SparseArray<HashMap<String, ArrayList<String>>> mUidMap;
public PendingPackageBroadcasts() {
- mUidMap = new SparseArray<HashMap<String, ArrayList<String>>>();
+ mUidMap = new SparseArray<HashMap<String, ArrayList<String>>>(2);
}
public ArrayList<String> get(int userId, String packageName) {
@@ -1054,13 +1059,18 @@
mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings(context);
- mSettings.addSharedUserLPw("android.uid.system",
- Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM);
+ mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
+ ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
+ mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
+ ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
+ mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
+ ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
+ mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
+ ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
+ mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
+ ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
+ mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
+ ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -1090,6 +1100,7 @@
synchronized (mPackages) {
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
+ Watchdog.getInstance().addThread(mHandler, mHandlerThread.getName());
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
@@ -1241,7 +1252,16 @@
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanMode | SCAN_NO_DEX, 0);
- // Collect all system packages.
+ // Collected privileged system packages.
+ mPrivilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
+ mPrivilegedInstallObserver = new AppDirObserver(
+ mPrivilegedAppDir.getPath(), OBSERVER_EVENTS, true);
+ mPrivilegedInstallObserver.startWatching();
+ scanDirLI(mPrivilegedAppDir, PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR
+ | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);
+
+ // Collect ordinary system packages.
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
@@ -1913,8 +1933,6 @@
getDataPathForPackage(packageName, 0).getPath();
pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
}
- // pkg.mSetEnabled = ps.getEnabled(userId);
- // pkg.mSetStopped = ps.getStopped(userId);
return generatePackageInfo(pkg, flags, userId);
}
return null;
@@ -3235,7 +3253,6 @@
public List<ProviderInfo> queryContentProviders(String processName,
int uid, int flags) {
ArrayList<ProviderInfo> finalList = null;
-
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
@@ -3311,7 +3328,8 @@
}
if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Scanning app dir " + dir);
+ Log.d(TAG, "Scanning app dir " + dir + " scanMode=" + scanMode
+ + " flags=0x" + Integer.toHexString(flags));
}
int i;
@@ -3344,7 +3362,7 @@
try {
File fname = getSettingsProblemFile();
FileOutputStream out = new FileOutputStream(fname, true);
- PrintWriter pw = new PrintWriter(out);
+ PrintWriter pw = new FastPrintWriter(out);
SimpleDateFormat formatter = new SimpleDateFormat();
String dateString = formatter.format(new Date(System.currentTimeMillis()));
pw.println(dateString + ": " + msg);
@@ -3400,10 +3418,12 @@
pp.setOnlyCoreApps(mOnlyCore);
final PackageParser.Package pkg = pp.parsePackage(scanFile,
scanPath, mMetrics, parseFlags);
+
if (pkg == null) {
mLastScanError = pp.getParseError();
return null;
}
+
PackageSetting ps = null;
PackageSetting updatedPkg;
// reader
@@ -3553,6 +3573,7 @@
} else {
resPath = pkg.mScanPath;
}
+
codePath = pkg.mScanPath;
// Set application objects path explicitly.
setApplicationInfoPaths(pkg, codePath, resPath);
@@ -3967,6 +3988,10 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
}
+ if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_PRIVILEGED;
+ }
+
if (pkg.packageName.equals("android")) {
synchronized (mPackages) {
if (mAndroidApplication != null) {
@@ -4540,7 +4565,7 @@
// version of the application while the new one gets installed.
if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
killApplication(pkg.applicationInfo.packageName,
- pkg.applicationInfo.uid);
+ pkg.applicationInfo.uid, "update pkg");
}
// Also need to kill any apps that are dependent on the library.
@@ -4548,7 +4573,7 @@
for (int i=0; i<clientLibPkgs.size(); i++) {
PackageParser.Package clientPkg = clientLibPkgs.get(i);
killApplication(clientPkg.applicationInfo.packageName,
- clientPkg.applicationInfo.uid);
+ clientPkg.applicationInfo.uid, "update lib");
}
}
@@ -4589,6 +4614,24 @@
}
}
+ // Add the package's KeySets to the global KeySetManager
+ KeySetManager ksm = mSettings.mKeySetManager;
+ try {
+ ksm.addSigningKeySetToPackage(pkg.packageName, pkg.mSigningKeys);
+ if (pkg.mKeySetMapping != null) {
+ for (Map.Entry<String, Set<PublicKey>> entry : pkg.mKeySetMapping.entrySet()) {
+ if (entry.getValue() != null) {
+ ksm.addDefinedKeySetToPackage(pkg.packageName,
+ entry.getValue(), entry.getKey());
+ }
+ }
+ }
+ } catch (NullPointerException e) {
+ Slog.e(TAG, "Could not add KeySet to " + pkg.packageName, e);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Could not add KeySet to malformed package" + pkg.packageName, e);
+ }
+
int N = pkg.providers.size();
StringBuilder r = null;
int i;
@@ -4880,14 +4923,14 @@
return NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir);
}
- private void killApplication(String pkgName, int appId) {
+ private void killApplication(String pkgName, int appId, String reason) {
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
- am.killApplicationWithAppId(pkgName, appId);
+ am.killApplicationWithAppId(pkgName, appId, reason);
} catch (RemoteException e) {
}
}
@@ -5340,7 +5383,7 @@
== PackageManager.SIGNATURE_MATCH);
if (!allowed && (bp.protectionLevel
& PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) {
- if (isSystemApp(pkg)) {
+ if (isPrivilegedApp(pkg)) {
// For updated system applications, a system permission
// is granted only if it had been defined by the original application.
if (isUpdatedSystemApp(pkg)) {
@@ -6107,6 +6150,120 @@
mHandler.sendMessage(msg);
}
+ private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting, int userId) {
+ Bundle extras = new Bundle(1);
+ extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
+
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ packageName, extras, null, null, new int[] {userId});
+ try {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ final boolean isSystem =
+ isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
+ if (isSystem && am.isUserRunning(userId, false)) {
+ // The just-installed/enabled app is bundled on the system, so presumed
+ // to be able to run automatically without needing an explicit launch.
+ // Send it a BOOT_COMPLETED if it would ordinarily have gotten one.
+ Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED)
+ .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
+ .setPackage(packageName);
+ am.broadcastIntent(null, bcIntent, null, null, 0, null, null, null,
+ android.app.AppOpsManager.OP_NONE, false, false, userId);
+ }
+ } catch (RemoteException e) {
+ // shouldn't happen
+ Slog.w(TAG, "Unable to bootstrap installed package", e);
+ }
+ }
+
+ @Override
+ public boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+ int userId) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ if (UserHandle.getUserId(uid) != userId) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "setApplicationBlocked for user " + userId);
+ }
+
+ if (blocked && isPackageDeviceAdmin(packageName, userId)) {
+ Slog.w(TAG, "Not blocking package " + packageName + ": has active device admin");
+ return false;
+ }
+
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ boolean sendAdded = false;
+ boolean sendRemoved = false;
+ // writer
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null) {
+ return false;
+ }
+ if (pkgSetting.getBlocked(userId) != blocked) {
+ pkgSetting.setBlocked(blocked, userId);
+ mSettings.writePackageRestrictionsLPr(userId);
+ if (blocked) {
+ sendRemoved = true;
+ } else {
+ sendAdded = true;
+ }
+ }
+ }
+ if (sendAdded) {
+ sendPackageAddedForUser(packageName, pkgSetting, userId);
+ return true;
+ }
+ if (sendRemoved) {
+ sendPackageBlockedForUser(packageName, pkgSetting, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ return false;
+ }
+
+ private void sendPackageBlockedForUser(String packageName, PackageSetting pkgSetting,
+ int userId) {
+ final PackageRemovedInfo info = new PackageRemovedInfo();
+ info.removedPackage = packageName;
+ info.removedUsers = new int[] {userId};
+ info.uid = UserHandle.getUid(userId, pkgSetting.appId);
+ info.sendBroadcast(false, false, false);
+ }
+
+ /**
+ * Returns true if application is not found or there was an error. Otherwise it returns
+ * the blocked state of the package for the given user.
+ */
+ @Override
+ public boolean getApplicationBlockedSettingAsUser(String packageName, int userId) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ if (UserHandle.getUserId(uid) != userId) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "getApplicationBlocked for user " + userId);
+ }
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ // writer
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null) {
+ return true;
+ }
+ return pkgSetting.getBlocked(userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
/**
* @hide
*/
@@ -6138,33 +6295,14 @@
}
if (!pkgSetting.getInstalled(userId)) {
pkgSetting.setInstalled(true, userId);
+ pkgSetting.setBlocked(false, userId);
mSettings.writePackageRestrictionsLPr(userId);
- extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
sendAdded = true;
}
}
if (sendAdded) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, new int[] {userId});
- try {
- IActivityManager am = ActivityManagerNative.getDefault();
- final boolean isSystem =
- isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
- if (isSystem && am.isUserRunning(userId, false)) {
- // The just-installed/enabled app is bundled on the system, so presumed
- // to be able to run automatically without needing an explicit launch.
- // Send it a BOOT_COMPLETED if it would ordinarily have gotten one.
- Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED)
- .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
- .setPackage(packageName);
- am.broadcastIntent(null, bcIntent, null, null, 0, null, null, null,
- android.app.AppOpsManager.OP_NONE, false, false, userId);
- }
- } catch (RemoteException e) {
- // shouldn't happen
- Slog.w(TAG, "Unable to bootstrap installed package", e);
- }
+ sendPackageAddedForUser(packageName, pkgSetting, userId);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -8231,6 +8369,9 @@
boolean updatedSettings = false;
parseFlags |= PackageManager.INSTALL_REPLACE_EXISTING |
PackageParser.PARSE_IS_SYSTEM;
+ if ((deletedPackage.applicationInfo.flags&ApplicationInfo.FLAG_PRIVILEGED) != 0) {
+ parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
+ }
String packageName = deletedPackage.packageName;
res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
if (packageName == null) {
@@ -8250,7 +8391,7 @@
}
}
- killApplication(packageName, oldPkg.applicationInfo.uid);
+ killApplication(packageName, oldPkg.applicationInfo.uid, "replace sys pkg");
res.removedInfo.uid = oldPkg.applicationInfo.uid;
res.removedInfo.removedPackage = packageName;
@@ -8543,6 +8684,10 @@
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ private static boolean isPrivilegedApp(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0;
+ }
+
private static boolean isSystemApp(ApplicationInfo info) {
return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
@@ -8651,6 +8796,19 @@
});
}
+ private boolean isPackageDeviceAdmin(String packageName, int userId) {
+ IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ try {
+ if (dpm != null && (dpm.packageHasActiveAdmins(packageName, userId)
+ || dpm.isDeviceOwner(packageName))) {
+ return true;
+ }
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
/**
* This method is an internal method that could be get invoked either
* to delete an installed package or to clean up a failed installation.
@@ -8669,15 +8827,9 @@
final PackageRemovedInfo info = new PackageRemovedInfo();
final boolean res;
- IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
- try {
- if (dpm != null && (dpm.packageHasActiveAdmins(packageName, userId)
- || dpm.isDeviceOwner(packageName))) {
- Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
- return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
- }
- } catch (RemoteException e) {
+ if (isPackageDeviceAdmin(packageName, userId)) {
+ Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
+ return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
}
boolean removedForAllUsers = false;
@@ -8993,6 +9145,7 @@
false, //installed
true, //stopped
true, //notLaunched
+ false, //blocked
null, null, null);
if (!isSystemApp(ps)) {
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
@@ -9043,7 +9196,9 @@
removePackageDataLI(ps, null, null, outInfo, flags, writeSettings);
return true;
}
+
boolean ret = false;
+ mSettings.mKeySetManager.removeAppKeySetData(packageName);
if (isSystemApp(ps)) {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package:" + ps.name);
// When an updated system application is deleted we delete the existing resources as well and
@@ -9053,11 +9208,12 @@
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package:" + ps.name);
// Kill application pre-emptively especially for apps on sd.
- killApplication(packageName, ps.appId);
+ killApplication(packageName, ps.appId, "uninstall pkg");
ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,
allUserHandles, perUserInstalled,
outInfo, writeSettings);
}
+
return ret;
}
@@ -9900,6 +10056,8 @@
public static final int DUMP_PREFERRED_XML = 1 << 10;
+ public static final int DUMP_KEYSETS = 1 << 11;
+
public static final int OPTION_SHOW_FILTERS = 1 << 0;
private int mTypes;
@@ -9997,6 +10155,7 @@
pw.println(" m[essages]: print collected runtime messages");
pw.println(" v[erifiers]: print package verifier info");
pw.println(" <package.name>: info about given package");
+ pw.println(" k[eysets]: print known keysets");
return;
} else if ("-f".equals(opt)) {
dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
@@ -10012,6 +10171,9 @@
// Is this a package name?
if ("android".equals(cmd) || cmd.contains(".")) {
packageName = cmd;
+ // When dumping a single package, we always dump all of its
+ // filter information since the amount of data will be reasonable.
+ dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
} else if ("l".equals(cmd) || "libraries".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_LIBS);
} else if ("f".equals(cmd) || "features".equals(cmd)) {
@@ -10038,6 +10200,8 @@
dumpState.setDump(DumpState.DUMP_MESSAGES);
} else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERIFIERS);
+ } else if ("k".equals(cmd) || "keysets".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_KEYSETS);
}
}
@@ -10045,7 +10209,7 @@
synchronized (mPackages) {
if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
pw.println("Verifiers:");
pw.print(" Required: ");
pw.print(mRequiredVerifierPackage);
@@ -10055,16 +10219,20 @@
}
if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
- if (dumpState.onTitlePrinted())
- pw.println(" ");
- pw.println("Libraries:");
+ boolean printedHeader = false;
final Iterator<String> it = mSharedLibraries.keySet().iterator();
while (it.hasNext()) {
String name = it.next();
+ SharedLibraryEntry ent = mSharedLibraries.get(name);
+ if (!printedHeader) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Libraries:");
+ printedHeader = true;
+ }
pw.print(" ");
pw.print(name);
pw.print(" -> ");
- SharedLibraryEntry ent = mSharedLibraries.get(name);
if (ent.path != null) {
pw.print("(jar) ");
pw.print(ent.path);
@@ -10078,7 +10246,7 @@
if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
pw.println("Features:");
Iterator<String> it = mAvailableFeatures.keySet().iterator();
while (it.hasNext()) {
@@ -10154,7 +10322,7 @@
}
if (!printedSomething) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
pw.println("Registered ContentProviders:");
printedSomething = true;
}
@@ -10169,7 +10337,7 @@
}
if (!printedSomething) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
pw.println("ContentProvider Authorities:");
printedSomething = true;
}
@@ -10181,7 +10349,11 @@
}
}
}
-
+
+ if (dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ mSettings.mKeySetManager.dump(pw, packageName, dumpState);
+ }
+
if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
mSettings.dumpPackagesLPr(pw, packageName, dumpState);
}
@@ -10192,10 +10364,10 @@
if (dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
mSettings.dumpReadMessagesLPr(pw, dumpState);
- pw.println(" ");
+ pw.println();
pw.println("Package warning messages:");
final File fname = getSettingsProblemFile();
FileInputStream in = null;
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index e64ec6d..7747c8f 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -65,6 +65,8 @@
boolean permissionsFixed;
boolean haveGids;
+ PackageKeySetData keySetData = new PackageKeySetData();
+
private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
// Whether this package is currently stopped, thus can not be
@@ -120,6 +122,9 @@
origPackage = base.origPackage;
installerPackageName = base.installerPackageName;
+
+ keySetData = new PackageKeySetData(base.keySetData);
+
}
void init(File codePath, File resourcePath, String nativeLibraryPathString,
@@ -170,6 +175,7 @@
userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
}
installStatus = base.installStatus;
+ keySetData = base.keySetData;
}
private PackageUserState modifyUserState(int userId) {
@@ -254,14 +260,24 @@
modifyUserState(userId).notLaunched = stop;
}
+ boolean getBlocked(int userId) {
+ return readUserState(userId).blocked;
+ }
+
+ void setBlocked(boolean blocked, int userId) {
+ modifyUserState(userId).blocked = blocked;
+ }
+
void setUserState(int userId, int enabled, boolean installed, boolean stopped,
- boolean notLaunched, String lastDisableAppCaller, HashSet<String> enabledComponents,
+ boolean notLaunched, boolean blocked,
+ String lastDisableAppCaller, HashSet<String> enabledComponents,
HashSet<String> disabledComponents) {
PackageUserState state = modifyUserState(userId);
state.enabled = enabled;
state.installed = installed;
state.stopped = stopped;
state.notLaunched = notLaunched;
+ state.blocked = blocked;
state.lastDisableAppCaller = lastDisableAppCaller;
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 2e48074..2d7d8a0 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -43,6 +43,7 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
+import android.content.pm.KeySet;
import android.content.pm.PackageCleanItem;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -57,6 +58,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
+import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -67,6 +69,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.security.PublicKey;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -106,6 +109,7 @@
private static final String ATTR_ENABLED = "enabled";
private static final String ATTR_ENABLED_CALLER = "enabledCaller";
private static final String ATTR_STOPPED = "stopped";
+ private static final String ATTR_BLOCKED = "blocked";
private static final String ATTR_INSTALLED = "inst";
private final File mSettingsFilename;
@@ -119,6 +123,8 @@
private final HashMap<String, PackageSetting> mDisabledSysPackages =
new HashMap<String, PackageSetting>();
+ private static int mFirstAvailableUid = 0;
+
// These are the last platform API version we were using for
// the apps installed on internal and external storage. It is
// used to grant newer permissions one time during a system upgrade.
@@ -177,6 +183,9 @@
private final Context mContext;
private final File mSystemDir;
+
+ public final KeySetManager mKeySetManager = new KeySetManager(mPackages);
+
Settings(Context context) {
this(context, Environment.getDataDirectory());
}
@@ -454,6 +463,7 @@
installed,
true, // stopped,
true, // notLaunched
+ false, // blocked
null, null, null);
writePackageRestrictionsLPr(user.id);
}
@@ -729,6 +739,7 @@
} else {
mOtherUserIds.remove(uid);
}
+ setFirstAvailableUid(uid+1);
}
private void replaceUserIdLPw(int uid, Object obj) {
@@ -851,6 +862,7 @@
true, // installed
false, // stopped
false, // notLaunched
+ false, // blocked
null, null, null);
}
return;
@@ -904,6 +916,9 @@
final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
final boolean stopped = stoppedStr == null
? false : Boolean.parseBoolean(stoppedStr);
+ final String blockedStr = parser.getAttributeValue(null, ATTR_BLOCKED);
+ final boolean blocked = blockedStr == null
+ ? false : Boolean.parseBoolean(blockedStr);
final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
final boolean notLaunched = stoppedStr == null
? false : Boolean.parseBoolean(notLaunchedStr);
@@ -927,7 +942,7 @@
}
}
- ps.setUserState(userId, enabled, installed, stopped, notLaunched,
+ ps.setUserState(userId, enabled, installed, stopped, notLaunched, blocked,
enabledCaller, enabledComponents, disabledComponents);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
@@ -1035,6 +1050,7 @@
PackageUserState ustate = pkg.readUserState(userId);
if (ustate.stopped || ustate.notLaunched || !ustate.installed
|| ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT
+ || ustate.blocked
|| (ustate.enabledComponents != null
&& ustate.enabledComponents.size() > 0)
|| (ustate.disabledComponents != null
@@ -1052,6 +1068,9 @@
if (ustate.notLaunched) {
serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
}
+ if (ustate.blocked) {
+ serializer.attribute(null, ATTR_BLOCKED, "true");
+ }
if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
serializer.attribute(null, ATTR_ENABLED,
Integer.toString(ustate.enabled));
@@ -1331,6 +1350,8 @@
}
}
+ mKeySetManager.writeKeySetManagerLPr(serializer);
+
serializer.endTag(null, "packages");
serializer.endDocument();
@@ -1521,9 +1542,31 @@
serializer.endTag(null, "perms");
}
+ writeSigningKeySetsLPr(serializer, pkg.keySetData);
+ writeKeySetAliasesLPr(serializer, pkg.keySetData);
+
serializer.endTag(null, "package");
}
+ void writeSigningKeySetsLPr(XmlSerializer serializer,
+ PackageKeySetData data) throws IOException {
+ for (long id : data.getSigningKeySets()) {
+ serializer.startTag(null, "signing-keyset");
+ serializer.attribute(null, "identifier", Long.toString(id));
+ serializer.endTag(null, "signing-keyset");
+ }
+ }
+
+ void writeKeySetAliasesLPr(XmlSerializer serializer,
+ PackageKeySetData data) throws IOException {
+ for (Map.Entry<String, Long> e: data.getAliases().entrySet()) {
+ serializer.startTag(null, "defined-keyset");
+ serializer.attribute(null, "alias", e.getKey());
+ serializer.attribute(null, "identifier", Long.toString(e.getValue()));
+ serializer.endTag(null, "defined-keyset");
+ }
+ }
+
void writePermissionLPr(XmlSerializer serializer, BasePermission bp)
throws XmlPullParserException, java.io.IOException {
if (bp.type != BasePermission.TYPE_BUILTIN && bp.sourcePackage != null) {
@@ -1698,6 +1741,8 @@
} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT);
mReadExternalStorageEnforced = "1".equals(enforcement);
+ } else if (tagName.equals("keyset-settings")) {
+ mKeySetManager.readKeySetsLPw(parser);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
+ parser.getName());
@@ -1882,15 +1927,20 @@
if (tmpPa.countDataSchemes() > 0) {
Uri.Builder builder = new Uri.Builder();
builder.scheme(tmpPa.getDataScheme(0));
- if (tmpPa.countDataAuthorities() > 0) {
- IntentFilter.AuthorityEntry auth = tmpPa.getDataAuthority(0);
- if (auth.getHost() != null) {
- builder.authority(auth.getHost());
+ if (tmpPa.countDataSchemeSpecificParts() > 0) {
+ PatternMatcher path = tmpPa.getDataSchemeSpecificPart(0);
+ builder.opaquePart(path.getPath());
+ } else {
+ if (tmpPa.countDataAuthorities() > 0) {
+ IntentFilter.AuthorityEntry auth = tmpPa.getDataAuthority(0);
+ if (auth.getHost() != null) {
+ builder.authority(auth.getHost());
+ }
}
- }
- if (tmpPa.countDataPaths() > 0) {
- PatternMatcher path = tmpPa.getDataPath(0);
- builder.path(path.getPath());
+ if (tmpPa.countDataPaths() > 0) {
+ PatternMatcher path = tmpPa.getDataPath(0);
+ builder.path(path.getPath());
+ }
}
intent.setData(builder.build());
} else if (tmpPa.countDataTypes() > 0) {
@@ -2293,12 +2343,22 @@
} else if (tagName.equals("perms")) {
readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions);
packageSetting.permissionsFixed = true;
+ } else if (tagName.equals("signing-keyset")) {
+ long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
+ packageSetting.keySetData.addSigningKeySet(id);
+ Slog.d(TAG, "Adding signing keyset " + Long.toString(id) + " to " + name);
+ } else if (tagName.equals("defined-keyset")) {
+ long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
+ String alias = parser.getAttributeValue(null, "alias");
+ packageSetting.keySetData.addDefinedKeySet(id, alias);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <package>: " + parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
+
+
} else {
XmlUtils.skipCurrentTag(parser);
}
@@ -2477,11 +2537,18 @@
file.delete();
}
+ // This should be called (at least) whenever an application is removed
+ private void setFirstAvailableUid(int uid) {
+ if (uid > mFirstAvailableUid) {
+ mFirstAvailableUid = uid;
+ }
+ }
+
// Returns -1 if we could not find an available UserId to assign
private int newUserIdLPw(Object obj) {
// Let's be stupidly inefficient for now...
final int N = mUserIds.size();
- for (int i = 0; i < N; i++) {
+ for (int i = mFirstAvailableUid; i < N; i++) {
if (mUserIds.get(i) == null) {
mUserIds.set(i, obj);
return Process.FIRST_APPLICATION_UID + i;
@@ -2790,6 +2857,8 @@
pw.print(prefix); pw.print(" User "); pw.print(user.id); pw.print(": ");
pw.print(" installed=");
pw.print(ps.getInstalled(user.id));
+ pw.print(" blocked=");
+ pw.print(ps.getBlocked(user.id));
pw.print(" stopped=");
pw.print(ps.getStopped(user.id));
pw.print(" notLaunched=");
@@ -2841,7 +2910,7 @@
if (!printedSomething) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
pw.println("Packages:");
printedSomething = true;
}
@@ -2857,7 +2926,7 @@
}
if (!printedSomething) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
pw.println("Renamed packages:");
printedSomething = true;
}
@@ -2877,7 +2946,7 @@
}
if (!printedSomething) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
pw.println("Hidden system packages:");
printedSomething = true;
}
@@ -2894,7 +2963,7 @@
}
if (!printedSomething) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
pw.println("Permissions:");
printedSomething = true;
}
@@ -2928,7 +2997,7 @@
}
if (!printedSomething) {
if (dumpState.onTitlePrinted())
- pw.println(" ");
+ pw.println();
pw.println("Shared users:");
printedSomething = true;
}
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 1323c93..2901212 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -39,12 +39,15 @@
import android.os.IUserManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AtomicFile;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
@@ -63,6 +66,9 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
@@ -78,6 +84,10 @@
private static final String ATTR_ID = "id";
private static final String ATTR_CREATION_TIME = "created";
private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn";
+ private static final String ATTR_SALT = "salt";
+ private static final String ATTR_PIN_HASH = "pinHash";
+ private static final String ATTR_FAILED_ATTEMPTS = "failedAttempts";
+ private static final String ATTR_LAST_RETRY_MS = "lastAttemptMs";
private static final String ATTR_SERIAL_NO = "serialNumber";
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String ATTR_PARTIAL = "partial";
@@ -107,6 +117,13 @@
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
+ // Number of attempts before jumping to the next BACKOFF_TIMES slot
+ private static final int BACKOFF_INC_INTERVAL = 5;
+
+ // Amount of time to force the user to wait before entering the PIN again, after failing
+ // BACKOFF_INC_INTERVAL times.
+ private static final int[] BACKOFF_TIMES = { 0, 30*1000, 60*1000, 5*60*1000, 30*60*1000 };
+
private final Context mContext;
private final PackageManagerService mPm;
private final Object mInstallLock;
@@ -121,6 +138,16 @@
private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
private final SparseArray<Bundle> mUserRestrictions = new SparseArray<Bundle>();
+ class RestrictionsPinState {
+ long salt;
+ String pinHash;
+ int failedAttempts;
+ long lastAttemptTime;
+ }
+
+ private final SparseArray<RestrictionsPinState> mRestrictionsPinStates =
+ new SparseArray<RestrictionsPinState>();
+
/**
* Set of user IDs being actively removed. Removed IDs linger in this set
* for several seconds to work around a VFS caching issue.
@@ -604,6 +631,21 @@
serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
Long.toString(userInfo.lastLoggedInTime));
+ RestrictionsPinState pinState = mRestrictionsPinStates.get(userInfo.id);
+ if (pinState != null) {
+ if (pinState.salt != 0) {
+ serializer.attribute(null, ATTR_SALT, Long.toString(pinState.salt));
+ }
+ if (pinState.pinHash != null) {
+ serializer.attribute(null, ATTR_PIN_HASH, pinState.pinHash);
+ }
+ if (pinState.failedAttempts != 0) {
+ serializer.attribute(null, ATTR_FAILED_ATTEMPTS,
+ Integer.toString(pinState.failedAttempts));
+ serializer.attribute(null, ATTR_LAST_RETRY_MS,
+ Long.toString(pinState.lastAttemptTime));
+ }
+ }
if (userInfo.iconPath != null) {
serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
}
@@ -690,6 +732,10 @@
String iconPath = null;
long creationTime = 0L;
long lastLoggedInTime = 0L;
+ long salt = 0L;
+ String pinHash = null;
+ int failedAttempts = 0;
+ long lastAttemptTime = 0L;
boolean partial = false;
Bundle restrictions = new Bundle();
@@ -722,6 +768,10 @@
iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);
lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);
+ salt = readLongAttribute(parser, ATTR_SALT, 0L);
+ pinHash = parser.getAttributeValue(null, ATTR_PIN_HASH);
+ failedAttempts = readIntAttribute(parser, ATTR_FAILED_ATTEMPTS, 0);
+ lastAttemptTime = readLongAttribute(parser, ATTR_LAST_RETRY_MS, 0L);
String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);
if ("true".equals(valueString)) {
partial = true;
@@ -761,6 +811,22 @@
userInfo.lastLoggedInTime = lastLoggedInTime;
userInfo.partial = partial;
mUserRestrictions.append(id, restrictions);
+ if (salt != 0L) {
+ RestrictionsPinState pinState = mRestrictionsPinStates.get(id);
+ if (pinState == null) {
+ pinState = new RestrictionsPinState();
+ mRestrictionsPinStates.put(id, pinState);
+ }
+ pinState.salt = salt;
+ pinState.pinHash = pinHash;
+ pinState.failedAttempts = failedAttempts;
+ pinState.lastAttemptTime = lastAttemptTime;
+ }
+ // If this is not a restricted profile and there is no restrictions pin, clean up
+ // any restrictions files that might have been left behind.
+ if (!userInfo.isRestricted() && salt == 0) {
+ cleanAppRestrictions(id);
+ }
return userInfo;
} catch (IOException ioe) {
@@ -812,6 +878,26 @@
}
}
+ /**
+ * Removes all the restrictions files (res_<packagename>) for a given user.
+ * Does not do any permissions checking.
+ */
+ private void cleanAppRestrictions(int userId) {
+ synchronized (mPackagesLock) {
+ File dir = Environment.getUserSystemDirectory(userId);
+ String[] files = dir.list();
+ if (files == null) return;
+ for (String fileName : files) {
+ if (fileName.startsWith(RESTRICTIONS_FILE_PREFIX)) {
+ File resFile = new File(dir, fileName);
+ if (resFile.exists()) {
+ resFile.delete();
+ }
+ }
+ }
+ }
+ }
+
@Override
public UserInfo createUser(String name, int flags) {
checkManageUsersPermission("Only the system can create users");
@@ -949,6 +1035,7 @@
}
}, MINUTE_IN_MILLIS);
+ mRestrictionsPinStates.remove(userHandle);
// Remove user file
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
userFile.delete();
@@ -999,6 +1086,123 @@
}
}
+ @Override
+ public boolean changeRestrictionsPin(String newPin) {
+ checkManageUsersPermission("Only system can modify the restrictions pin");
+ int userId = UserHandle.getCallingUserId();
+ synchronized (mPackagesLock) {
+ RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
+ if (pinState == null) {
+ pinState = new RestrictionsPinState();
+ }
+ if (newPin == null) {
+ pinState.salt = 0;
+ pinState.pinHash = null;
+ } else {
+ try {
+ pinState.salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
+ } catch (NoSuchAlgorithmException e) {
+ pinState.salt = (long) (Math.random() * Long.MAX_VALUE);
+ }
+ pinState.pinHash = passwordToHash(newPin, pinState.salt);
+ pinState.failedAttempts = 0;
+ }
+ mRestrictionsPinStates.put(userId, pinState);
+ writeUserLocked(mUsers.get(userId));
+ }
+ return true;
+ }
+
+ @Override
+ public int checkRestrictionsPin(String pin) {
+ checkManageUsersPermission("Only system can verify the restrictions pin");
+ int userId = UserHandle.getCallingUserId();
+ synchronized (mPackagesLock) {
+ RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
+ // If there's no pin set, return error code
+ if (pinState == null || pinState.salt == 0 || pinState.pinHash == null) {
+ return UserManager.PIN_VERIFICATION_FAILED_NOT_SET;
+ } else if (pin == null) {
+ // If just checking if user can be prompted, return remaining time
+ int waitTime = getRemainingTimeForPinAttempt(pinState);
+ Slog.d(LOG_TAG, "Remaining waittime peek=" + waitTime);
+ return waitTime;
+ } else {
+ int waitTime = getRemainingTimeForPinAttempt(pinState);
+ Slog.d(LOG_TAG, "Remaining waittime=" + waitTime);
+ if (waitTime > 0) {
+ return waitTime;
+ }
+ if (passwordToHash(pin, pinState.salt).equals(pinState.pinHash)) {
+ pinState.failedAttempts = 0;
+ writeUserLocked(mUsers.get(userId));
+ return UserManager.PIN_VERIFICATION_SUCCESS;
+ } else {
+ pinState.failedAttempts++;
+ pinState.lastAttemptTime = System.currentTimeMillis();
+ writeUserLocked(mUsers.get(userId));
+ return waitTime;
+ }
+ }
+ }
+ }
+
+ private int getRemainingTimeForPinAttempt(RestrictionsPinState pinState) {
+ int backoffIndex = Math.min(pinState.failedAttempts / BACKOFF_INC_INTERVAL,
+ BACKOFF_TIMES.length - 1);
+ int backoffTime = (pinState.failedAttempts % BACKOFF_INC_INTERVAL) == 0 ?
+ BACKOFF_TIMES[backoffIndex] : 0;
+ return (int) Math.max(backoffTime + pinState.lastAttemptTime - System.currentTimeMillis(),
+ 0);
+ }
+
+ @Override
+ public boolean hasRestrictionsPin() {
+ int userId = UserHandle.getCallingUserId();
+ synchronized (mPackagesLock) {
+ RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
+ if (pinState == null || pinState.salt == 0 || pinState.pinHash == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*
+ * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
+ * Not the most secure, but it is at least a second level of protection. First level is that
+ * the file is in a location only readable by the system process.
+ * @param password the password.
+ * @param salt the randomly generated salt
+ * @return the hash of the pattern in a String.
+ */
+ private String passwordToHash(String password, long salt) {
+ if (password == null) {
+ return null;
+ }
+ String algo = null;
+ String hashed = salt + password;
+ try {
+ byte[] saltedPassword = (password + salt).getBytes();
+ byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
+ byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
+ hashed = toHex(sha1) + toHex(md5);
+ } catch (NoSuchAlgorithmException e) {
+ Log.w(LOG_TAG, "Failed to encode string because of missing algorithm: " + algo);
+ }
+ return hashed;
+ }
+
+ private static String toHex(byte[] ary) {
+ final String hex = "0123456789ABCDEF";
+ String ret = "";
+ for (int i = 0; i < ary.length; i++) {
+ ret += hex.charAt((ary[i] >> 4) & 0xf);
+ ret += hex.charAt(ary[i] & 0xf);
+ }
+ return ret;
+ }
+
private int getUidForPackage(String packageName) {
long ident = Binder.clearCallingIdentity();
try {
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
index d99d523..e44cfe5 100644
--- a/services/java/com/android/server/power/Notifier.java
+++ b/services/java/com/android/server/power/Notifier.java
@@ -16,6 +16,8 @@
package com.android.server.power;
+import android.app.AppOpsManager;
+import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.server.EventLogTags;
@@ -75,6 +77,7 @@
private final Context mContext;
private final IBatteryStats mBatteryStats;
+ private final IAppOpsService mAppOps;
private final SuspendBlocker mSuspendBlocker;
private final ScreenOnBlocker mScreenOnBlocker;
private final WindowManagerPolicy mPolicy;
@@ -104,10 +107,11 @@
private boolean mScreenOnBlockerAcquired;
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
- SuspendBlocker suspendBlocker, ScreenOnBlocker screenOnBlocker,
+ IAppOpsService appOps, SuspendBlocker suspendBlocker, ScreenOnBlocker screenOnBlocker,
WindowManagerPolicy policy) {
mContext = context;
mBatteryStats = batteryStats;
+ mAppOps = appOps;
mSuspendBlocker = suspendBlocker;
mScreenOnBlocker = screenOnBlocker;
mPolicy = policy;
@@ -124,11 +128,12 @@
/**
* Called when a wake lock is acquired.
*/
- public void onWakeLockAcquired(int flags, String tag, int ownerUid, int ownerPid,
- WorkSource workSource) {
+ public void onWakeLockAcquired(int flags, String tag, String packageName,
+ int ownerUid, int ownerPid, WorkSource workSource) {
if (DEBUG) {
Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag
- + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ + "\", packageName=" + packageName
+ + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
@@ -138,6 +143,8 @@
mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, monitorType);
} else {
mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, monitorType);
+ // XXX need to deal with disabled operations.
+ mAppOps.startOperation(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
}
} catch (RemoteException ex) {
// Ignore
@@ -147,11 +154,12 @@
/**
* Called when a wake lock is released.
*/
- public void onWakeLockReleased(int flags, String tag, int ownerUid, int ownerPid,
- WorkSource workSource) {
+ public void onWakeLockReleased(int flags, String tag, String packageName,
+ int ownerUid, int ownerPid, WorkSource workSource) {
if (DEBUG) {
Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag
- + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ + "\", packageName=" + packageName
+ + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
@@ -161,6 +169,7 @@
mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, monitorType);
} else {
mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, monitorType);
+ mAppOps.finishOperation(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
}
} catch (RemoteException ex) {
// Ignore
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 1203e02..5c81cde 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.power;
+import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.server.BatteryService;
import com.android.server.EventLogTags;
@@ -50,6 +51,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.SystemService;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -171,6 +173,7 @@
private BatteryService mBatteryService;
private DisplayManagerService mDisplayManagerService;
private IBatteryStats mBatteryStats;
+ private IAppOpsService mAppOps;
private HandlerThread mHandlerThread;
private PowerManagerHandler mHandler;
private WindowManagerPolicy mPolicy;
@@ -364,8 +367,6 @@
private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE;
private native void nativeInit();
- private static native void nativeShutdown();
- private static native void nativeReboot(String reason) throws IOException;
private static native void nativeSetPowerState(boolean screenOn, boolean screenBright);
private static native void nativeAcquireSuspendBlocker(String name);
@@ -395,17 +396,19 @@
*/
public void init(Context context, LightsService ls,
ActivityManagerService am, BatteryService bs, IBatteryStats bss,
- DisplayManagerService dm) {
+ IAppOpsService appOps, DisplayManagerService dm) {
mContext = context;
mLightsService = ls;
mBatteryService = bs;
mBatteryStats = bss;
+ mAppOps = appOps;
mDisplayManagerService = dm;
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addMonitor(this);
+ Watchdog.getInstance().addThread(mHandler, mHandlerThread.getName());
// Forcibly turn the screen on at boot so that it is in a known power state.
// We do this in init() rather than in the constructor because setting the
@@ -437,7 +440,7 @@
// The notifier runs on the system server's main looper so as not to interfere
// with the animations and other critical functions of the power manager.
mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
- createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
+ mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
mScreenOnBlocker, mPolicy);
// The display power controller runs on the power manager service's
@@ -572,10 +575,14 @@
}
@Override // Binder call
- public void acquireWakeLock(IBinder lock, int flags, String tag, WorkSource ws) {
+ public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
+ WorkSource ws) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
+ if (packageName == null) {
+ throw new IllegalArgumentException("packageName must not be null");
+ }
PowerManager.validateWakeLockParameters(flags, tag);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
@@ -590,14 +597,14 @@
final int pid = Binder.getCallingPid();
final long ident = Binder.clearCallingIdentity();
try {
- acquireWakeLockInternal(lock, flags, tag, ws, uid, pid);
+ acquireWakeLockInternal(lock, flags, tag, packageName, ws, uid, pid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- private void acquireWakeLockInternal(IBinder lock, int flags, String tag, WorkSource ws,
- int uid, int pid) {
+ private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName,
+ WorkSource ws, int uid, int pid) {
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "acquireWakeLockInternal: lock=" + Objects.hashCode(lock)
@@ -612,11 +619,11 @@
if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
// Update existing wake lock. This shouldn't happen but is harmless.
notifyWakeLockReleasedLocked(wakeLock);
- wakeLock.updateProperties(flags, tag, ws, uid, pid);
+ wakeLock.updateProperties(flags, tag, packageName, ws, uid, pid);
notifyWakeLockAcquiredLocked(wakeLock);
}
} else {
- wakeLock = new WakeLock(lock, flags, tag, ws, uid, pid);
+ wakeLock = new WakeLock(lock, flags, tag, packageName, ws, uid, pid);
try {
lock.linkToDeath(wakeLock, 0);
} catch (RemoteException ex) {
@@ -785,14 +792,16 @@
private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
if (mSystemReady) {
- mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag,
+ wakeLock.mNotifiedAcquired = true;
+ mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
}
}
private void notifyWakeLockReleasedLocked(WakeLock wakeLock) {
- if (mSystemReady) {
- mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag,
+ if (mSystemReady && wakeLock.mNotifiedAcquired) {
+ wakeLock.mNotifiedAcquired = false;
+ mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
}
}
@@ -2164,18 +2173,26 @@
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
*/
public static void lowLevelShutdown() {
- nativeShutdown();
+ SystemProperties.set("sys.powerctl", "shutdown");
}
/**
- * Low-level function to reboot the device.
+ * Low-level function to reboot the device. On success, this function
+ * doesn't return. If more than 5 seconds passes from the time,
+ * a reboot is requested, this method returns.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
- * @throws IOException if reboot fails for some reason (eg, lack of
- * permission)
*/
- public static void lowLevelReboot(String reason) throws IOException {
- nativeReboot(reason);
+ public static void lowLevelReboot(String reason) {
+ if (reason == null) {
+ reason = "";
+ }
+ SystemProperties.set("sys.powerctl", "reboot," + reason);
+ try {
+ Thread.sleep(20000);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
}
@Override // Watchdog.Monitor implementation
@@ -2420,15 +2437,18 @@
public final IBinder mLock;
public int mFlags;
public String mTag;
+ public final String mPackageName;
public WorkSource mWorkSource;
- public int mOwnerUid;
- public int mOwnerPid;
+ public final int mOwnerUid;
+ public final int mOwnerPid;
+ public boolean mNotifiedAcquired;
- public WakeLock(IBinder lock, int flags, String tag, WorkSource workSource,
- int ownerUid, int ownerPid) {
+ public WakeLock(IBinder lock, int flags, String tag, String packageName,
+ WorkSource workSource, int ownerUid, int ownerPid) {
mLock = lock;
mFlags = flags;
mTag = tag;
+ mPackageName = packageName;
mWorkSource = copyWorkSource(workSource);
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
@@ -2448,13 +2468,23 @@
&& mOwnerPid == ownerPid;
}
- public void updateProperties(int flags, String tag, WorkSource workSource,
- int ownerUid, int ownerPid) {
+ public void updateProperties(int flags, String tag, String packageName,
+ WorkSource workSource, int ownerUid, int ownerPid) {
+ if (!mPackageName.equals(packageName)) {
+ throw new IllegalStateException("Existing wake lock package name changed: "
+ + mPackageName + " to " + packageName);
+ }
+ if (mOwnerUid != ownerUid) {
+ throw new IllegalStateException("Existing wake lock uid changed: "
+ + mOwnerUid + " to " + ownerUid);
+ }
+ if (mOwnerPid != ownerPid) {
+ throw new IllegalStateException("Existing wake lock pid changed: "
+ + mOwnerPid + " to " + ownerPid);
+ }
mFlags = flags;
mTag = tag;
updateWorkSource(workSource);
- mOwnerUid = ownerUid;
- mOwnerPid = ownerPid;
}
public boolean hasSameWorkSource(WorkSource workSource) {
diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java
index c084666..88a27f5 100644
--- a/services/java/com/android/server/power/ShutdownThread.java
+++ b/services/java/com/android/server/power/ShutdownThread.java
@@ -492,11 +492,8 @@
public static void rebootOrShutdown(boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
- try {
- PowerManagerService.lowLevelReboot(reason);
- } catch (Exception e) {
- Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
- }
+ PowerManagerService.lowLevelReboot(reason);
+ Log.e(TAG, "Reboot failed, will attempt shutdown instead");
} else if (SHUTDOWN_VIBRATE_MS > 0) {
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator();
diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java
new file mode 100644
index 0000000..5173998
--- /dev/null
+++ b/services/java/com/android/server/print/PrintManagerService.java
@@ -0,0 +1,789 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.print;
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.print.IPrintAdapter;
+import android.print.IPrintClient;
+import android.print.IPrintManager;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintAttributes;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.IPrintService;
+import android.printservice.IPrintServiceClient;
+import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.Slog;
+
+import com.android.internal.content.PackageMonitor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+public final class PrintManagerService extends IPrintManager.Stub {
+
+ private static final String LOG_TAG = PrintManagerService.class.getSimpleName();
+
+ private static final char COMPONENT_NAME_SEPARATOR = ':';
+
+ private final Object mLock = new Object();
+
+ private final SimpleStringSplitter mStringColonSplitter =
+ new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
+
+ private final Map<ComponentName, PrintServiceClient> mServices =
+ new HashMap<ComponentName, PrintServiceClient>();
+
+ private final List<PrintServiceInfo> mInstalledServices = new ArrayList<PrintServiceInfo>();
+
+ private final Set<ComponentName> mEnabledServiceNames = new HashSet<ComponentName>();
+
+ private final Context mContext;
+
+ private final RemoteSpooler mSpooler;
+
+ private final int mMyUid;
+
+ private int mCurrentUserId = UserHandle.USER_OWNER;
+
+ private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
+
+ public PrintManagerService(Context context) {
+ mContext = context;
+ mSpooler = new RemoteSpooler(context);
+ mMyUid = android.os.Process.myUid();
+ registerContentObservers();
+ registerBoradcastreceivers();
+ }
+
+ @Override
+ public PrintJobInfo print(String printJobName, IPrintClient client, IPrintAdapter printAdapter,
+ PrintAttributes attributes, int appId, int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mSpooler.createPrintJob(printJobName, client, printAdapter,
+ attributes, resolvedAppId, resolvedUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public List<PrintJobInfo> getPrintJobs(int appId, int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ // TODO: Do we want to return jobs in STATE_CREATED? We should probably
+ // have additional argument for the types to get
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mSpooler.getPrintJobs(null, PrintJobInfo.STATE_ANY,
+ resolvedAppId, resolvedUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public PrintJobInfo getPrintJob(int printJobId, int appId, int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mSpooler.getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void cancelPrintJob(int printJobId, int appId, int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mSpooler.cancelPrintJob(printJobId, resolvedAppId, resolvedUserId)) {
+ return;
+ }
+ PrintJobInfo printJob = getPrintJob(printJobId, resolvedAppId, resolvedUserId);
+ if (printJob == null) {
+ return;
+ }
+ ComponentName printServiceName = printJob.getPrinterId().getServiceComponentName();
+ PrintServiceClient printService = null;
+ synchronized (mLock) {
+ printService = mServices.get(printServiceName);
+ }
+ if (printService == null) {
+ return;
+ }
+ printService.requestCancelPrintJob(printJob);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ // Called only from the spooler.
+ @Override
+ public void onPrintJobQueued(PrinterId printerId, PrintJobInfo printJob) {
+ throwIfCallerNotSignedWithSystemKey();
+ PrintServiceClient printService = null;
+ synchronized (mLock) {
+ ComponentName printServiceName = printerId.getServiceComponentName();
+ printService = mServices.get(printServiceName);
+ }
+ if (printService != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ printService.notifyPrintJobQueued(printJob);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ // Called only from the spooler.
+ @Override
+ public void startDiscoverPrinters(IPrinterDiscoveryObserver observer) {
+ throwIfCallerNotSignedWithSystemKey();
+ List<PrintServiceClient> services = new ArrayList<PrintServiceClient>();
+ synchronized (mLock) {
+ mPrinterDiscoveryObserver = observer;
+ services.addAll(mServices.values());
+ }
+ final int serviceCount = services.size();
+ if (serviceCount <= 0) {
+ return;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < serviceCount; i++) {
+ PrintServiceClient service = services.get(i);
+ service.startPrinterDiscovery();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ // Called only from the spooler.
+ @Override
+ public void stopDiscoverPrinters() {
+ throwIfCallerNotSignedWithSystemKey();
+ List<PrintServiceClient> services = new ArrayList<PrintServiceClient>();
+ synchronized (mLock) {
+ mPrinterDiscoveryObserver = null;
+ services.addAll(mServices.values());
+ }
+ final int serviceCount = services.size();
+ if (serviceCount <= 0) {
+ return;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < serviceCount; i++) {
+ PrintServiceClient service = services.get(i);
+ service.stopPrintersDiscovery();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void registerContentObservers() {
+ final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_PRINT_SERVICES);
+
+ ContentObserver observer = new ContentObserver(new Handler(mContext.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (enabledPrintServicesUri.equals(uri)) {
+ synchronized (mLock) {
+ if (readEnabledPrintServicesChangedLocked()) {
+ onUserStateChangedLocked();
+ }
+ }
+ }
+ }
+ };
+
+ mContext.getContentResolver().registerContentObserver(enabledPrintServicesUri,
+ false, observer, UserHandle.USER_ALL);
+ }
+
+ private void registerBoradcastreceivers() {
+ PackageMonitor monitor = new PackageMonitor() {
+ @Override
+ public void onSomePackagesChanged() {
+ synchronized (mLock) {
+ if (getChangingUserId() != mCurrentUserId) {
+ return;
+ }
+ if (readConfigurationForUserStateLocked()) {
+ onUserStateChangedLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ synchronized (mLock) {
+ if (getChangingUserId() != mCurrentUserId) {
+ return;
+ }
+ Iterator<ComponentName> iterator = mEnabledServiceNames.iterator();
+ while (iterator.hasNext()) {
+ ComponentName componentName = iterator.next();
+ if (packageName.equals(componentName.getPackageName())) {
+ iterator.remove();
+ onEnabledServiceNamesChangedLocked();
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
+ int uid, boolean doit) {
+ synchronized (mLock) {
+ if (getChangingUserId() != mCurrentUserId) {
+ return false;
+ }
+ Iterator<ComponentName> iterator = mEnabledServiceNames.iterator();
+ while (iterator.hasNext()) {
+ ComponentName componentName = iterator.next();
+ String componentPackage = componentName.getPackageName();
+ for (String stoppedPackage : stoppedPackages) {
+ if (componentPackage.equals(stoppedPackage)) {
+ if (!doit) {
+ return true;
+ }
+ iterator.remove();
+ onEnabledServiceNamesChangedLocked();
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ private void onEnabledServiceNamesChangedLocked() {
+ // Update the enabled services setting.
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_PRINT_SERVICES,
+ mEnabledServiceNames, mCurrentUserId);
+ // Update the current user state.
+ onUserStateChangedLocked();
+ }
+ };
+
+ // package changes
+ monitor.register(mContext, null, UserHandle.ALL, true);
+
+ // user changes
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ }
+ }
+ }, UserHandle.ALL, intentFilter, null, null);
+ }
+
+ private void throwIfCallerNotSignedWithSystemKey() {
+ if (mContext.getPackageManager().checkSignatures(
+ mMyUid, Binder.getCallingUid()) != PackageManager.SIGNATURE_MATCH) {
+ throw new SecurityException("Caller must be signed with the system key!");
+ }
+ }
+
+ private void onUserStateChangedLocked() {
+ manageServicesLocked();
+ }
+
+ private void manageServicesLocked() {
+ final int installedCount = mInstalledServices.size();
+ for (int i = 0; i < installedCount; i++) {
+ ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo();
+ ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name);
+ if (mEnabledServiceNames.contains(serviceName)) {
+ if (!mServices.containsKey(serviceName)) {
+ new PrintServiceClient(serviceName, mCurrentUserId).ensureBoundLocked();
+ }
+ } else {
+ PrintServiceClient service = mServices.get(serviceName);
+ if (service != null) {
+ service.ensureUnboundLocked();
+ }
+ }
+ }
+ }
+
+ private boolean readConfigurationForUserStateLocked() {
+ boolean somethingChanged = false;
+ somethingChanged |= readInstalledPrintServiceLocked();
+ somethingChanged |= readEnabledPrintServicesChangedLocked();
+ return somethingChanged;
+ }
+
+ private boolean readEnabledPrintServicesChangedLocked() {
+ Set<ComponentName> tempEnabledServiceNameSet = new HashSet<ComponentName>();
+ readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
+ mCurrentUserId, tempEnabledServiceNameSet);
+ if (!tempEnabledServiceNameSet.equals(mEnabledServiceNames)) {
+ mEnabledServiceNames.clear();
+ mEnabledServiceNames.addAll(tempEnabledServiceNameSet);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean readInstalledPrintServiceLocked() {
+ Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
+
+ List<ResolveInfo> installedServices = mContext.getPackageManager()
+ .queryIntentServicesAsUser(
+ new Intent(android.printservice.PrintService.SERVICE_INTERFACE),
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+ mCurrentUserId);
+
+ final int installedCount = installedServices.size();
+ for (int i = 0, count = installedCount; i < count; i++) {
+ ResolveInfo installedService = installedServices.get(i);
+ if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals(
+ installedService.serviceInfo.permission)) {
+ ComponentName serviceName = new ComponentName(
+ installedService.serviceInfo.packageName,
+ installedService.serviceInfo.name);
+ Slog.w(LOG_TAG, "Skipping print service "
+ + serviceName.flattenToShortString()
+ + " since it does not require permission "
+ + android.Manifest.permission.BIND_PRINT_SERVICE);
+ continue;
+ }
+ tempPrintServices.add(PrintServiceInfo.create(installedService, mContext));
+ }
+
+ if (!tempPrintServices.equals(mInstalledServices)) {
+ mInstalledServices.clear();
+ mInstalledServices.addAll(tempPrintServices);
+ return true;
+ }
+ return false;
+ }
+
+ private void readComponentNamesFromSettingLocked(String settingName, int userId,
+ Set<ComponentName> outComponentNames) {
+ String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ settingName, userId);
+ outComponentNames.clear();
+ if (!TextUtils.isEmpty(settingValue)) {
+ TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+ splitter.setString(settingValue);
+ while (splitter.hasNext()) {
+ String string = splitter.next();
+ if (TextUtils.isEmpty(string)) {
+ continue;
+ }
+ ComponentName componentName = ComponentName.unflattenFromString(string);
+ if (componentName != null) {
+ outComponentNames.add(componentName);
+ }
+ }
+ }
+ }
+
+ private void persistComponentNamesToSettingLocked(String settingName,
+ Set<ComponentName> componentNames, int userId) {
+ StringBuilder builder = new StringBuilder();
+ for (ComponentName componentName : componentNames) {
+ if (builder.length() > 0) {
+ builder.append(COMPONENT_NAME_SEPARATOR);
+ }
+ builder.append(componentName.flattenToShortString());
+ }
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ settingName, builder.toString(), userId);
+ }
+
+ private void switchUser(int newUserId) {
+ synchronized (mLock) {
+ // Disconnect services for the old user.
+ mEnabledServiceNames.clear();
+ onUserStateChangedLocked();
+
+ // The user changed.
+ mCurrentUserId = newUserId;
+
+ // Update the user state based on current settings.
+ readConfigurationForUserStateLocked();
+ onUserStateChangedLocked();
+ }
+
+ // Unbind the spooler for the old user).
+ mSpooler.unbind();
+
+ // If we have queued jobs, advertise it, or we do
+ // not need the spooler for now.
+ if (notifyQueuedPrintJobs()) {
+ mSpooler.unbind();
+ }
+ }
+
+ private boolean notifyQueuedPrintJobs() {
+ Map<PrintServiceClient, List<PrintJobInfo>> notifications =
+ new HashMap<PrintServiceClient, List<PrintJobInfo>>();
+ synchronized (mLock) {
+ for (PrintServiceClient service : mServices.values()) {
+ List<PrintJobInfo> printJobs = mSpooler.getPrintJobs(
+ service.mComponentName, PrintJobInfo.STATE_QUEUED,
+ PrintManager.APP_ID_ANY, service.mUserId);
+ notifications.put(service, printJobs);
+ }
+ }
+ if (notifications.isEmpty()) {
+ return false;
+ }
+ for (Map.Entry<PrintServiceClient, List<PrintJobInfo>> notification
+ : notifications.entrySet()) {
+ PrintServiceClient service = notification.getKey();
+ List<PrintJobInfo> printJobs = notification.getValue();
+ final int printJobIdCount = printJobs.size();
+ for (int i = 0; i < printJobIdCount; i++) {
+ service.notifyPrintJobQueued(printJobs.get(i));
+ }
+ }
+ return true;
+ }
+
+ private int resolveCallingUserEnforcingPermissionsIdLocked(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == 0 || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID) {
+ return userId;
+ }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (callingUserId == userId) {
+ return userId;
+ }
+ if (mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED
+ || mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ != PackageManager.PERMISSION_GRANTED) {
+ if (userId == UserHandle.USER_CURRENT_OR_SELF) {
+ return callingUserId;
+ }
+ throw new SecurityException("Call from user " + callingUserId + " as user "
+ + userId + " without permission INTERACT_ACROSS_USERS or "
+ + "INTERACT_ACROSS_USERS_FULL not allowed.");
+ }
+ if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
+ return mCurrentUserId;
+ }
+ throw new IllegalArgumentException("Calling user can be changed to only "
+ + "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
+ }
+
+ private int resolveCallingAppEnforcingPermissionsLocked(int appId) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == 0 || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID) {
+ return appId;
+ }
+ final int callingAppId = UserHandle.getAppId(callingUid);
+ if (appId == callingAppId) {
+ return appId;
+ }
+ if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Call from app " + callingAppId + " as app "
+ + appId + " without permission INTERACT_ACROSS_APPS");
+ }
+ return appId;
+ }
+
+ private final class PrintServiceClient extends IPrintServiceClient.Stub
+ implements ServiceConnection, DeathRecipient {
+
+ private final ComponentName mComponentName;
+
+ private final Intent mIntent;
+
+ private final int mUserId;
+
+ private IPrintService mInterface;
+
+ private boolean mBinding;
+
+ private boolean mWasConnectedAndDied;
+
+ public PrintServiceClient(ComponentName componentName, int userId) {
+ mComponentName = componentName;
+ mIntent = new Intent().setComponent(mComponentName);
+ mUserId = userId;
+ }
+
+ @Override
+ public List<PrintJobInfo> getPrintJobs() {
+ return mSpooler.getPrintJobs(mComponentName, PrintJobInfo.STATE_ANY,
+ PrintManager.APP_ID_ANY, mUserId);
+ }
+
+ @Override
+ public PrintJobInfo getPrintJob(int printJobId) {
+ return mSpooler.getPrintJobInfo(printJobId,
+ PrintManager.APP_ID_ANY, mUserId);
+ }
+
+ @Override
+ public boolean setPrintJobState(int printJobId, int state) {
+ return mSpooler.setPrintJobState(printJobId, state, mUserId);
+ }
+
+ @Override
+ public boolean setPrintJobTag(int printJobId, String tag) {
+ return mSpooler.setPrintJobTag(printJobId, tag, mUserId);
+ }
+
+ @Override
+ public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
+ mSpooler.writePrintJobData(fd, printJobId, mUserId);
+ }
+
+ @Override
+ public void addDiscoveredPrinters(List<PrinterInfo> printers) {
+ throwIfPrinterIdsForPrinterInfoTampered(printers);
+ synchronized (mLock) {
+ if (mPrinterDiscoveryObserver != null) {
+ try {
+ mPrinterDiscoveryObserver.addDiscoveredPrinters(printers);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ }
+ }
+
+ @Override
+ public void removeDiscoveredPrinters(List<PrinterId> printerIds) {
+ throwIfPrinterIdsTampered(printerIds);
+ synchronized (mLock) {
+ if (mPrinterDiscoveryObserver != null) {
+ try {
+ mPrinterDiscoveryObserver.removeDiscoveredPrinters(printerIds);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ }
+ }
+
+ public void requestCancelPrintJob(PrintJobInfo printJob) {
+ synchronized (mLock) {
+ try {
+ mInterface.requestCancelPrintJob(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error canceling pring job!", re);
+ }
+ }
+ }
+
+ public void notifyPrintJobQueued(PrintJobInfo printJob) {
+ IPrintService service = mInterface;
+ if (service != null) {
+ try {
+ service.onPrintJobQueued(printJob);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ }
+
+ public void startPrinterDiscovery() {
+ IPrintService service = mInterface;
+ if (service != null) {
+ try {
+ service.startPrinterDiscovery();
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ }
+
+ public void stopPrintersDiscovery() {
+ IPrintService service = mInterface;
+ if (service != null) {
+ try {
+ service.stopPrinterDiscovery();
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ }
+
+ public void ensureBoundLocked() {
+ if (mBinding) {
+ return;
+ }
+ if (mInterface == null) {
+ mBinding = true;
+ mContext.bindServiceAsUser(mIntent, this,
+ Context.BIND_AUTO_CREATE, new UserHandle(mUserId));
+ }
+ }
+
+ public void ensureUnboundLocked() {
+ if (mBinding) {
+ mBinding = false;
+ return;
+ }
+ if (mInterface != null) {
+ mContext.unbindService(this);
+ destroyLocked();
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mInterface = IPrintService.Stub.asInterface(service);
+ mServices.put(mComponentName, this);
+ try {
+ mInterface.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException re) {
+ destroyLocked();
+ return;
+ }
+ if (mUserId != mCurrentUserId) {
+ destroyLocked();
+ return;
+ }
+ if (mBinding || mWasConnectedAndDied) {
+ mBinding = false;
+ mWasConnectedAndDied = false;
+ onUserStateChangedLocked();
+ try {
+ mInterface.setClient(this);
+ } catch (RemoteException re) {
+ Slog.w(LOG_TAG, "Error while setting client for service: "
+ + service, re);
+ }
+ } else {
+ destroyLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ /* do nothing - #binderDied takes care */
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ if (isConnectedLocked()) {
+ mWasConnectedAndDied = true;
+ }
+ destroyLocked();
+ }
+ }
+
+ private void destroyLocked() {
+ if (mServices.remove(mComponentName) == null) {
+ return;
+ }
+ if (isConnectedLocked()) {
+ try {
+ mInterface.asBinder().unlinkToDeath(this, 0);
+ } catch (NoSuchElementException nse) {
+ /* ignore */
+ }
+ try {
+ mInterface.setClient(null);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ mInterface = null;
+ }
+ mBinding = false;
+ }
+
+ private boolean isConnectedLocked() {
+ return (mInterface != null);
+ }
+
+ private void throwIfPrinterIdsForPrinterInfoTampered(List<PrinterInfo> printerInfos) {
+ final int printerInfoCount = printerInfos.size();
+ for (int i = 0; i < printerInfoCount; i++) {
+ PrinterId printerId = printerInfos.get(i).getId();
+ throwIfPrinterIdTampered(printerId);
+ }
+ }
+
+ private void throwIfPrinterIdsTampered(List<PrinterId> printerIds) {
+ final int printerIdCount = printerIds.size();
+ for (int i = 0; i < printerIdCount; i++) {
+ PrinterId printerId = printerIds.get(i);
+ throwIfPrinterIdTampered(printerId);
+ }
+ }
+
+ private void throwIfPrinterIdTampered(PrinterId printerId) {
+ if (printerId == null || printerId.getServiceComponentName() == null
+ || !printerId.getServiceComponentName().equals(mComponentName)) {
+ throw new IllegalArgumentException("Invalid printer id: " + printerId);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/print/RemoteSpooler.java b/services/java/com/android/server/print/RemoteSpooler.java
new file mode 100644
index 0000000..fef5818
--- /dev/null
+++ b/services/java/com/android/server/print/RemoteSpooler.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.print;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.IBinder.DeathRecipient;
+import android.print.IPrintAdapter;
+import android.print.IPrintClient;
+import android.print.IPrintSpoolerService;
+import android.print.IPrintSpoolerServiceCallbacks;
+import android.print.PrintAttributes;
+import android.print.PrintJobInfo;
+import android.util.Slog;
+import android.util.TimedRemoteCaller;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This represents the remote print spooler as a local object to the
+ * PrintManagerSerivce. It is responsible to connecting to the remove
+ * spooler if needed, to make the timed out remote calls, and to handle
+ * remove exceptions.
+ */
+final class RemoteSpooler implements ServiceConnection, DeathRecipient {
+
+ private static final String LOG_TAG = "Spooler";
+
+ private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
+
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+
+ private final Intent mIntent;
+
+ private final GetPrintJobsCaller mGetPrintJobsCaller = new GetPrintJobsCaller();
+
+ private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller();
+
+ private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller();
+
+ private final GetPrintJobCaller mGetPrintJobCaller = new GetPrintJobCaller();
+
+ private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
+
+ private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller();
+
+ private IPrintSpoolerService mRemoteInterface;
+
+ private int mUserId = UserHandle.USER_NULL;
+
+ public RemoteSpooler(Context context) {
+ mContext = context;
+ mIntent = new Intent();
+ mIntent.setComponent(new ComponentName("com.android.printspooler",
+ "com.android.printspooler.PrintSpoolerService"));
+ }
+
+ public List<PrintJobInfo> getPrintJobs(ComponentName componentName, int state, int appId,
+ int userId) {
+ try {
+ return mGetPrintJobsCaller.getPrintJobs(getRemoteInstance(userId),
+ componentName, state, appId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error getting print jobs!", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error getting print jobs!", te);
+ }
+ return null;
+ }
+
+ public PrintJobInfo createPrintJob(String printJobName, IPrintClient client,
+ IPrintAdapter printAdapter, PrintAttributes attributes, int appId, int userId) {
+ try {
+ return mCreatePrintJobCaller.createPrintJob(getRemoteInstance(userId),
+ printJobName, client, printAdapter, attributes, appId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error creating print job!", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error creating print job!", te);
+ }
+ return null;
+ }
+
+ public boolean cancelPrintJob(int printJobId, int appId, int userId) {
+ try {
+ return mCancelPrintJobCaller.cancelPrintJob(getRemoteInstance(userId),
+ printJobId, appId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error canceling print job!", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error canceling print job!", te);
+ }
+ return false;
+ }
+
+ public void writePrintJobData(ParcelFileDescriptor fd, int printJobId, int userId) {
+ try {
+ getRemoteInstance(userId).writePrintJobData(fd, printJobId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error writing print job data!", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error writing print job data!", te);
+ } finally {
+ // We passed the file descriptor across and now the other
+ // side is responsible to close it, so close the local copy.
+ try {
+ fd.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+
+ public PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId) {
+ try {
+ return mGetPrintJobCaller.getPrintJobInfo(getRemoteInstance(userId),
+ printJobId, appId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error getting print job!", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error getting print job!", te);
+ }
+ return null;
+ }
+
+ public boolean setPrintJobState(int printJobId, int state, int userId) {
+ try {
+ return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstance(userId),
+ printJobId, state);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error setting print job status!", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error setting print job status!", te);
+ }
+ return false;
+ }
+
+ public boolean setPrintJobTag(int printJobId, String tag, int userId) {
+ try {
+ return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstance(userId),
+ printJobId, tag);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error setting print job tag!", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error setting print job tag!", te);
+ }
+ return false;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ binderDied();
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ try {
+ service.linkToDeath(this, 0);
+ mRemoteInterface = IPrintSpoolerService.Stub.asInterface(service);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ }
+
+ private IPrintSpoolerService getRemoteInstance(int userId) throws TimeoutException {
+ synchronized (mLock) {
+ if (mRemoteInterface != null && mUserId == userId) {
+ return mRemoteInterface;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mUserId != UserHandle.USER_NULL && mUserId != userId) {
+ unbind();
+ }
+
+ mContext.bindServiceAsUser(mIntent, this,
+ Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT,
+ UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ final long startMillis = SystemClock.uptimeMillis();
+ while (true) {
+ if (mRemoteInterface != null) {
+ break;
+ }
+ final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+ final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis;
+ if (remainingMillis <= 0) {
+ throw new TimeoutException("Cannot get spooler!");
+ }
+ try {
+ mLock.wait(remainingMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+
+ mUserId = userId;
+
+ return mRemoteInterface;
+ }
+ }
+
+ public void unbind() {
+ synchronized (mLock) {
+ if (mRemoteInterface != null) {
+ mContext.unbindService(this);
+ mRemoteInterface = null;
+ mUserId = UserHandle.USER_NULL;
+ }
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ if (mRemoteInterface != null) {
+ mRemoteInterface.asBinder().unlinkToDeath(this, 0);
+ mRemoteInterface = null;
+ }
+ }
+ }
+
+ private final class GetPrintJobsCaller extends TimedRemoteCaller<List<PrintJobInfo>> {
+ private final IPrintSpoolerServiceCallbacks mCallback;
+
+ public GetPrintJobsCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onGetPrintJobsResult(List<PrintJobInfo> printJobs, int sequence) {
+ onRemoteMethodResult(printJobs, sequence);
+ }
+ };
+ }
+
+ public List<PrintJobInfo> getPrintJobs(IPrintSpoolerService target,
+ ComponentName componentName, int state, int appId)
+ throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.getPrintJobs(mCallback, componentName, state, appId, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private final class CreatePrintJobCaller extends TimedRemoteCaller<PrintJobInfo> {
+ private final IPrintSpoolerServiceCallbacks mCallback;
+
+ public CreatePrintJobCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
+ onRemoteMethodResult(printJob, sequence);
+ }
+ };
+ }
+
+ public PrintJobInfo createPrintJob(IPrintSpoolerService target, String printJobName,
+ IPrintClient client, IPrintAdapter printAdapter, PrintAttributes attributes,
+ int appId) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.createPrintJob(printJobName, client, printAdapter, attributes,
+ mCallback, appId, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private final class CancelPrintJobCaller extends TimedRemoteCaller<Boolean> {
+ private final IPrintSpoolerServiceCallbacks mCallback;
+
+ public CancelPrintJobCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onCancelPrintJobResult(boolean canceled, int sequence) {
+ onRemoteMethodResult(canceled, sequence);
+ }
+ };
+ }
+
+ public boolean cancelPrintJob(IPrintSpoolerService target, int printJobId,
+ int appId) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.cancelPrintJob(printJobId, mCallback, appId, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private final class GetPrintJobCaller extends TimedRemoteCaller<PrintJobInfo> {
+ private final IPrintSpoolerServiceCallbacks mCallback;
+
+ public GetPrintJobCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
+ onRemoteMethodResult(printJob, sequence);
+ }
+ };
+ }
+
+ public PrintJobInfo getPrintJobInfo(IPrintSpoolerService target, int printJobId,
+ int appId) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.getPrintJob(printJobId, mCallback, appId, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> {
+ private final IPrintSpoolerServiceCallbacks mCallback;
+
+ public SetPrintJobStateCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onSetPrintJobStateResult(boolean success, int sequence) {
+ onRemoteMethodResult(success, sequence);
+ }
+ };
+ }
+
+ public boolean setPrintJobState(IPrintSpoolerService target, int printJobId,
+ int status) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.setPrintJobState(printJobId, status, mCallback, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> {
+ private final IPrintSpoolerServiceCallbacks mCallback;
+
+ public SetPrintJobTagCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onSetPrintJobTagResult(boolean success, int sequence) {
+ onRemoteMethodResult(success, sequence);
+ }
+ };
+ }
+
+ public boolean setPrintJobTag(IPrintSpoolerService target, int printJobId,
+ String tag) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.setPrintJobTag(printJobId, tag, mCallback, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private abstract class BasePrintSpoolerServiceCallbacks
+ extends IPrintSpoolerServiceCallbacks.Stub {
+ @Override
+ public void onGetPrintJobsResult(List<PrintJobInfo> printJobIds, int sequence) {
+ /** do nothing */
+ }
+
+ @Override
+ public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
+ /** do nothing */
+ }
+
+ @Override
+ public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
+ /** do nothing */
+ }
+
+ @Override
+ public void onCancelPrintJobResult(boolean canceled, int sequence) {
+ /** do nothing */
+ }
+
+ @Override
+ public void onSetPrintJobStateResult(boolean success, int sequece) {
+ /** do nothing */
+ }
+
+ @Override
+ public void onSetPrintJobTagResult(boolean success, int sequence) {
+ /** do nothing */
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/usb/UsbDebuggingManager.java b/services/java/com/android/server/usb/UsbDebuggingManager.java
index 93d3114..ce953a4 100644
--- a/services/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/java/com/android/server/usb/UsbDebuggingManager.java
@@ -22,15 +22,14 @@
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Looper;
import android.os.Message;
-import android.os.Process;
import android.os.SystemClock;
import android.util.Slog;
import android.util.Base64;
+import com.android.server.FgThread;
import java.lang.Thread;
import java.io.File;
@@ -54,7 +53,6 @@
private final Context mContext;
private final Handler mHandler;
- private final HandlerThread mHandlerThread;
private Thread mThread;
private boolean mAdbEnabled = false;
private String mFingerprints;
@@ -62,9 +60,7 @@
private OutputStream mOutputStream = null;
public UsbDebuggingManager(Context context) {
- mHandlerThread = new HandlerThread("UsbDebuggingHandler");
- mHandlerThread.start();
- mHandler = new UsbDebuggingHandler(mHandlerThread.getLooper());
+ mHandler = new UsbDebuggingHandler(FgThread.get().getLooper());
mContext = context;
}
@@ -165,7 +161,7 @@
mAdbEnabled = true;
- mThread = new Thread(UsbDebuggingManager.this);
+ mThread = new Thread(UsbDebuggingManager.this, TAG);
mThread.start();
break;
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 87aa8cce..5a60de0 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -32,11 +32,9 @@
import android.hardware.usb.UsbManager;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
-import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UEventObserver;
@@ -48,6 +46,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.FgThread;
import java.io.File;
import java.io.FileDescriptor;
@@ -57,6 +56,7 @@
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Scanner;
@@ -158,11 +158,7 @@
readOemUsbOverrideConfig();
- // create a thread for our Handler
- HandlerThread thread = new HandlerThread("UsbDeviceManager",
- Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- mHandler = new UsbHandler(thread.getLooper());
+ mHandler = new UsbHandler(FgThread.get().getLooper());
if (nativeIsStartRequested()) {
if (DEBUG) Slog.d(TAG, "accessory attached at boot");
@@ -245,7 +241,7 @@
for (int i = 0; i < serialLength; i++) {
address[i % (ETH_ALEN - 1) + 1] ^= (int)serial.charAt(i);
}
- String addrString = String.format("%02X:%02X:%02X:%02X:%02X:%02X",
+ String addrString = String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
address[0], address[1], address[2], address[3], address[4], address[5]);
try {
FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString);
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index 6293dc6..3cccf1d 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -6,7 +6,6 @@
import android.util.Slog;
import android.util.TimeUtils;
import android.view.Display;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index fbb5013..8cc1d02 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -30,6 +30,10 @@
import android.view.WindowManager;
import java.io.PrintWriter;
+import java.util.ArrayList;
+
+class AppTokenList extends ArrayList<AppWindowToken> {
+}
/**
* Version of WindowToken that is specifically for a particular application (or
diff --git a/services/java/com/android/server/wm/DimLayer.java b/services/java/com/android/server/wm/DimLayer.java
index 9bd36ba..7839d06 100644
--- a/services/java/com/android/server/wm/DimLayer.java
+++ b/services/java/com/android/server/wm/DimLayer.java
@@ -3,10 +3,10 @@
package com.android.server.wm;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.os.SystemClock;
import android.util.Slog;
import android.view.DisplayInfo;
-import android.view.Surface;
import android.view.SurfaceControl;
import java.io.PrintWriter;
@@ -27,8 +27,11 @@
/** Last value passed to mDimSurface.setLayer() */
int mLayer = -1;
- /** Last values passed to mDimSurface.setSize() */
- int mLastDimWidth, mLastDimHeight;
+ /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
+ Rect mBounds = new Rect();
+
+ /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
+ Rect mLastBounds = new Rect();
/** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
private boolean mShowing = false;
@@ -117,6 +120,10 @@
}
}
+ void setBounds(Rect bounds) {
+ mBounds.set(bounds);
+ }
+
/**
* @param duration The time to test.
* @return True if the duration would lead to an earlier end to the current animation.
@@ -152,6 +159,7 @@
return;
}
+ /*
// Set surface size to screen size.
final DisplayInfo info = mDisplayContent.getDisplayInfo();
// Multiply by 1.5 so that rotating a frozen surface that includes this does not expose a
@@ -161,17 +169,17 @@
// back off position so 1/4 of Surface is before and 1/4 is after.
final float xPos = -1 * dw / 6;
final float yPos = -1 * dh / 6;
+ */
- if (mLastDimWidth != dw || mLastDimHeight != dh || mLayer != layer) {
+ if (!mLastBounds.equals(mBounds) || mLayer != layer) {
try {
- mDimSurface.setPosition(xPos, yPos);
- mDimSurface.setSize(dw, dh);
+ mDimSurface.setPosition(mBounds.left, mBounds.top);
+ mDimSurface.setSize(mBounds.width(), mBounds.height());
mDimSurface.setLayer(layer);
} catch (RuntimeException e) {
Slog.w(TAG, "Failure setting size or layer", e);
}
- mLastDimWidth = dw;
- mLastDimHeight = dh;
+ mLastBounds.set(mBounds);
mLayer = layer;
}
@@ -255,15 +263,16 @@
}
public void printTo(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("mDimSurface="); pw.println(mDimSurface);
- pw.print(prefix); pw.print(" mLayer="); pw.print(mLayer);
+ pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
+ pw.print(" mLayer="); pw.print(mLayer);
pw.print(" mAlpha="); pw.println(mAlpha);
- pw.print(prefix); pw.print("mLastDimWidth="); pw.print(mLastDimWidth);
- pw.print(" mLastDimWidth="); pw.println(mLastDimWidth);
- pw.print(prefix); pw.print("Last animation: mStartTime="); pw.print(mStartTime);
+ pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
+ pw.print(" mBounds="); pw.println(mBounds.toShortString());
+ pw.print(prefix); pw.print("Last animation: ");
pw.print(" mDuration="); pw.print(mDuration);
+ pw.print(" mStartTime="); pw.print(mStartTime);
pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
- pw.print(" mStartAlpha="); pw.println(mStartAlpha);
- pw.print(" mTargetAlpha="); pw.print(mTargetAlpha);
+ pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
+ pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
}
}
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 59e4b0e..feac370 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -16,6 +16,16 @@
package com.android.server.wm;
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.TAG;
+
+import android.app.ActivityManager.StackBoxInfo;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.Slog;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
@@ -61,12 +71,50 @@
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Display mDisplay;
+ Rect mBaseDisplayRect = new Rect();
+
// Accessed directly by all users.
boolean layoutNeeded;
int pendingLayoutChanges;
final boolean isDefaultDisplay;
/**
+ * Window tokens that are in the process of exiting, but still
+ * on screen for animations.
+ */
+ final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>();
+
+ /**
+ * Application tokens that are in the process of exiting, but still
+ * on screen for animations.
+ */
+ final AppTokenList mExitingAppTokens = new AppTokenList();
+
+ /** Array containing the home StackBox and possibly one more which would contain apps. Array
+ * is stored in display order with the current bottom stack at 0. */
+ private ArrayList<StackBox> mStackBoxes = new ArrayList<StackBox>();
+
+ /** True when the home StackBox is at the top of mStackBoxes, false otherwise. */
+ private TaskStack mHomeStack = null;
+
+ /** Sorted most recent at top, oldest at [0]. */
+ ArrayList<TaskStack> mStackHistory = new ArrayList<TaskStack>();
+
+ /** Detect user tapping outside of current focused stack bounds .*/
+ StackTapPointerEventListener mTapDetector;
+
+ /** Detect user tapping outside of current focused stack bounds .*/
+ Region mTouchExcludeRegion = new Region();
+
+ SparseArray<UserStacks> mUserStacks = new SparseArray<UserStacks>();
+
+ /** Save allocating when retrieving tasks */
+ ArrayList<Task> mTmpTasks = new ArrayList<Task>();
+
+ /** Save allocating when calculating rects */
+ Rect mTmpRect = new Rect();
+
+ /**
* @param display May not be null.
*/
DisplayContent(Display display) {
@@ -92,10 +140,305 @@
return mDisplayInfo;
}
+ /**
+ * Returns true if the specified UID has access to this display.
+ */
+ public boolean hasAccess(int uid) {
+ return mDisplay.hasAccess(uid);
+ }
+
+ boolean homeOnTop() {
+ return mStackBoxes.get(0).mStack != mHomeStack;
+ }
+
+ void moveStack(TaskStack stack, boolean toTop) {
+ mStackHistory.remove(stack);
+ mStackHistory.add(toTop ? mStackHistory.size() : 0, stack);
+ }
+
+ /**
+ * Retrieve the tasks on this display in stack order from the bottommost TaskStack up.
+ * @return All the Tasks, in order, on this display.
+ */
+ ArrayList<Task> getTasks() {
+ mTmpTasks.clear();
+ // First do the tasks belonging to other users.
+ final int numUserStacks = mUserStacks.size();
+ for (int i = 0; i < numUserStacks; ++i) {
+ UserStacks userStacks = mUserStacks.valueAt(i);
+ ArrayList<TaskStack> stacks = userStacks.mSavedStackHistory;
+ final int numStacks = stacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ TaskStack stack = stacks.get(stackNdx);
+ if (stack != mHomeStack) {
+ if (WindowManagerService.DEBUG_LAYERS) Slog.i(TAG, "getTasks: mStackHistory=" +
+ mStackHistory);
+ mTmpTasks.addAll(stack.getTasks());
+ }
+ }
+ }
+ // Now do the current user's tasks.
+ final int numStacks = mStackHistory.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ mTmpTasks.addAll(mStackHistory.get(stackNdx).getTasks());
+ }
+ if (WindowManagerService.DEBUG_LAYERS) Slog.i(TAG, "getTasks: mStackHistory=" +
+ mStackHistory);
+ return mTmpTasks;
+ }
+
+ TaskStack getHomeStack() {
+ return mHomeStack;
+ }
+
public void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
}
+ /** @return The number of tokens in all of the Tasks on this display. */
+ int numTokens() {
+ getTasks();
+ int count = 0;
+ for (int taskNdx = mTmpTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ count += mTmpTasks.get(taskNdx).mAppTokens.size();
+ }
+ return count;
+ }
+
+ /** Refer to {@link WindowManagerService#createStack(int, int, int, float)} */
+ TaskStack createStack(WindowManagerService service, int stackId, int relativeStackBoxId,
+ int position, float weight) {
+ TaskStack newStack = null;
+ if (DEBUG_STACK) Slog.d(TAG, "createStack: stackId=" + stackId + " relativeStackBoxId="
+ + relativeStackBoxId + " position=" + position + " weight=" + weight);
+ if (mStackBoxes.isEmpty()) {
+ if (stackId != HOME_STACK_ID) {
+ throw new IllegalArgumentException("createStack: First stackId not "
+ + HOME_STACK_ID);
+ }
+ StackBox newBox = new StackBox(service, this, null);
+ mStackBoxes.add(newBox);
+ newStack = new TaskStack(service, stackId, this);
+ newStack.mStackBox = newBox;
+ newBox.mStack = newStack;
+ mHomeStack = newStack;
+ } else {
+ int stackBoxNdx;
+ for (stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ final StackBox box = mStackBoxes.get(stackBoxNdx);
+ if (position == StackBox.TASK_STACK_GOES_OVER
+ || position == StackBox.TASK_STACK_GOES_UNDER) {
+ // Position indicates a new box is added at top level only.
+ if (box.contains(relativeStackBoxId)) {
+ StackBox newBox = new StackBox(service, this, null);
+ newStack = new TaskStack(service, stackId, this);
+ newStack.mStackBox = newBox;
+ newBox.mStack = newStack;
+ final int offset = position == StackBox.TASK_STACK_GOES_OVER ? 1 : 0;
+ if (DEBUG_STACK) Slog.d(TAG, "createStack: inserting stack at " +
+ (stackBoxNdx + offset));
+ mStackBoxes.add(stackBoxNdx + offset, newBox);
+ break;
+ }
+ } else {
+ // Remaining position values indicate a box must be split.
+ newStack = box.split(stackId, relativeStackBoxId, position, weight);
+ if (newStack != null) {
+ break;
+ }
+ }
+ }
+ if (stackBoxNdx < 0) {
+ throw new IllegalArgumentException("createStack: stackBoxId " + relativeStackBoxId
+ + " not found.");
+ }
+ }
+ if (newStack != null) {
+ layoutNeeded = true;
+ }
+ return newStack;
+ }
+
+ /** Refer to {@link WindowManagerService#resizeStackBox(int, float)} */
+ boolean resizeStack(int stackBoxId, float weight) {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ final StackBox box = mStackBoxes.get(stackBoxNdx);
+ if (box.resize(stackBoxId, weight)) {
+ layoutNeeded = true;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void addStackBox(StackBox box, boolean toTop) {
+ if (mStackBoxes.size() >= 2) {
+ throw new RuntimeException("addStackBox: Too many toplevel StackBoxes!");
+ }
+ mStackBoxes.add(toTop ? mStackBoxes.size() : 0, box);
+ }
+
+ void removeStackBox(StackBox box) {
+ if (DEBUG_STACK) Slog.d(TAG, "removeStackBox: box=" + box);
+ final TaskStack stack = box.mStack;
+ if (stack != null && stack.mStackId == HOME_STACK_ID) {
+ // Never delete the home stack, even if it is empty.
+ if (DEBUG_STACK) Slog.d(TAG, "removeStackBox: Not deleting home stack.");
+ return;
+ }
+ mStackBoxes.remove(box);
+ }
+
+ StackBoxInfo getStackBoxInfo(StackBox box) {
+ StackBoxInfo info = new StackBoxInfo();
+ info.stackBoxId = box.mStackBoxId;
+ info.weight = box.mWeight;
+ info.vertical = box.mVertical;
+ info.bounds = new Rect(box.mBounds);
+ if (box.mStack != null) {
+ info.stackId = box.mStack.mStackId;
+ // ActivityManagerService will fill in the StackInfo.
+ } else {
+ info.stackId = -1;
+ info.children = new StackBoxInfo[2];
+ info.children[0] = getStackBoxInfo(box.mFirst);
+ info.children[1] = getStackBoxInfo(box.mSecond);
+ }
+ return info;
+ }
+
+ ArrayList<StackBoxInfo> getStackBoxInfos() {
+ ArrayList<StackBoxInfo> list = new ArrayList<StackBoxInfo>();
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ list.add(getStackBoxInfo(mStackBoxes.get(stackBoxNdx)));
+ }
+ return list;
+ }
+
+ /**
+ * Move the home StackBox to the top or bottom of mStackBoxes. That is the only place
+ * it is allowed to be. This is a nop if the home StackBox is already in the correct position.
+ * @param toTop Move home to the top of mStackBoxes if true, to the bottom if false.
+ * @return true if a change was made, false otherwise.
+ */
+ boolean moveHomeStackBox(boolean toTop) {
+ if (DEBUG_STACK) Slog.d(TAG, "moveHomeStackBox: toTop=" + toTop);
+ switch (mStackBoxes.size()) {
+ case 0: throw new RuntimeException("moveHomeStackBox: No home StackBox!");
+ case 1: return false; // Only the home StackBox exists.
+ case 2:
+ if (homeOnTop() ^ toTop) {
+ mStackBoxes.add(mStackBoxes.remove(0));
+ return true;
+ }
+ return false;
+ default: throw new RuntimeException("moveHomeStackBox: Too many toplevel StackBoxes!");
+ }
+ }
+
+ /**
+ * Propagate the new bounds to all child stack boxes, applying weights as we move down.
+ * @param contentRect The bounds to apply at the top level.
+ */
+ boolean setStackBoxSize(Rect contentRect) {
+ boolean change = false;
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ change |= mStackBoxes.get(stackBoxNdx).setStackBoxSizes(contentRect, true);
+ }
+ return change;
+ }
+
+ Rect getStackBounds(int stackId) {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ Rect bounds = mStackBoxes.get(stackBoxNdx).getStackBounds(stackId);
+ if (bounds != null) {
+ return bounds;
+ }
+ }
+ // Not in the visible stacks, try the saved ones.
+ for (int userNdx = mUserStacks.size() - 1; userNdx >= 0; --userNdx) {
+ UserStacks userStacks = mUserStacks.valueAt(userNdx);
+ Rect bounds = userStacks.mSavedStackBox.getStackBounds(stackId);
+ if (bounds != null) {
+ return bounds;
+ }
+ }
+ return null;
+ }
+
+ int stackIdFromPoint(int x, int y) {
+ StackBox topBox = mStackBoxes.get(mStackBoxes.size() - 1);
+ return topBox.stackIdFromPoint(x, y);
+ }
+
+ void setTouchExcludeRegion(TaskStack focusedStack) {
+ mTouchExcludeRegion.set(mBaseDisplayRect);
+ WindowList windows = getWindowList();
+ for (int i = windows.size() - 1; i >= 0; --i) {
+ final WindowState win = windows.get(i);
+ final TaskStack stack = win.getStack();
+ if (win.isVisibleLw() && stack != null && stack != focusedStack) {
+ mTmpRect.set(win.mVisibleFrame);
+ mTmpRect.intersect(win.mVisibleInsets);
+ mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+ }
+ }
+ }
+
+ void switchUserStacks(int oldUserId, int newUserId) {
+ final WindowList windows = getWindowList();
+ for (int i = 0; i < windows.size(); i++) {
+ final WindowState win = windows.get(i);
+ if (win.isHiddenFromUserLocked()) {
+ if (DEBUG_VISIBILITY) Slog.w(TAG, "user changing " + newUserId + " hiding "
+ + win + ", attrs=" + win.mAttrs.type + ", belonging to "
+ + win.mOwnerUid);
+ win.hideLw(false);
+ }
+ }
+ // Clear the old user's non-home StackBox
+ mUserStacks.put(oldUserId, new UserStacks());
+ UserStacks userStacks = mUserStacks.get(newUserId);
+ if (userStacks != null) {
+ userStacks.restore();
+ mUserStacks.delete(newUserId);
+ }
+ }
+
+ void resetAnimationBackgroundAnimator() {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ mStackBoxes.get(stackBoxNdx).resetAnimationBackgroundAnimator();
+ }
+ }
+
+ boolean animateDimLayers() {
+ boolean result = false;
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ result |= mStackBoxes.get(stackBoxNdx).animateDimLayers();
+ }
+ return result;
+ }
+
+ void resetDimming() {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ mStackBoxes.get(stackBoxNdx).resetDimming();
+ }
+ }
+
+ boolean isDimming() {
+ boolean result = false;
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ result |= mStackBoxes.get(stackBoxNdx).isDimming();
+ }
+ return result;
+ }
+
+ void stopDimmingIfNeeded() {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ mStackBoxes.get(stackBoxNdx).stopDimmingIfNeeded();
+ }
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
final String subPrefix = " " + prefix;
@@ -119,7 +462,91 @@
pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
- pw.print(subPrefix); pw.print("layoutNeeded="); pw.print(layoutNeeded);
+ pw.print(subPrefix); pw.print("layoutNeeded="); pw.println(layoutNeeded);
+ for (int boxNdx = 0; boxNdx < mStackBoxes.size(); ++boxNdx) {
+ pw.print(prefix); pw.print("StackBox #"); pw.println(boxNdx);
+ mStackBoxes.get(boxNdx).dump(prefix + " ", pw);
+ }
+ int ndx = numTokens();
+ if (ndx > 0) {
+ pw.println();
+ pw.println(" Application tokens in Z order:");
+ getTasks();
+ for (int taskNdx = mTmpTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ AppTokenList tokens = mTmpTasks.get(taskNdx).mAppTokens;
+ for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
+ final AppWindowToken wtoken = tokens.get(tokenNdx);
+ pw.print(" App #"); pw.print(ndx--);
+ pw.print(' '); pw.print(wtoken); pw.println(":");
+ wtoken.dump(pw, " ");
+ }
+ }
+ }
+ if (mExitingTokens.size() > 0) {
+ pw.println();
+ pw.println(" Exiting tokens:");
+ for (int i=mExitingTokens.size()-1; i>=0; i--) {
+ WindowToken token = mExitingTokens.get(i);
+ pw.print(" Exiting #"); pw.print(i);
+ pw.print(' '); pw.print(token);
+ pw.println(':');
+ token.dump(pw, " ");
+ }
+ }
+ if (mExitingAppTokens.size() > 0) {
+ pw.println();
+ pw.println(" Exiting application tokens:");
+ for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
+ WindowToken token = mExitingAppTokens.get(i);
+ pw.print(" Exiting App #"); pw.print(i);
+ pw.print(' '); pw.print(token);
+ pw.println(':');
+ token.dump(pw, " ");
+ }
+ }
+ if (mUserStacks.size() > 0) {
+ pw.println();
+ pw.println(" Saved user stacks:");
+ for (int i = 0; i < mUserStacks.size(); ++i) {
+ UserStacks userStacks = mUserStacks.valueAt(i);
+ pw.print(" UserId="); pw.println(Integer.toHexString(mUserStacks.keyAt(i)));
+ pw.print(" StackHistory="); pw.println(userStacks.mSavedStackHistory);
+ pw.print(" StackBox="); userStacks.mSavedStackBox.dump(" ", pw);
+ }
+ }
pw.println();
}
+
+ private final class UserStacks {
+ final ArrayList<TaskStack> mSavedStackHistory;
+ StackBox mSavedStackBox;
+ int mBoxNdx;
+
+ public UserStacks() {
+ mSavedStackHistory = new ArrayList<TaskStack>(mStackHistory);
+ for (int stackNdx = mStackHistory.size() - 1; stackNdx >=0; --stackNdx) {
+ if (mStackHistory.get(stackNdx) != mHomeStack) {
+ mStackHistory.remove(stackNdx);
+ }
+ }
+ mSavedStackBox = null;
+ mBoxNdx = -1;
+ for (int boxNdx = mStackBoxes.size() - 1; boxNdx >= 0; --boxNdx) {
+ StackBox box = mStackBoxes.get(boxNdx);
+ if (box.mStack != mHomeStack) {
+ mSavedStackBox = box;
+ mBoxNdx = boxNdx;
+ mStackBoxes.remove(boxNdx);
+ break;
+ }
+ }
+ }
+
+ void restore() {
+ mStackHistory = mSavedStackHistory;
+ if (mBoxNdx >= 0) {
+ mStackBoxes.add(mBoxNdx, mSavedStackBox);
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/wm/FocusedStackFrame.java b/services/java/com/android/server/wm/FocusedStackFrame.java
new file mode 100644
index 0000000..9c18331
--- /dev/null
+++ b/services/java/com/android/server/wm/FocusedStackFrame.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerService.DEBUG_SURFACE_TRACE;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+import com.android.server.wm.WindowStateAnimator.SurfaceTrace;
+
+class FocusedStackFrame {
+ private static final String TAG = "FocusedStackFrame";
+ private static final int THICKNESS = 10;
+ private static final float ALPHA = 0.3f;
+
+ private final SurfaceControl mSurfaceControl;
+ private final Surface mSurface = new Surface();
+ private Rect mLastBounds = new Rect();
+ private Rect mBounds = new Rect();
+ private Rect mTmpDrawRect = new Rect();
+
+ public FocusedStackFrame(Display display, SurfaceSession session) {
+ SurfaceControl ctrl = null;
+ try {
+ if (DEBUG_SURFACE_TRACE) {
+ ctrl = new SurfaceTrace(session, "FocusedStackFrame",
+ 1, 1, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ } else {
+ ctrl = new SurfaceControl(session, "FocusedStackFrame",
+ 1, 1, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ }
+ ctrl.setLayerStack(display.getLayerStack());
+ ctrl.setAlpha(ALPHA);
+ mSurface.copyFrom(ctrl);
+ } catch (SurfaceControl.OutOfResourcesException e) {
+ }
+ mSurfaceControl = ctrl;
+ }
+
+ private void draw(Rect bounds, int color) {
+ if (DEBUG_STACK) Slog.i(TAG, "draw: bounds=" + bounds.toShortString() +
+ " color=" + Integer.toHexString(color));
+ mTmpDrawRect.set(bounds);
+ Canvas c = null;
+ try {
+ c = mSurface.lockCanvas(mTmpDrawRect);
+ } catch (IllegalArgumentException e) {
+ } catch (Surface.OutOfResourcesException e) {
+ }
+ if (c == null) {
+ return;
+ }
+
+ final int w = bounds.width();
+ final int h = bounds.height();
+
+ // Top
+ mTmpDrawRect.set(0, 0, w, THICKNESS);
+ c.clipRect(mTmpDrawRect, Region.Op.REPLACE);
+ c.drawColor(color);
+ // Left (not including Top or Bottom stripe).
+ mTmpDrawRect.set(0, THICKNESS, THICKNESS, h - THICKNESS);
+ c.clipRect(mTmpDrawRect, Region.Op.REPLACE);
+ c.drawColor(color);
+ // Right (not including Top or Bottom stripe).
+ mTmpDrawRect.set(w - THICKNESS, THICKNESS, w, h - THICKNESS);
+ c.clipRect(mTmpDrawRect, Region.Op.REPLACE);
+ c.drawColor(color);
+ // Bottom
+ mTmpDrawRect.set(0, h - THICKNESS, w, h);
+ c.clipRect(mTmpDrawRect, Region.Op.REPLACE);
+ c.drawColor(color);
+
+ mSurface.unlockCanvasAndPost(c);
+ }
+
+ private void positionSurface(Rect bounds) {
+ if (DEBUG_STACK) Slog.i(TAG, "positionSurface: bounds=" + bounds.toShortString());
+ mSurfaceControl.setSize(bounds.width(), bounds.height());
+ mSurfaceControl.setPosition(bounds.left, bounds.top);
+ }
+
+ // Note: caller responsible for being inside
+ // Surface.openTransaction() / closeTransaction()
+ public void setVisibility(boolean on) {
+ if (DEBUG_STACK) Slog.i(TAG, "setVisibility: on=" + on +
+ " mLastBounds=" + mLastBounds.toShortString() +
+ " mBounds=" + mBounds.toShortString());
+ if (mSurfaceControl == null) {
+ return;
+ }
+ if (on) {
+ if (!mLastBounds.equals(mBounds)) {
+ // Erase the previous rectangle.
+ positionSurface(mLastBounds);
+ draw(mLastBounds, Color.TRANSPARENT);
+ // Draw the latest rectangle.
+ positionSurface(mBounds);
+ draw(mBounds, Color.WHITE);
+ // Update the history.
+ mLastBounds.set(mBounds);
+ }
+ mSurfaceControl.show();
+ } else {
+ mSurfaceControl.hide();
+ }
+ }
+
+ public void setBounds(Rect bounds) {
+ if (DEBUG_STACK) Slog.i(TAG, "setBounds: bounds=" + bounds);
+ mBounds.set(bounds);
+ }
+
+ public void setLayer(int layer) {
+ mSurfaceControl.setLayer(layer);
+ }
+}
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index d966001..cacc723 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -31,7 +31,6 @@
import android.view.KeyEvent;
import android.view.WindowManager;
-import java.util.ArrayList;
import java.util.Arrays;
final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
@@ -68,6 +67,7 @@
*
* Called by the InputManager.
*/
+ @Override
public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
if (inputWindowHandle == null) {
return;
@@ -87,6 +87,7 @@
*
* Called by the InputManager.
*/
+ @Override
public long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle) {
AppWindowToken appWindowToken = null;
@@ -163,10 +164,20 @@
}
private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,
- final WindowState child, final int flags, final int type,
+ final WindowState child, int flags, final int type,
final boolean isVisible, final boolean hasFocus, final boolean hasWallpaper) {
// Add a window to our list of input windows.
inputWindowHandle.name = child.toString();
+ final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) == 0;
+ if (modal && child.mAppToken != null) {
+ // Limit the outer touch to the activity stack region.
+ flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ inputWindowHandle.touchableRegion.set(child.getStackBounds());
+ } else {
+ // Not modal or full screen modal
+ child.getTouchableRegion(inputWindowHandle.touchableRegion);
+ }
inputWindowHandle.layoutParamsFlags = flags;
inputWindowHandle.layoutParamsType = type;
inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
@@ -195,7 +206,6 @@
inputWindowHandle.scaleFactor = 1;
}
- child.getTouchableRegion(inputWindowHandle.touchableRegion);
addInputWindowHandleLw(inputWindowHandle);
}
@@ -259,10 +269,10 @@
// Skip this window because it cannot possibly receive input.
continue;
}
-
+
final int flags = child.mAttrs.flags;
final int type = child.mAttrs.type;
-
+
final boolean hasFocus = (child == mInputFocus);
final boolean isVisible = child.isVisibleLw();
final boolean hasWallpaper = (child == mService.mWallpaperTarget)
diff --git a/services/java/com/android/server/wm/PointerEventDispatcher.java b/services/java/com/android/server/wm/PointerEventDispatcher.java
new file mode 100644
index 0000000..6b0e4c9
--- /dev/null
+++ b/services/java/com/android/server/wm/PointerEventDispatcher.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy.PointerEventListener;
+
+import com.android.server.UiThread;
+
+import java.util.ArrayList;
+
+public class PointerEventDispatcher extends InputEventReceiver {
+ ArrayList<PointerEventListener> mListeners = new ArrayList<PointerEventListener>();
+ PointerEventListener[] mListenersArray = new PointerEventListener[0];
+
+ public PointerEventDispatcher(InputChannel inputChannel) {
+ super(inputChannel, UiThread.getHandler().getLooper());
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ try {
+ if (event instanceof MotionEvent
+ && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ final MotionEvent motionEvent = (MotionEvent)event;
+ PointerEventListener[] listeners;
+ synchronized (mListeners) {
+ if (mListenersArray == null) {
+ mListenersArray = new PointerEventListener[mListeners.size()];
+ mListeners.toArray(mListenersArray);
+ }
+ listeners = mListenersArray;
+ }
+ for (int i = 0; i < listeners.length; ++i) {
+ listeners[i].onPointerEvent(motionEvent);
+ }
+ }
+ } finally {
+ finishInputEvent(event, false);
+ }
+ }
+
+ /**
+ * Add the specified listener to the list.
+ * @param listener The listener to add.
+ */
+ public void registerInputEventListener(PointerEventListener listener) {
+ synchronized (mListeners) {
+ if (mListeners.contains(listener)) {
+ throw new IllegalStateException("registerInputEventListener: trying to register" +
+ listener + " twice.");
+ }
+ mListeners.add(listener);
+ mListenersArray = null;
+ }
+ }
+
+ /**
+ * Remove the specified listener from the list.
+ * @param listener The listener to remove.
+ */
+ public void unregisterInputEventListener(PointerEventListener listener) {
+ synchronized (mListeners) {
+ if (!mListeners.contains(listener)) {
+ throw new IllegalStateException("registerInputEventListener: " + listener +
+ " not registered.");
+ }
+ mListeners.remove(listener);
+ mListenersArray = null;
+ }
+ }
+}
diff --git a/services/java/com/android/server/wm/StackBox.java b/services/java/com/android/server/wm/StackBox.java
new file mode 100644
index 0000000..d352464
--- /dev/null
+++ b/services/java/com/android/server/wm/StackBox.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.graphics.Rect;
+import android.util.Slog;
+
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerService.TAG;
+
+import java.io.PrintWriter;
+
+public class StackBox {
+ /** Used with {@link WindowManagerService#createStack}. Dependent on Configuration LTR/RTL. */
+ public static final int TASK_STACK_GOES_BEFORE = 0;
+ /** Used with {@link WindowManagerService#createStack}. Dependent on Configuration LTR/RTL. */
+ public static final int TASK_STACK_GOES_AFTER = 1;
+ /** Used with {@link WindowManagerService#createStack}. Horizontal to left of. */
+ public static final int TASK_STACK_TO_LEFT_OF = 2;
+ /** Used with {@link WindowManagerService#createStack}. Horizontal to right of. */
+ public static final int TASK_STACK_TO_RIGHT_OF = 3;
+ /** Used with {@link WindowManagerService#createStack}. Vertical: lower t/b Rect values. */
+ public static final int TASK_STACK_GOES_ABOVE = 4;
+ /** Used with {@link WindowManagerService#createStack}. Vertical: higher t/b Rect values. */
+ public static final int TASK_STACK_GOES_BELOW = 5;
+ /** Used with {@link WindowManagerService#createStack}. Put on a higher layer on display. */
+ public static final int TASK_STACK_GOES_OVER = 6;
+ /** Used with {@link WindowManagerService#createStack}. Put on a lower layer on display. */
+ public static final int TASK_STACK_GOES_UNDER = 7;
+
+ static int sCurrentBoxId = 0;
+
+ /** Unique id for this box */
+ final int mStackBoxId;
+
+ /** The service */
+ final WindowManagerService mService;
+
+ /** The display this box sits in. */
+ final DisplayContent mDisplayContent;
+
+ /** Non-null indicates this is mFirst or mSecond of a parent StackBox. Null indicates this
+ * is this entire size of mDisplayContent. */
+ StackBox mParent;
+
+ /** First child, this is null exactly when mStack is non-null. */
+ StackBox mFirst;
+
+ /** Second child, this is null exactly when mStack is non-null. */
+ StackBox mSecond;
+
+ /** Stack of Tasks, this is null exactly when mFirst and mSecond are non-null. */
+ TaskStack mStack;
+
+ /** Content limits relative to the DisplayContent this sits in. */
+ Rect mBounds = new Rect();
+
+ /** Relative orientation of mFirst and mSecond. */
+ boolean mVertical;
+
+ /** Fraction of mBounds to devote to mFirst, remainder goes to mSecond */
+ float mWeight;
+
+ /** Dirty flag. Something inside this or some descendant of this has changed. */
+ boolean layoutNeeded;
+
+ /** True if this StackBox sits below the Status Bar. */
+ boolean mUnderStatusBar;
+
+ /** Used to keep from reallocating a temporary Rect for propagating bounds to child boxes */
+ Rect mTmpRect = new Rect();
+
+ StackBox(WindowManagerService service, DisplayContent displayContent, StackBox parent) {
+ synchronized (StackBox.class) {
+ mStackBoxId = sCurrentBoxId++;
+ }
+
+ mService = service;
+ mDisplayContent = displayContent;
+ mParent = parent;
+ }
+
+ /** Propagate #layoutNeeded bottom up. */
+ void makeDirty() {
+ layoutNeeded = true;
+ if (mParent != null) {
+ mParent.makeDirty();
+ }
+ }
+
+ /**
+ * Determine if a particular StackBox is this one or a descendant of this one.
+ * @param stackBoxId The StackBox being searched for.
+ * @return true if the specified StackBox matches this or one of its descendants.
+ */
+ boolean contains(int stackBoxId) {
+ return mStackBoxId == stackBoxId || mFirst.contains(stackBoxId)
+ || mSecond.contains(stackBoxId);
+ }
+
+ /**
+ * Return the stackId of the stack that intersects the passed point.
+ * @param x coordinate of point.
+ * @param y coordinate of point.
+ * @return -1 if point is outside of mBounds, otherwise the stackId of the containing stack.
+ */
+ int stackIdFromPoint(int x, int y) {
+ if (!mBounds.contains(x, y)) {
+ return -1;
+ }
+ if (mStack != null) {
+ return mStack.mStackId;
+ }
+ int stackId = mFirst.stackIdFromPoint(x, y);
+ if (stackId >= 0) {
+ return stackId;
+ }
+ return mSecond.stackIdFromPoint(x, y);
+ }
+
+ /** Determine if this StackBox is the first child or second child.
+ * @return true if this is the first child.
+ */
+ boolean isFirstChild() {
+ return mParent != null && mParent.mFirst == this;
+ }
+
+ /** Returns the bounds of the specified TaskStack if it is contained in this StackBox.
+ * @param stackId the TaskStack to find the bounds of.
+ * @return a new Rect with the bounds of stackId if it is within this StackBox, null otherwise.
+ */
+ Rect getStackBounds(int stackId) {
+ if (mStack != null) {
+ return mStack.mStackId == stackId ? new Rect(mBounds) : null;
+ }
+ Rect bounds = mFirst.getStackBounds(stackId);
+ if (bounds != null) {
+ return bounds;
+ }
+ return mSecond.getStackBounds(stackId);
+ }
+
+ /**
+ * Create a new TaskStack relative to a specified one by splitting the StackBox containing
+ * the specified TaskStack into two children. The size and position each of the new StackBoxes
+ * is determined by the passed parameters.
+ * @param stackId The id of the new TaskStack to create.
+ * @param relativeStackBoxId The id of the StackBox to place the new TaskStack next to.
+ * @param position One of the static TASK_STACK_GOES_xxx positions defined in this class.
+ * @param weight The percentage size of the parent StackBox to devote to the new TaskStack.
+ * @return The new TaskStack.
+ */
+ TaskStack split(int stackId, int relativeStackBoxId, int position, float weight) {
+ if (mStackBoxId != relativeStackBoxId) {
+ // This is not the targeted StackBox.
+ if (mStack != null) {
+ return null;
+ }
+ // Propagate the split to see if the targeted StackBox is in either sub box.
+ TaskStack stack = mFirst.split(stackId, relativeStackBoxId, position, weight);
+ if (stack != null) {
+ return stack;
+ }
+ return mSecond.split(stackId, relativeStackBoxId, position, weight);
+ }
+
+ // Found it!
+ TaskStack stack = new TaskStack(mService, stackId, mDisplayContent);
+ TaskStack firstStack;
+ TaskStack secondStack;
+ if (position == TASK_STACK_GOES_BEFORE) {
+ // TODO: Test Configuration here for LTR/RTL.
+ position = TASK_STACK_TO_LEFT_OF;
+ } else if (position == TASK_STACK_GOES_AFTER) {
+ // TODO: Test Configuration here for LTR/RTL.
+ position = TASK_STACK_TO_RIGHT_OF;
+ }
+ switch (position) {
+ default:
+ case TASK_STACK_TO_LEFT_OF:
+ case TASK_STACK_TO_RIGHT_OF:
+ mVertical = false;
+ if (position == TASK_STACK_TO_LEFT_OF) {
+ mWeight = weight;
+ firstStack = stack;
+ secondStack = mStack;
+ } else {
+ mWeight = 1.0f - weight;
+ firstStack = mStack;
+ secondStack = stack;
+ }
+ break;
+ case TASK_STACK_GOES_ABOVE:
+ case TASK_STACK_GOES_BELOW:
+ mVertical = true;
+ if (position == TASK_STACK_GOES_ABOVE) {
+ mWeight = weight;
+ firstStack = stack;
+ secondStack = mStack;
+ } else {
+ mWeight = 1.0f - weight;
+ firstStack = mStack;
+ secondStack = stack;
+ }
+ break;
+ }
+
+ mFirst = new StackBox(mService, mDisplayContent, this);
+ firstStack.mStackBox = mFirst;
+ mFirst.mStack = firstStack;
+
+ mSecond = new StackBox(mService, mDisplayContent, this);
+ secondStack.mStackBox = mSecond;
+ mSecond.mStack = secondStack;
+
+ mStack = null;
+ return stack;
+ }
+
+ /** Return the stackId of the first mFirst StackBox with a non-null mStack */
+ int getStackId() {
+ if (mStack != null) {
+ return mStack.mStackId;
+ }
+ return mFirst.getStackId();
+ }
+
+ /** Remove this box and propagate its sibling's content up to their parent.
+ * @return The first stackId of the resulting StackBox. */
+ int remove() {
+ if (mStack != null) {
+ if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: removing stackId=" + mStack.mStackId);
+ mDisplayContent.mStackHistory.remove(mStack);
+ }
+ mDisplayContent.layoutNeeded = true;
+
+ if (mParent == null) {
+ // This is the top-plane stack.
+ if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: removing top plane.");
+ mDisplayContent.removeStackBox(this);
+ return HOME_STACK_ID;
+ }
+
+ StackBox sibling = isFirstChild() ? mParent.mSecond : mParent.mFirst;
+ StackBox grandparent = mParent.mParent;
+ sibling.mParent = grandparent;
+ if (grandparent == null) {
+ // mParent is a top-plane stack. Now sibling will be.
+ if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent null");
+ mDisplayContent.removeStackBox(mParent);
+ mDisplayContent.addStackBox(sibling, true);
+ } else {
+ if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent getting sibling");
+ if (mParent.isFirstChild()) {
+ grandparent.mFirst = sibling;
+ } else {
+ grandparent.mSecond = sibling;
+ }
+ }
+ return sibling.getStackId();
+ }
+
+ boolean resize(int stackBoxId, float weight) {
+ if (mStackBoxId != stackBoxId) {
+ return mStack == null &&
+ (mFirst.resize(stackBoxId, weight) || mSecond.resize(stackBoxId, weight));
+ }
+ // Don't change weight on topmost stack.
+ if (mParent != null) {
+ mParent.mWeight = isFirstChild() ? weight : 1.0f - weight;
+ }
+ return true;
+ }
+
+ /** If this is a terminal StackBox (contains a TaskStack) set the bounds.
+ * @param bounds The rectangle to set the bounds to.
+ * @param underStatusBar True if the StackBox is directly below the Status Bar.
+ * @return True if the bounds changed, false otherwise. */
+ boolean setStackBoxSizes(Rect bounds, boolean underStatusBar) {
+ boolean change = false;
+ if (mUnderStatusBar != underStatusBar) {
+ change = true;
+ mUnderStatusBar = underStatusBar;
+ }
+ if (mStack != null) {
+ change |= !mBounds.equals(bounds);
+ if (change) {
+ mBounds.set(bounds);
+ mStack.setBounds(bounds, underStatusBar);
+ }
+ } else {
+ mTmpRect.set(bounds);
+ if (mVertical) {
+ final int height = bounds.height();
+ int firstHeight = (int)(height * mWeight);
+ mTmpRect.bottom = bounds.top + firstHeight;
+ change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar);
+ mTmpRect.top = mTmpRect.bottom;
+ mTmpRect.bottom = bounds.top + height;
+ change |= mSecond.setStackBoxSizes(mTmpRect, false);
+ } else {
+ final int width = bounds.width();
+ int firstWidth = (int)(width * mWeight);
+ mTmpRect.right = bounds.left + firstWidth;
+ change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar);
+ mTmpRect.left = mTmpRect.right;
+ mTmpRect.right = bounds.left + width;
+ change |= mSecond.setStackBoxSizes(mTmpRect, underStatusBar);
+ }
+ }
+ return change;
+ }
+
+ void resetAnimationBackgroundAnimator() {
+ if (mStack != null) {
+ mStack.resetAnimationBackgroundAnimator();
+ return;
+ }
+ mFirst.resetAnimationBackgroundAnimator();
+ mSecond.resetAnimationBackgroundAnimator();
+ }
+
+ boolean animateDimLayers() {
+ if (mStack != null) {
+ return mStack.animateDimLayers();
+ }
+ boolean result = mFirst.animateDimLayers();
+ result |= mSecond.animateDimLayers();
+ return result;
+ }
+
+ void resetDimming() {
+ if (mStack != null) {
+ mStack.resetDimmingTag();
+ return;
+ }
+ mFirst.resetDimming();
+ mSecond.resetDimming();
+ }
+
+ boolean isDimming() {
+ if (mStack != null) {
+ return mStack.isDimming();
+ }
+ boolean result = mFirst.isDimming();
+ result |= mSecond.isDimming();
+ return result;
+ }
+
+ void stopDimmingIfNeeded() {
+ if (mStack != null) {
+ mStack.stopDimmingIfNeeded();
+ return;
+ }
+ mFirst.stopDimmingIfNeeded();
+ mSecond.stopDimmingIfNeeded();
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mParent="); pw.println(mParent);
+ pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
+ pw.print(" mVertical="); pw.print(mVertical);
+ pw.print(" layoutNeeded="); pw.println(layoutNeeded);
+ if (mFirst != null) {
+ pw.print(prefix); pw.print("mFirst="); pw.println(System.identityHashCode(mFirst));
+ mFirst.dump(prefix + " ", pw);
+ pw.print(prefix); pw.print("mSecond="); pw.println(System.identityHashCode(mSecond));
+ mSecond.dump(prefix + " ", pw);
+ } else {
+ pw.print(prefix); pw.print("mStack="); pw.println(mStack);
+ mStack.dump(prefix + " ", pw);
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (mStack != null) {
+ return "Box{" + hashCode() + " stack=" + mStack.mStackId + "}";
+ }
+ return "Box{" + hashCode() + " parent=" + System.identityHashCode(mParent)
+ + " first=" + System.identityHashCode(mFirst)
+ + " second=" + System.identityHashCode(mSecond) + "}";
+ }
+}
diff --git a/services/java/com/android/server/wm/StackTapPointerEventListener.java b/services/java/com/android/server/wm/StackTapPointerEventListener.java
new file mode 100644
index 0000000..19d8ab3
--- /dev/null
+++ b/services/java/com/android/server/wm/StackTapPointerEventListener.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.graphics.Region;
+import android.view.DisplayInfo;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy.PointerEventListener;
+
+import com.android.server.wm.WindowManagerService.H;
+
+public class StackTapPointerEventListener implements PointerEventListener {
+ private static final int TAP_TIMEOUT_MSEC = 300;
+ private static final float TAP_MOTION_SLOP_INCHES = 0.125f;
+
+ private final int mMotionSlop;
+ private float mDownX;
+ private float mDownY;
+ private int mPointerId;
+ final private Region mTouchExcludeRegion;
+ private final WindowManagerService mService;
+ private final DisplayContent mDisplayContent;
+
+ public StackTapPointerEventListener(WindowManagerService service,
+ DisplayContent displayContent) {
+ mService = service;
+ mDisplayContent = displayContent;
+ mTouchExcludeRegion = displayContent.mTouchExcludeRegion;
+ DisplayInfo info = displayContent.getDisplayInfo();
+ mMotionSlop = (int)(info.logicalDensityDpi * TAP_MOTION_SLOP_INCHES);
+ }
+
+ @Override
+ public void onPointerEvent(MotionEvent motionEvent) {
+ final int action = motionEvent.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ mPointerId = motionEvent.getPointerId(0);
+ mDownX = motionEvent.getX();
+ mDownY = motionEvent.getY();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mPointerId >= 0) {
+ int index = motionEvent.findPointerIndex(mPointerId);
+ if ((motionEvent.getEventTime() - motionEvent.getDownTime()) > TAP_TIMEOUT_MSEC
+ || (motionEvent.getX(index) - mDownX) > mMotionSlop
+ || (motionEvent.getY(index) - mDownY) > mMotionSlop) {
+ mPointerId = -1;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP: {
+ int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ // Extract the index of the pointer that left the touch sensor
+ if (mPointerId == motionEvent.getPointerId(index)) {
+ final int x = (int)motionEvent.getX(index);
+ final int y = (int)motionEvent.getY(index);
+ if ((motionEvent.getEventTime() - motionEvent.getDownTime())
+ < TAP_TIMEOUT_MSEC
+ && (x - mDownX) < mMotionSlop && (y - mDownY) < mMotionSlop
+ && !mTouchExcludeRegion.contains(x, y)) {
+ mService.mH.obtainMessage(H.TAP_OUTSIDE_STACK, x, y,
+ mDisplayContent).sendToTarget();
+ }
+ mPointerId = -1;
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/wm/StartingData.java b/services/java/com/android/server/wm/StartingData.java
index 46bb480..7115b0f 100644
--- a/services/java/com/android/server/wm/StartingData.java
+++ b/services/java/com/android/server/wm/StartingData.java
@@ -25,17 +25,19 @@
final CharSequence nonLocalizedLabel;
final int labelRes;
final int icon;
+ final int logo;
final int windowFlags;
StartingData(String _pkg, int _theme, CompatibilityInfo _compatInfo,
CharSequence _nonLocalizedLabel,
- int _labelRes, int _icon, int _windowFlags) {
+ int _labelRes, int _icon, int _logo, int _windowFlags) {
pkg = _pkg;
theme = _theme;
compatInfo = _compatInfo;
nonLocalizedLabel = _nonLocalizedLabel;
labelRes = _labelRes;
icon = _icon;
+ logo = _logo;
windowFlags = _windowFlags;
}
}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/Task.java b/services/java/com/android/server/wm/Task.java
new file mode 100644
index 0000000..88eb96e
--- /dev/null
+++ b/services/java/com/android/server/wm/Task.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+class Task {
+// private final String TAG = "TaskGroup";
+ TaskStack mStack;
+ final AppTokenList mAppTokens = new AppTokenList();
+ final int taskId;
+
+ Task(AppWindowToken wtoken, TaskStack stack) {
+ taskId = wtoken.groupId;
+ mAppTokens.add(wtoken);
+ mStack = stack;
+ }
+
+ DisplayContent getDisplayContent() {
+ return mStack.getDisplayContent();
+ }
+
+ void addAppToken(int addPos, AppWindowToken wtoken) {
+ mAppTokens.add(addPos, wtoken);
+ }
+
+ boolean removeAppToken(AppWindowToken wtoken) {
+ mAppTokens.remove(wtoken);
+ if (mAppTokens.size() == 0) {
+ mStack.removeTask(this);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "{taskId=" + taskId + " appTokens=" + mAppTokens + "}";
+ }
+}
diff --git a/services/java/com/android/server/wm/TaskGroup.java b/services/java/com/android/server/wm/TaskGroup.java
new file mode 100644
index 0000000..1f1dd58
--- /dev/null
+++ b/services/java/com/android/server/wm/TaskGroup.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.view.IApplicationToken;
+
+import java.util.ArrayList;
+
+public class TaskGroup {
+ public int taskId = -1;
+ public ArrayList<IApplicationToken> tokens = new ArrayList<IApplicationToken>();
+
+ @Override
+ public String toString() {
+ return "id=" + taskId + " tokens=" + tokens;
+ }
+}
diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java
new file mode 100644
index 0000000..b43a7a1
--- /dev/null
+++ b/services/java/com/android/server/wm/TaskStack.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
+import static com.android.server.wm.WindowManagerService.TAG;
+
+import android.graphics.Rect;
+import android.os.Debug;
+import android.util.Slog;
+import android.util.TypedValue;
+
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class TaskStack {
+ /** Amount of time in milliseconds to animate the dim surface from one value to another,
+ * when no window animation is driving it. */
+ private static final int DEFAULT_DIM_DURATION = 200;
+
+ /** Unique identifier */
+ final int mStackId;
+
+ /** The service */
+ private final WindowManagerService mService;
+
+ /** The display this stack sits under. */
+ private final DisplayContent mDisplayContent;
+
+ /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
+ * mTaskHistory in the ActivityStack with the same mStackId */
+ private ArrayList<Task> mTasks = new ArrayList<Task>();
+
+ /** The StackBox this sits in. */
+ StackBox mStackBox;
+
+ /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
+ final DimLayer mDimLayer;
+
+ /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
+ WindowStateAnimator mDimWinAnimator;
+
+ /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
+ final DimLayer mAnimationBackgroundSurface;
+
+ /** The particular window with an Animation with non-zero background color. */
+ WindowStateAnimator mAnimationBackgroundAnimator;
+
+ /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
+ * then stop any dimming. */
+ boolean mDimmingTag;
+
+ TaskStack(WindowManagerService service, int stackId, DisplayContent displayContent) {
+ mService = service;
+ mStackId = stackId;
+ mDisplayContent = displayContent;
+ final int displayId = displayContent.getDisplayId();
+ mDimLayer = new DimLayer(service, displayId);
+ mAnimationBackgroundSurface = new DimLayer(service, displayId);
+ }
+
+ DisplayContent getDisplayContent() {
+ return mDisplayContent;
+ }
+
+ ArrayList<Task> getTasks() {
+ return mTasks;
+ }
+
+ boolean isHomeStack() {
+ return mStackId == HOME_STACK_ID;
+ }
+
+ /**
+ * Put a Task in this stack. Used for adding and moving.
+ * @param task The task to add.
+ * @param toTop Whether to add it to the top or bottom.
+ */
+ boolean addTask(Task task, boolean toTop) {
+ if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop);
+ mStackBox.makeDirty();
+ mTasks.add(toTop ? mTasks.size() : 0, task);
+ task.mStack = this;
+ return mDisplayContent.moveHomeStackBox(mStackId == HOME_STACK_ID);
+ }
+
+ boolean moveTaskToTop(Task task) {
+ if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
+ + Debug.getCallers(6));
+ mTasks.remove(task);
+ return addTask(task, true);
+ }
+
+ boolean moveTaskToBottom(Task task) {
+ if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
+ mTasks.remove(task);
+ return addTask(task, false);
+ }
+
+ /**
+ * Delete a Task from this stack. If it is the last Task in the stack, remove this stack from
+ * its parent StackBox and merge the parent.
+ * @param task The Task to delete.
+ */
+ void removeTask(Task task) {
+ if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
+ mStackBox.makeDirty();
+ mTasks.remove(task);
+ }
+
+ int remove() {
+ mAnimationBackgroundSurface.destroySurface();
+ mDimLayer.destroySurface();
+ return mStackBox.remove();
+ }
+
+ void resetAnimationBackgroundAnimator() {
+ mAnimationBackgroundAnimator = null;
+ mAnimationBackgroundSurface.hide();
+ }
+
+ private long getDimBehindFadeDuration(long duration) {
+ TypedValue tv = new TypedValue();
+ mService.mContext.getResources().getValue(
+ com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
+ if (tv.type == TypedValue.TYPE_FRACTION) {
+ duration = (long)tv.getFraction(duration, duration);
+ } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
+ duration = tv.data;
+ }
+ return duration;
+ }
+
+ boolean animateDimLayers() {
+ final int dimLayer;
+ final float dimAmount;
+ if (mDimWinAnimator == null) {
+ dimLayer = mDimLayer.getLayer();
+ dimAmount = 0;
+ } else {
+ dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
+ dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
+ }
+ final float targetAlpha = mDimLayer.getTargetAlpha();
+ if (targetAlpha != dimAmount) {
+ if (mDimWinAnimator == null) {
+ mDimLayer.hide(DEFAULT_DIM_DURATION);
+ } else {
+ long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
+ ? mDimWinAnimator.mAnimation.computeDurationHint()
+ : DEFAULT_DIM_DURATION;
+ if (targetAlpha > dimAmount) {
+ duration = getDimBehindFadeDuration(duration);
+ }
+ mDimLayer.show(dimLayer, dimAmount, duration);
+ }
+ } else if (mDimLayer.getLayer() != dimLayer) {
+ mDimLayer.setLayer(dimLayer);
+ }
+ if (mDimLayer.isAnimating()) {
+ if (!mService.okToDisplay()) {
+ // Jump to the end of the animation.
+ mDimLayer.show();
+ } else {
+ return mDimLayer.stepAnimation();
+ }
+ }
+ return false;
+ }
+
+ void resetDimmingTag() {
+ mDimmingTag = false;
+ }
+
+ void setDimmingTag() {
+ mDimmingTag = true;
+ }
+
+ boolean testDimmingTag() {
+ return mDimmingTag;
+ }
+
+ boolean isDimming() {
+ return mDimLayer.isDimming();
+ }
+
+ boolean isDimming(WindowStateAnimator winAnimator) {
+ return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
+ }
+
+ void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
+ // Only set dim params on the highest dimmed layer.
+ final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
+ // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
+ if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
+ || !existingDimWinAnimator.mSurfaceShown
+ || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
+ mDimWinAnimator = newWinAnimator;
+ }
+ }
+
+ void stopDimmingIfNeeded() {
+ if (!mDimmingTag && isDimming()) {
+ mDimWinAnimator = null;
+ }
+ }
+
+ void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
+ int animLayer = winAnimator.mAnimLayer;
+ if (mAnimationBackgroundAnimator == null
+ || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
+ mAnimationBackgroundAnimator = winAnimator;
+ animLayer = mService.adjustAnimationBackground(winAnimator);
+ mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
+ ((color >> 24) & 0xff) / 255f, 0);
+ }
+ }
+
+ void setBounds(Rect bounds, boolean underStatusBar) {
+ mDimLayer.setBounds(bounds);
+ mAnimationBackgroundSurface.setBounds(bounds);
+
+ final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
+ for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
+ for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+ final WindowState win = windows.get(winNdx);
+ if (!resizingWindows.contains(win)) {
+ resizingWindows.add(win);
+ }
+ win.mUnderStatusBar = underStatusBar;
+ }
+ }
+ }
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
+ for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
+ pw.print(prefix); pw.println(mTasks.get(taskNdx));
+ }
+ if (mAnimationBackgroundSurface.isDimming()) {
+ pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
+ mAnimationBackgroundSurface.printTo(prefix + " ", pw);
+ }
+ if (mDimLayer.isDimming()) {
+ pw.print(prefix); pw.println("mDimLayer:");
+ mDimLayer.printTo(prefix, pw);
+ pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
+ }
+}
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 054a075..3fc3ac6 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -19,9 +19,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
-import android.util.TypedValue;
import android.view.Display;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
@@ -39,10 +37,6 @@
public class WindowAnimator {
private static final String TAG = "WindowAnimator";
- /** Amount of time in milliseconds to animate the dim surface from one value to another,
- * when no window animation is driving it. */
- static final int DEFAULT_DIM_DURATION = 200;
-
final WindowManagerService mService;
final Context mContext;
final WindowManagerPolicy mPolicy;
@@ -51,8 +45,6 @@
final Runnable mAnimationRunnable;
- int mAdjResult;
-
/** Time of current animation step. Reset on each iteration */
long mCurrentTime;
@@ -73,7 +65,7 @@
Object mLastWindowFreezeSource;
SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators =
- new SparseArray<WindowAnimator.DisplayContentsAnimator>();
+ new SparseArray<WindowAnimator.DisplayContentsAnimator>(2);
boolean mInitialized = false;
@@ -121,18 +113,10 @@
void removeDisplayLocked(final int displayId) {
final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
if (displayAnimator != null) {
- if (displayAnimator.mWindowAnimationBackgroundSurface != null) {
- displayAnimator.mWindowAnimationBackgroundSurface.destroySurface();
- displayAnimator.mWindowAnimationBackgroundSurface = null;
- }
if (displayAnimator.mScreenRotationAnimation != null) {
displayAnimator.mScreenRotationAnimation.kill();
displayAnimator.mScreenRotationAnimation = null;
}
- if (displayAnimator.mDimAnimator != null) {
- displayAnimator.mDimAnimator.destroySurface();
- displayAnimator.mDimAnimator = null;
- }
}
mDisplayContentsAnimators.delete(displayId);
@@ -173,28 +157,33 @@
}
}
- private void updateAppWindowsLocked() {
- int i;
- final ArrayList<AppWindowToken> appTokens = mService.mAnimatingAppTokens;
- final int NAT = appTokens.size();
- for (i=0; i<NAT; i++) {
- final AppWindowAnimator appAnimator = appTokens.get(i).mAppAnimator;
- final boolean wasAnimating = appAnimator.animation != null
- && appAnimator.animation != AppWindowAnimator.sDummyAnimation;
- if (appAnimator.stepAnimationLocked(mCurrentTime)) {
- mAnimating = true;
- } else if (wasAnimating) {
- // stopped animating, do one more pass through the layout
- setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
- "appToken " + appAnimator.mAppToken + " done");
- if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
- "updateWindowsApps...: done animating " + appAnimator.mAppToken);
+ private void updateAppWindowsLocked(int displayId) {
+ final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
+ final ArrayList<Task> tasks = displayContent.getTasks();
+ final int numTasks = tasks.size();
+ for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
+ final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ final int numTokens = tokens.size();
+ for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
+ final AppWindowAnimator appAnimator = tokens.get(tokenNdx).mAppAnimator;
+ final boolean wasAnimating = appAnimator.animation != null
+ && appAnimator.animation != AppWindowAnimator.sDummyAnimation;
+ if (appAnimator.stepAnimationLocked(mCurrentTime)) {
+ mAnimating = true;
+ } else if (wasAnimating) {
+ // stopped animating, do one more pass through the layout
+ setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "appToken " + appAnimator.mAppToken + " done");
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
+ "updateWindowsApps...: done animating " + appAnimator.mAppToken);
+ }
}
}
- final int NEAT = mService.mExitingAppTokens.size();
- for (i=0; i<NEAT; i++) {
- final AppWindowAnimator appAnimator = mService.mExitingAppTokens.get(i).mAppAnimator;
+ final AppTokenList exitingAppTokens = displayContent.mExitingAppTokens;
+ final int NEAT = exitingAppTokens.size();
+ for (int i = 0; i < NEAT; i++) {
+ final AppWindowAnimator appAnimator = exitingAppTokens.get(i).mAppAnimator;
final boolean wasAnimating = appAnimator.animation != null
&& appAnimator.animation != AppWindowAnimator.sDummyAnimation;
if (appAnimator.stepAnimationLocked(mCurrentTime)) {
@@ -300,7 +289,8 @@
wallpaperInUnForceHiding = true;
}
}
- if (mCurrentFocus == null || mCurrentFocus.mLayer < win.mLayer) {
+ final WindowState currentFocus = mService.mCurrentFocus;
+ if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
// We are showing on to of the current
// focus, so re-evaluate focus to make
// sure it is correct.
@@ -360,11 +350,9 @@
}
private void updateWallpaperLocked(int displayId) {
- final DisplayContentsAnimator displayAnimator =
- getDisplayContentsAnimatorLocked(displayId);
+ mService.getDisplayContentLocked(displayId).resetAnimationBackgroundAnimator();
+
final WindowList windows = mService.getWindowListLocked(displayId);
- WindowStateAnimator windowAnimationBackground = null;
- int windowAnimationBackgroundColor = 0;
WindowState detachedWallpaper = null;
for (int i = windows.size() - 1; i >= 0; i--) {
@@ -385,13 +373,9 @@
&& winAnimator.mAnimation.getDetachWallpaper()) {
detachedWallpaper = win;
}
- final int backgroundColor = winAnimator.mAnimation.getBackgroundColor();
- if (backgroundColor != 0) {
- if (windowAnimationBackground == null || (winAnimator.mAnimLayer <
- windowAnimationBackground.mAnimLayer)) {
- windowAnimationBackground = winAnimator;
- windowAnimationBackgroundColor = backgroundColor;
- }
+ final int color = winAnimator.mAnimation.getBackgroundColor();
+ if (color != 0) {
+ win.getStack().setAnimationBackground(winAnimator, color);
}
}
mAnimating = true;
@@ -408,13 +392,9 @@
detachedWallpaper = win;
}
- final int backgroundColor = appAnimator.animation.getBackgroundColor();
- if (backgroundColor != 0) {
- if (windowAnimationBackground == null || (winAnimator.mAnimLayer <
- windowAnimationBackground.mAnimLayer)) {
- windowAnimationBackground = winAnimator;
- windowAnimationBackgroundColor = backgroundColor;
- }
+ final int color = appAnimator.animation.getBackgroundColor();
+ if (color != 0) {
+ win.getStack().setAnimationBackground(winAnimator, color);
}
}
} // end forall windows
@@ -426,68 +406,47 @@
mWindowDetachedWallpaper = detachedWallpaper;
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
}
-
- if (windowAnimationBackgroundColor != 0) {
- // If the window that wants black is the current wallpaper
- // target, then the black goes *below* the wallpaper so we
- // don't cause the wallpaper to suddenly disappear.
- int animLayer = windowAnimationBackground.mAnimLayer;
- WindowState win = windowAnimationBackground.mWin;
- if (mService.mWallpaperTarget == win || mService.mLowerWallpaperTarget == win
- || mService.mUpperWallpaperTarget == win) {
- final int N = windows.size();
- for (int i = 0; i < N; i++) {
- WindowStateAnimator winAnimator = windows.get(i).mWinAnimator;
- if (winAnimator.mIsWallpaper) {
- animLayer = winAnimator.mAnimLayer;
- break;
- }
- }
- }
-
- displayAnimator.mWindowAnimationBackgroundSurface.show(
- animLayer - WindowManagerService.LAYER_OFFSET_DIM,
- ((windowAnimationBackgroundColor >> 24) & 0xff) / 255f, 0);
- } else {
- displayAnimator.mWindowAnimationBackgroundSurface.hide();
- }
}
/** See if any windows have been drawn, so they (and others associated with them) can now be
* shown. */
- private void testTokenMayBeDrawnLocked() {
+ private void testTokenMayBeDrawnLocked(int displayId) {
// See if any windows have been drawn, so they (and others
// associated with them) can now be shown.
- final ArrayList<AppWindowToken> appTokens = mService.mAnimatingAppTokens;
- final int NT = appTokens.size();
- for (int i=0; i<NT; i++) {
- AppWindowToken wtoken = appTokens.get(i);
- AppWindowAnimator appAnimator = wtoken.mAppAnimator;
- final boolean allDrawn = wtoken.allDrawn;
- if (allDrawn != appAnimator.allDrawn) {
- appAnimator.allDrawn = allDrawn;
- if (allDrawn) {
- // The token has now changed state to having all
- // windows shown... what to do, what to do?
- if (appAnimator.freezingScreen) {
- appAnimator.showAllWindowsLocked();
- mService.unsetAppFreezingScreenLocked(wtoken, false, true);
- if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG,
- "Setting mOrientationChangeComplete=true because wtoken "
- + wtoken + " numInteresting=" + wtoken.numInterestingWindows
- + " numDrawn=" + wtoken.numDrawnWindows);
- // This will set mOrientationChangeComplete and cause a pass through layout.
- setAppLayoutChanges(appAnimator,
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
- "testTokenMayBeDrawnLocked: freezingScreen");
- } else {
- setAppLayoutChanges(appAnimator,
- WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
- "testTokenMayBeDrawnLocked");
-
- // We can now show all of the drawn windows!
- if (!mService.mOpeningApps.contains(wtoken)) {
- mAnimating |= appAnimator.showAllWindowsLocked();
+ final ArrayList<Task> tasks = mService.getDisplayContentLocked(displayId).getTasks();
+ final int numTasks = tasks.size();
+ for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
+ final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ final int numTokens = tokens.size();
+ for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
+ final AppWindowToken wtoken = tokens.get(tokenNdx);
+ AppWindowAnimator appAnimator = wtoken.mAppAnimator;
+ final boolean allDrawn = wtoken.allDrawn;
+ if (allDrawn != appAnimator.allDrawn) {
+ appAnimator.allDrawn = allDrawn;
+ if (allDrawn) {
+ // The token has now changed state to having all
+ // windows shown... what to do, what to do?
+ if (appAnimator.freezingScreen) {
+ appAnimator.showAllWindowsLocked();
+ mService.unsetAppFreezingScreenLocked(wtoken, false, true);
+ if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG,
+ "Setting mOrientationChangeComplete=true because wtoken "
+ + wtoken + " numInteresting=" + wtoken.numInterestingWindows
+ + " numDrawn=" + wtoken.numDrawnWindows);
+ // This will set mOrientationChangeComplete and cause a pass through layout.
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "testTokenMayBeDrawnLocked: freezingScreen");
+ } else {
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
+ "testTokenMayBeDrawnLocked");
+
+ // We can now show all of the drawn windows!
+ if (!mService.mOpeningApps.contains(wtoken)) {
+ mAnimating |= appAnimator.showAllWindowsLocked();
+ }
}
}
}
@@ -500,17 +459,6 @@
updateWallpaperLocked(displayId);
}
- private long getDimBehindFadeDuration(long duration) {
- TypedValue tv = new TypedValue();
- mContext.getResources().getValue(
- com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
- if (tv.type == TypedValue.TYPE_FRACTION) {
- duration = (long)tv.getFraction(duration, duration);
- } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
- duration = tv.data;
- }
- return duration;
- }
/** Locked on mService.mWindowMap. */
private void animateLocked() {
@@ -531,11 +479,10 @@
SurfaceControl.openTransaction();
SurfaceControl.setAnimationTransaction();
try {
- updateAppWindowsLocked();
-
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
+ updateAppWindowsLocked(displayId);
DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
final ScreenRotationAnimation screenRotationAnimation =
@@ -561,53 +508,18 @@
}
}
- testTokenMayBeDrawnLocked();
-
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
+
+ testTokenMayBeDrawnLocked(displayId);
final ScreenRotationAnimation screenRotationAnimation =
- displayAnimator.mScreenRotationAnimation;
+ mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
if (screenRotationAnimation != null) {
screenRotationAnimation.updateSurfacesInTransaction();
}
- final DimLayer dimAnimator = displayAnimator.mDimAnimator;
- final WindowStateAnimator winAnimator = displayAnimator.mDimWinAnimator;
- final int dimLayer;
- final float dimAmount;
- if (winAnimator == null) {
- dimLayer = dimAnimator.getLayer();
- dimAmount = 0;
- } else {
- dimLayer = winAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
- dimAmount = winAnimator.mWin.mAttrs.dimAmount;
- }
- final float targetAlpha = dimAnimator.getTargetAlpha();
- if (targetAlpha != dimAmount) {
- if (winAnimator == null) {
- dimAnimator.hide(DEFAULT_DIM_DURATION);
- } else {
- long duration = (winAnimator.mAnimating && winAnimator.mAnimation != null)
- ? winAnimator.mAnimation.computeDurationHint()
- : DEFAULT_DIM_DURATION;
- if (targetAlpha > dimAmount) {
- duration = getDimBehindFadeDuration(duration);
- }
- dimAnimator.show(dimLayer, dimAmount, duration);
- }
- } else if (dimAnimator.getLayer() != dimLayer) {
- dimAnimator.setLayer(dimLayer);
- }
- if (dimAnimator.isAnimating()) {
- if (!mService.okToDisplay()) {
- // Jump to the end of the animation.
- dimAnimator.show();
- } else {
- mAnimating |= dimAnimator.stepAnimation();
- }
- }
+ mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers();
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
@@ -615,6 +527,8 @@
}
}
+ mService.setFocusedStackLayer();
+
if (mService.mWatermark != null) {
mService.mWatermark.drawIfNeeded();
}
@@ -661,26 +575,6 @@
}
}
- WindowState mCurrentFocus;
- void setCurrentFocus(final WindowState currentFocus) {
- mCurrentFocus = currentFocus;
- }
-
- boolean isDimmingLocked(int displayId) {
- return getDisplayContentsAnimatorLocked(displayId).mDimAnimator.isDimming();
- }
-
- boolean isDimmingLocked(final WindowStateAnimator winAnimator) {
- final int displayId = winAnimator.mWin.getDisplayId();
- DisplayContentsAnimator displayAnimator =
- getDisplayContentsAnimatorLocked(displayId);
- if (displayAnimator != null) {
- return displayAnimator.mDimWinAnimator == winAnimator
- && displayAnimator.mDimAnimator.isDimming();
- }
- return false;
- }
-
static String bulkUpdateParamsToString(int bulkUpdateParams) {
StringBuilder builder = new StringBuilder(128);
if ((bulkUpdateParams & LayoutFields.SET_UPDATE_ROTATION) != 0) {
@@ -718,18 +612,6 @@
pw.print(subPrefix); pw.print("Window #"); pw.print(j);
pw.print(": "); pw.println(wanim);
}
- if (displayAnimator.mWindowAnimationBackgroundSurface != null) {
- if (dumpAll || displayAnimator.mWindowAnimationBackgroundSurface.isDimming()) {
- pw.print(subPrefix); pw.println("mWindowAnimationBackgroundSurface:");
- displayAnimator.mWindowAnimationBackgroundSurface.printTo(subSubPrefix, pw);
- }
- }
- if (dumpAll || displayAnimator.mDimAnimator.isDimming()) {
- pw.print(subPrefix); pw.println("mDimAnimator:");
- displayAnimator.mDimAnimator.printTo(subSubPrefix, pw);
- pw.print(subPrefix); pw.print("mDimWinAnimator=");
- pw.println(displayAnimator.mDimWinAnimator);
- }
if (displayAnimator.mScreenRotationAnimation != null) {
pw.print(subPrefix); pw.println("mScreenRotationAnimation:");
displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw);
@@ -772,7 +654,7 @@
void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String s) {
// Used to track which displays layout changes have been done.
- SparseIntArray displays = new SparseIntArray();
+ SparseIntArray displays = new SparseIntArray(2);
WindowList windows = appAnimator.mAppToken.allAppWindows;
for (int i = windows.size() - 1; i >= 0; i--) {
final int displayId = windows.get(i).getDisplayId();
@@ -787,26 +669,10 @@
}
}
- void setDimWinAnimatorLocked(int displayId, WindowStateAnimator newWinAnimator) {
- DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
- if (newWinAnimator == null) {
- displayAnimator.mDimWinAnimator = null;
- } else {
- // Only set dim params on the highest dimmed layer.
- final WindowStateAnimator existingDimWinAnimator = displayAnimator.mDimWinAnimator;
- // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
- if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
- || !existingDimWinAnimator.mSurfaceShown
- || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
- displayAnimator.mDimWinAnimator = newWinAnimator;
- }
- }
- }
-
private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
if (displayAnimator == null) {
- displayAnimator = new DisplayContentsAnimator(displayId);
+ displayAnimator = new DisplayContentsAnimator();
mDisplayContentsAnimators.put(displayId, displayAnimator);
}
return displayAnimator;
@@ -821,14 +687,6 @@
}
private class DisplayContentsAnimator {
- DimLayer mDimAnimator = null;
- WindowStateAnimator mDimWinAnimator = null;
- DimLayer mWindowAnimationBackgroundSurface = null;
ScreenRotationAnimation mScreenRotationAnimation = null;
-
- public DisplayContentsAnimator(int displayId) {
- mDimAnimator = new DimLayer(mService, displayId);
- mWindowAnimationBackgroundSurface = new DimLayer(mService, displayId);
- }
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index de72c26..50d267f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -16,44 +16,27 @@
package com.android.server.wm;
-import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.*;
+
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import android.app.AppOpsManager;
import android.util.TimeUtils;
import android.view.IWindowId;
+
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
import com.android.internal.policy.impl.PhoneWindowManager;
+import com.android.internal.util.FastPrintWriter;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AttributeCache;
import com.android.server.EventLogTags;
+import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.DisplayManagerService;
@@ -62,6 +45,7 @@
import com.android.server.power.ShutdownThread;
import android.Manifest;
+import android.app.ActivityManager.StackBoxInfo;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.StatusBarManager;
@@ -77,7 +61,6 @@
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -143,6 +126,7 @@
import android.view.WindowManagerPolicy;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerPolicy.FakeWindow;
+import android.view.WindowManagerPolicy.PointerEventListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
@@ -200,6 +184,8 @@
static final boolean DEBUG_LAYOUT_REPEATS = true;
static final boolean DEBUG_SURFACE_TRACE = false;
static final boolean DEBUG_WINDOW_TRACE = false;
+ static final boolean DEBUG_TASK_MOVEMENT = false;
+ static final boolean DEBUG_STACK = false;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
@@ -209,6 +195,9 @@
static final boolean PROFILE_ORIENTATION = false;
static final boolean localLOGV = DEBUG;
+ final static boolean REVERSE_ITERATOR = true;
+ final static boolean FORWARD_ITERATOR = false;
+
/** How much to multiply the policy's type layer, to reserve room
* for multiple windows of the same type and Z-ordering adjustment
* with TYPE_LAYER_OFFSET. */
@@ -234,6 +223,11 @@
static final int LAYER_OFFSET_BLUR = 2;
/**
+ * FocusedStackFrame layer is immediately above focused window.
+ */
+ static final int LAYER_OFFSET_FOCUSED_STACK = 1;
+
+ /**
* Animation thumbnail is as far as possible below the window above
* the thumbnail (or in other words as far as possible above the window
* below it).
@@ -276,6 +270,12 @@
// Default input dispatching timeout in nanoseconds.
static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
+ /** Minimum value for createStack and resizeStack weight value */
+ public static final float STACK_WEIGHT_MIN = 0.2f;
+
+ /** Maximum value for createStack and resizeStack weight value */
+ public static final float STACK_WEIGHT_MAX = 0.8f;
+
static final int UPDATE_FOCUS_NORMAL = 0;
static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
@@ -337,34 +337,7 @@
/**
* Mapping from a token IBinder to a WindowToken object.
*/
- final HashMap<IBinder, WindowToken> mTokenMap =
- new HashMap<IBinder, WindowToken>();
-
- /**
- * Window tokens that are in the process of exiting, but still
- * on screen for animations.
- */
- final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>();
-
- /**
- * List controlling the ordering of windows in different applications which must
- * be kept in sync with ActivityManager.
- */
- final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();
-
- /**
- * AppWindowTokens in the Z order they were in at the start of an animation. Between
- * animations this list is maintained in the exact order of mAppTokens. If tokens
- * are added to mAppTokens during an animation an attempt is made to insert them at the same
- * logical location in this list. Note that this list is always in sync with mWindows.
- */
- ArrayList<AppWindowToken> mAnimatingAppTokens = new ArrayList<AppWindowToken>();
-
- /**
- * Application tokens that are in the process of exiting, but still
- * on screen for animations.
- */
- final ArrayList<AppWindowToken> mExitingAppTokens = new ArrayList<AppWindowToken>();
+ final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<IBinder, WindowToken>();
/**
* List of window tokens that have finished starting their application,
@@ -437,6 +410,9 @@
final SurfaceSession mFxSession;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
+ FocusedStackFrame mFocusedStackFrame;
+
+ int mFocusedStackLayer;
final float[] mTmpFloats = new float[9];
@@ -449,8 +425,10 @@
String mLastANRState;
- /** All DisplayDontents in the world, kept here */
- private SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>();
+ /** All DisplayContents in the world, kept here */
+ private SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>(2);
+
+ private final AllWindowsIterator mTmpWindowsIterator = new AllWindowsIterator();
int mRotation = 0;
int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -589,7 +567,6 @@
Object mLastWindowFreezeSource = null;
private Session mHoldScreen = null;
private boolean mObscured = false;
- boolean mDimming = false;
private boolean mSyswin = false;
private float mScreenBrightness = -1;
private float mButtonBrightness = -1;
@@ -604,22 +581,6 @@
}
final LayoutFields mInnerFields = new LayoutFields();
- static class AppWindowAnimParams {
- AppWindowAnimator mAppAnimator;
- ArrayList<WindowStateAnimator> mWinAnimators;
-
- public AppWindowAnimParams(final AppWindowAnimator appAnimator) {
- mAppAnimator = appAnimator;
-
- final AppWindowToken atoken = appAnimator.mAppToken;
- mWinAnimators = new ArrayList<WindowStateAnimator>();
- final int N = atoken.allAppWindows.size();
- for (int i = 0; i < N; i++) {
- mWinAnimators.add(atoken.allAppWindows.get(i).mWinAnimator);
- }
- }
- }
-
boolean mAnimationScheduled;
/** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
@@ -631,6 +592,11 @@
final WindowAnimator mAnimator;
+ SparseArray<Task> mTaskIdToTask = new SparseArray<Task>();
+ SparseArray<TaskStack> mStackIdToStack = new SparseArray<TaskStack>();
+
+ private final PointerEventDispatcher mPointerEventDispatcher;
+
final class DragInputEventReceiver extends InputEventReceiver {
public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
@@ -722,8 +688,7 @@
public static WindowManagerService main(final Context context,
final PowerManagerService pm, final DisplayManagerService dm,
- final InputManagerService im,
- final Handler uiHandler, final Handler wmHandler,
+ final InputManagerService im, final Handler wmHandler,
final boolean haveInputMethods, final boolean showBootMsgs,
final boolean onlyCore) {
final WindowManagerService[] holder = new WindowManagerService[1];
@@ -731,7 +696,7 @@
@Override
public void run() {
holder[0] = new WindowManagerService(context, pm, dm, im,
- uiHandler, haveInputMethods, showBootMsgs, onlyCore);
+ haveInputMethods, showBootMsgs, onlyCore);
}
}, 0);
return holder[0];
@@ -753,7 +718,6 @@
private WindowManagerService(Context context, PowerManagerService pm,
DisplayManagerService displayManager, InputManagerService inputManager,
- Handler uiHandler,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
mContext = context;
mHaveInputMethods = haveInputMethods;
@@ -761,11 +725,14 @@
mOnlyCore = onlyCore;
mLimitedAlphaCompositing = context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_limitedAlpha);
+ mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerService = displayManager;
mHeadless = displayManager.isHeadless();
mDisplaySettings = new DisplaySettings(context);
mDisplaySettings.readSettingsLocked();
+ mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG));
+
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(this, null);
Display[] displays = mDisplayManager.getDisplays();
@@ -812,11 +779,10 @@
| PowerManager.ON_AFTER_RELEASE, TAG);
mHoldingScreenWakeLock.setReferenceCounted(false);
- mInputManager = inputManager;
mFxSession = new SurfaceSession();
mAnimator = new WindowAnimator(this);
- initPolicy(uiHandler);
+ initPolicy(UiThread.getHandler());
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
@@ -824,6 +790,8 @@
SurfaceControl.openTransaction();
try {
createWatermarkInTransaction();
+ mFocusedStackFrame = new FocusedStackFrame(
+ getDefaultDisplayContentLocked().getDisplay(), mFxSession);
} finally {
SurfaceControl.closeTransaction();
}
@@ -860,10 +828,14 @@
private void placeWindowBefore(WindowState pos, WindowState window) {
final WindowList windows = pos.getWindowList();
- final int i = windows.indexOf(pos);
+ int i = windows.indexOf(pos);
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + window + " at "
+ i + " of " + windows.size() + " (before " + pos + ")");
+ if (i < 0) {
+ Slog.w(TAG, "placeWindowBefore: Unable to find " + pos + " in " + windows);
+ i = 0;
+ }
windows.add(i, window);
mWindowsChanged = true;
}
@@ -920,217 +892,259 @@
return -1;
}
- private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
+ private int addAppWindowToListLocked(final WindowState win) {
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
final DisplayContent displayContent = win.mDisplayContent;
final WindowList windows = win.getWindowList();
final int N = windows.size();
- final WindowState attached = win.mAttachedWindow;
- int i;
WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
- if (attached == null) {
- int tokenWindowsPos = 0;
- int windowListPos = tokenWindowList.size();
- if (token.appWindowToken != null) {
- int index = windowListPos - 1;
- if (index >= 0) {
- // If this application has existing windows, we
- // simply place the new window on top of them... but
- // keep the starting window on top.
- if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
- // Base windows go behind everything else.
- WindowState lowestWindow = tokenWindowList.get(0);
- placeWindowBefore(lowestWindow, win);
- tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
- } else {
- AppWindowToken atoken = win.mAppToken;
- WindowState lastWindow = tokenWindowList.get(index);
- if (atoken != null && lastWindow == atoken.startingWindow) {
- placeWindowBefore(lastWindow, win);
- tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows);
- } else {
- int newIdx = findIdxBasedOnAppTokens(win);
- //there is a window above this one associated with the same
- //apptoken note that the window could be a floating window
- //that was created later or a window at the top of the list of
- //windows associated with this token.
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
- Slog.v(TAG, "Adding window " + win + " at "
- + (newIdx + 1) + " of " + N);
- }
- windows.add(newIdx + 1, win);
- if (newIdx < 0) {
- // No window from token found on win's display.
- tokenWindowsPos = 0;
- } else {
- tokenWindowsPos = indexOfWinInWindowList(
- windows.get(newIdx), token.windows) + 1;
- }
- mWindowsChanged = true;
- }
- }
+ int tokenWindowsPos = 0;
+ int windowListPos = tokenWindowList.size();
+ if (!tokenWindowList.isEmpty()) {
+ // If this application has existing windows, we
+ // simply place the new window on top of them... but
+ // keep the starting window on top.
+ if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
+ // Base windows go behind everything else.
+ WindowState lowestWindow = tokenWindowList.get(0);
+ placeWindowBefore(lowestWindow, win);
+ tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
+ } else {
+ AppWindowToken atoken = win.mAppToken;
+ WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
+ if (atoken != null && lastWindow == atoken.startingWindow) {
+ placeWindowBefore(lastWindow, win);
+ tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows);
} else {
- // No windows from this token on this display
- if (localLOGV) Slog.v(
- TAG, "Figuring out where to add app window "
- + client.asBinder() + " (token=" + token + ")");
- // Figure out where the window should go, based on the
- // order of applications.
- final int NA = mAnimatingAppTokens.size();
- WindowState pos = null;
- for (i=NA-1; i>=0; i--) {
- AppWindowToken t = mAnimatingAppTokens.get(i);
- if (t == token) {
- i--;
- break;
- }
-
- // We haven't reached the token yet; if this token
- // is not going to the bottom and has windows on this display, we can
- // use it as an anchor for when we do reach the token.
- tokenWindowList = getTokenWindowsOnDisplay(t, win.mDisplayContent);
- if (!t.sendingToBottom && tokenWindowList.size() > 0) {
- pos = tokenWindowList.get(0);
- }
- }
- // We now know the index into the apps. If we found
- // an app window above, that gives us the position; else
- // we need to look some more.
- if (pos != null) {
- // Move behind any windows attached to this one.
- WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
- if (atoken != null) {
- tokenWindowList =
- getTokenWindowsOnDisplay(atoken, win.mDisplayContent);
- final int NC = tokenWindowList.size();
- if (NC > 0) {
- WindowState bottom = tokenWindowList.get(0);
- if (bottom.mSubLayer < 0) {
- pos = bottom;
- }
- }
- }
- placeWindowBefore(pos, win);
+ int newIdx = findIdxBasedOnAppTokens(win);
+ //there is a window above this one associated with the same
+ //apptoken note that the window could be a floating window
+ //that was created later or a window at the top of the list of
+ //windows associated with this token.
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "Adding window " + win + " at " + (newIdx + 1) + " of " + N);
+ windows.add(newIdx + 1, win);
+ if (newIdx < 0) {
+ // No window from token found on win's display.
+ tokenWindowsPos = 0;
} else {
- // Continue looking down until we find the first
- // token that has windows on this display.
- while (i >= 0) {
- AppWindowToken t = mAnimatingAppTokens.get(i);
- tokenWindowList = getTokenWindowsOnDisplay(t, win.mDisplayContent);
- final int NW = tokenWindowList.size();
- if (NW > 0) {
- pos = tokenWindowList.get(NW-1);
- break;
- }
- i--;
- }
- if (pos != null) {
- // Move in front of any windows attached to this
- // one.
- WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
- if (atoken != null) {
- final int NC = atoken.windows.size();
- if (NC > 0) {
- WindowState top = atoken.windows.get(NC-1);
- if (top.mSubLayer >= 0) {
- pos = top;
- }
- }
- }
- placeWindowAfter(pos, win);
- } else {
- // Just search for the start of this layer.
- final int myLayer = win.mBaseLayer;
- for (i=0; i<N; i++) {
- WindowState w = windows.get(i);
- if (w.mBaseLayer > myLayer) {
- break;
- }
- }
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
- Slog.v(TAG, "Adding window " + win + " at "
- + i + " of " + N);
- }
- windows.add(i, win);
- mWindowsChanged = true;
+ tokenWindowsPos = indexOfWinInWindowList(
+ windows.get(newIdx), token.windows) + 1;
+ }
+ mWindowsChanged = true;
+ }
+ }
+ return tokenWindowsPos;
+ }
+
+ // No windows from this token on this display
+ if (localLOGV) Slog.v(TAG, "Figuring out where to add app window " + client.asBinder()
+ + " (token=" + token + ")");
+ // Figure out where the window should go, based on the
+ // order of applications.
+ WindowState pos = null;
+
+ final ArrayList<Task> tasks = win.getStack().getTasks();
+ int taskNdx;
+ int tokenNdx = -1;
+ for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
+ final AppWindowToken t = tokens.get(tokenNdx);
+ if (t == token) {
+ --tokenNdx;
+ if (tokenNdx < 0) {
+ --taskNdx;
+ if (taskNdx >= 0) {
+ tokenNdx = tasks.get(taskNdx).mAppTokens.size() - 1;
}
}
+ break;
+ }
+
+ // We haven't reached the token yet; if this token
+ // is not going to the bottom and has windows on this display, we can
+ // use it as an anchor for when we do reach the token.
+ tokenWindowList = getTokenWindowsOnDisplay(t, displayContent);
+ if (!t.sendingToBottom && tokenWindowList.size() > 0) {
+ pos = tokenWindowList.get(0);
+ }
+ }
+ if (tokenNdx >= 0) {
+ // early exit
+ break;
+ }
+ }
+
+ // We now know the index into the apps. If we found
+ // an app window above, that gives us the position; else
+ // we need to look some more.
+ if (pos != null) {
+ // Move behind any windows attached to this one.
+ WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
+ if (atoken != null) {
+ tokenWindowList =
+ getTokenWindowsOnDisplay(atoken, displayContent);
+ final int NC = tokenWindowList.size();
+ if (NC > 0) {
+ WindowState bottom = tokenWindowList.get(0);
+ if (bottom.mSubLayer < 0) {
+ pos = bottom;
+ }
+ }
+ }
+ placeWindowBefore(pos, win);
+ return tokenWindowsPos;
+ }
+
+ // Continue looking down until we find the first
+ // token that has windows on this display.
+ for ( ; taskNdx >= 0; --taskNdx) {
+ AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ for ( ; tokenNdx >= 0; --tokenNdx) {
+ final AppWindowToken t = tokens.get(tokenNdx);
+ tokenWindowList = getTokenWindowsOnDisplay(t, displayContent);
+ final int NW = tokenWindowList.size();
+ if (NW > 0) {
+ pos = tokenWindowList.get(NW-1);
+ break;
+ }
+ }
+ if (tokenNdx >= 0) {
+ // found
+ break;
+ }
+ }
+
+ if (pos != null) {
+ // Move in front of any windows attached to this
+ // one.
+ WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
+ if (atoken != null) {
+ final int NC = atoken.windows.size();
+ if (NC > 0) {
+ WindowState top = atoken.windows.get(NC-1);
+ if (top.mSubLayer >= 0) {
+ pos = top;
+ }
+ }
+ }
+ placeWindowAfter(pos, win);
+ return tokenWindowsPos;
+ }
+
+ // Just search for the start of this layer.
+ final int myLayer = win.mBaseLayer;
+ int i;
+ for (i = 0; i < N; i++) {
+ WindowState w = windows.get(i);
+ if (w.mBaseLayer > myLayer) {
+ break;
+ }
+ }
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "Adding window " + win + " at " + i + " of " + N);
+ windows.add(i, win);
+ mWindowsChanged = true;
+ return tokenWindowsPos;
+ }
+
+ private void addFreeWindowToListLocked(final WindowState win) {
+ final WindowList windows = win.getWindowList();
+
+ // Figure out where window should go, based on layer.
+ final int myLayer = win.mBaseLayer;
+ int i;
+ for (i = windows.size() - 1; i >= 0; i--) {
+ if (windows.get(i).mBaseLayer <= myLayer) {
+ break;
+ }
+ }
+ i++;
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "Adding window " + win + " at " + i + " of " + windows.size());
+ windows.add(i, win);
+ mWindowsChanged = true;
+ }
+
+ private void addAttachedWindowToListLocked(final WindowState win, boolean addToToken) {
+ final WindowToken token = win.mToken;
+ final DisplayContent displayContent = win.mDisplayContent;
+ final WindowState attached = win.mAttachedWindow;
+
+ WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
+
+ // Figure out this window's ordering relative to the window
+ // it is attached to.
+ final int NA = tokenWindowList.size();
+ final int sublayer = win.mSubLayer;
+ int largestSublayer = Integer.MIN_VALUE;
+ WindowState windowWithLargestSublayer = null;
+ int i;
+ for (i = 0; i < NA; i++) {
+ WindowState w = tokenWindowList.get(i);
+ final int wSublayer = w.mSubLayer;
+ if (wSublayer >= largestSublayer) {
+ largestSublayer = wSublayer;
+ windowWithLargestSublayer = w;
+ }
+ if (sublayer < 0) {
+ // For negative sublayers, we go below all windows
+ // in the same sublayer.
+ if (wSublayer >= sublayer) {
+ if (addToToken) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
+ token.windows.add(i, win);
+ }
+ placeWindowBefore(wSublayer >= 0 ? attached : w, win);
+ break;
}
} else {
- // Figure out where window should go, based on layer.
- final int myLayer = win.mBaseLayer;
- for (i=N-1; i>=0; i--) {
- if (windows.get(i).mBaseLayer <= myLayer) {
- break;
+ // For positive sublayers, we go above all windows
+ // in the same sublayer.
+ if (wSublayer > sublayer) {
+ if (addToToken) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
+ token.windows.add(i, win);
}
+ placeWindowBefore(w, win);
+ break;
}
- i++;
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
- TAG, "Adding window " + win + " at "
- + i + " of " + N);
- windows.add(i, win);
- mWindowsChanged = true;
}
+ }
+ if (i >= NA) {
+ if (addToToken) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
+ token.windows.add(win);
+ }
+ if (sublayer < 0) {
+ placeWindowBefore(attached, win);
+ } else {
+ placeWindowAfter(largestSublayer >= 0
+ ? windowWithLargestSublayer
+ : attached,
+ win);
+ }
+ }
+ }
+ private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
+ if (win.mAttachedWindow == null) {
+ final WindowToken token = win.mToken;
+ int tokenWindowsPos = 0;
+ if (token.appWindowToken != null) {
+ tokenWindowsPos = addAppWindowToListLocked(win);
+ } else {
+ addFreeWindowToListLocked(win);
+ }
if (addToToken) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
token.windows.add(tokenWindowsPos, win);
}
-
} else {
- // Figure out this window's ordering relative to the window
- // it is attached to.
- final int NA = tokenWindowList.size();
- final int sublayer = win.mSubLayer;
- int largestSublayer = Integer.MIN_VALUE;
- WindowState windowWithLargestSublayer = null;
- for (i=0; i<NA; i++) {
- WindowState w = tokenWindowList.get(i);
- final int wSublayer = w.mSubLayer;
- if (wSublayer >= largestSublayer) {
- largestSublayer = wSublayer;
- windowWithLargestSublayer = w;
- }
- if (sublayer < 0) {
- // For negative sublayers, we go below all windows
- // in the same sublayer.
- if (wSublayer >= sublayer) {
- if (addToToken) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
- token.windows.add(i, win);
- }
- placeWindowBefore(wSublayer >= 0 ? attached : w, win);
- break;
- }
- } else {
- // For positive sublayers, we go above all windows
- // in the same sublayer.
- if (wSublayer > sublayer) {
- if (addToToken) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
- token.windows.add(i, win);
- }
- placeWindowBefore(w, win);
- break;
- }
- }
- }
- if (i >= NA) {
- if (addToToken) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
- token.windows.add(win);
- }
- if (sublayer < 0) {
- placeWindowBefore(attached, win);
- } else {
- placeWindowAfter(largestSublayer >= 0
- ? windowWithLargestSublayer
- : attached,
- win);
- }
- }
+ addAttachedWindowToListLocked(win, addToToken);
}
if (win.mAppToken != null && addToToken) {
@@ -1546,10 +1560,6 @@
return true;
}
- void adjustInputMethodDialogsLocked() {
- moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
- }
-
final boolean isWallpaperVisible(WindowState wallpaperTarget) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
+ (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
@@ -1840,15 +1850,36 @@
// Now stick it in.
if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
Slog.v(TAG, "Moving wallpaper " + wallpaper
- + " from " + oldIndex + " to " + foundI);
+ + " from " + oldIndex + " to " + 0);
}
- windows.add(foundI, wallpaper);
+ windows.add(0, wallpaper);
mWindowsChanged = true;
changed |= ADJUST_WALLPAPER_LAYERS_CHANGED;
}
}
+ /*
+ final TaskStack targetStack =
+ mWallpaperTarget == null ? null : mWallpaperTarget.getStack();
+ if ((changed & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0 &&
+ targetStack != null && !targetStack.isHomeStack()) {
+ // If the wallpaper target is not on the home stack then make sure that all windows
+ // from other non-home stacks are above the wallpaper.
+ for (i = foundI - 1; i >= 0; --i) {
+ WindowState win = windows.get(i);
+ if (!win.isVisibleLw()) {
+ continue;
+ }
+ final TaskStack winStack = win.getStack();
+ if (winStack != null && !winStack.isHomeStack() && winStack != targetStack) {
+ windows.remove(i);
+ windows.add(foundI + 1, win);
+ }
+ }
+ }
+ */
+
if (targetChanged && DEBUG_WALLPAPER_LIGHT) {
Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
+ " lower=" + mLowerWallpaperTarget + " upper="
@@ -2076,6 +2107,13 @@
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent == null) {
+ Slog.w(TAG, "Attempted to add window to a display that does not exist: "
+ + displayId + ". Aborting.");
+ return WindowManagerGlobal.ADD_INVALID_DISPLAY;
+ }
+ if (!displayContent.hasAccess(session.mUid)) {
+ Slog.w(TAG, "Attempted to add window to a display for which the application "
+ + "does not have access: " + displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
@@ -2223,7 +2261,7 @@
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
mInputMethodDialogs.add(win);
addWindowToListInOrderLocked(win, true);
- adjustInputMethodDialogsLocked();
+ moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
imMayMove = false;
} else {
addWindowToListInOrderLocked(win, true);
@@ -2314,9 +2352,9 @@
if (localLOGV || DEBUG_FOCUS) Slog.v(
TAG, "Remove " + win + " client="
- + Integer.toHexString(System.identityHashCode(
- win.mClient.asBinder()))
- + ", surface=" + win.mWinAnimator.mSurfaceControl);
+ + Integer.toHexString(System.identityHashCode(win.mClient.asBinder()))
+ + ", surface=" + win.mWinAnimator.mSurfaceControl,
+ new RuntimeException("here").fillInStackTrace());
final long origId = Binder.clearCallingIdentity();
@@ -2366,7 +2404,6 @@
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
performLayoutAndPlaceSurfacesLocked();
- mInputMonitor.updateInputWindowsLw(false /*force*/);
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
@@ -2495,22 +2532,15 @@
public void updateAppOpsState() {
synchronized(mWindowMap) {
- boolean changed = false;
- for (int i=0; i<mDisplayContents.size(); i++) {
- DisplayContent display = mDisplayContents.valueAt(i);
- WindowList windows = display.getWindowList();
- for (int j=0; j<windows.size(); j++) {
- final WindowState win = windows.get(j);
- if (win.mAppOp != AppOpsManager.OP_NONE) {
- changed |= win.setAppOpVisibilityLw(mAppOps.checkOpNoThrow(win.mAppOp,
- win.getOwningUid(),
- win.getOwningPackage()) == AppOpsManager.MODE_ALLOWED);
- }
+ mTmpWindowsIterator.reset(FORWARD_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
+ final WindowState win = mTmpWindowsIterator.next();
+ if (win.mAppOp != AppOpsManager.OP_NONE) {
+ final int mode = mAppOps.checkOpNoThrow(win.mAppOp, win.getOwningUid(),
+ win.getOwningPackage());
+ win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED);
}
}
- if (changed) {
- scheduleAnimationLocked();
- }
}
}
@@ -3157,35 +3187,70 @@
// Application Window Tokens
// -------------------------------------------------------------
- public void validateAppTokens(List<IBinder> tokens) {
- int v = tokens.size()-1;
- int m = mAppTokens.size()-1;
- while (v >= 0 && m >= 0) {
- AppWindowToken atoken = mAppTokens.get(m);
- if (atoken.removed) {
- m--;
- continue;
+ public void validateAppTokens(int stackId, List<TaskGroup> tasks) {
+ synchronized (mWindowMap) {
+ int t = tasks.size() - 1;
+ if (t < 0) {
+ Slog.w(TAG, "validateAppTokens: empty task list");
+ return;
}
- if (tokens.get(v) != atoken.token) {
- Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v)
- + " @ " + v + ", internal is " + atoken.token + " @ " + m);
+
+ TaskGroup task = tasks.get(0);
+ int taskId = task.taskId;
+ Task targetTask = mTaskIdToTask.get(taskId);
+ DisplayContent displayContent = targetTask.getDisplayContent();
+ if (displayContent == null) {
+ Slog.w(TAG, "validateAppTokens: no Display for taskId=" + taskId);
+ return;
}
- v--;
- m--;
- }
- while (v >= 0) {
- Slog.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v);
- v--;
- }
- while (m >= 0) {
- AppWindowToken atoken = mAppTokens.get(m);
- if (!atoken.removed) {
- Slog.w(TAG, "Invalid internal atoken: " + atoken.token + " @ " + m);
+
+ final ArrayList<Task> localTasks = mStackIdToStack.get(stackId).getTasks();
+ int taskNdx;
+ for (taskNdx = localTasks.size() - 1; taskNdx >= 0 && t >= 0; --taskNdx, --t) {
+ AppTokenList localTokens = localTasks.get(taskNdx).mAppTokens;
+ task = tasks.get(t);
+ List<IApplicationToken> tokens = task.tokens;
+
+ DisplayContent lastDisplayContent = displayContent;
+ displayContent = mTaskIdToTask.get(taskId).getDisplayContent();
+ if (displayContent != lastDisplayContent) {
+ Slog.w(TAG, "validateAppTokens: displayContent changed in TaskGroup list!");
+ return;
+ }
+
+ int tokenNdx;
+ int v;
+ for (tokenNdx = localTokens.size() - 1, v = task.tokens.size() - 1;
+ tokenNdx >= 0 && v >= 0; ) {
+ final AppWindowToken atoken = localTokens.get(tokenNdx);
+ if (atoken.removed) {
+ --tokenNdx;
+ continue;
+ }
+ if (tokens.get(v) != atoken.token) {
+ break;
+ }
+ --tokenNdx;
+ v--;
+ }
+
+ if (tokenNdx >= 0 || v >= 0) {
+ break;
+ }
}
- m--;
+
+ if (taskNdx >= 0 || t >= 0) {
+ Slog.w(TAG, "validateAppTokens: Mismatch! ActivityManager=" + tasks);
+ Slog.w(TAG, "validateAppTokens: Mismatch! WindowManager=" + localTasks);
+ Slog.w(TAG, "validateAppTokens: Mismatch! Callers=" + Debug.getCallers(4));
+ }
}
}
+ public void validateStackOrder(Integer[] remoteStackIds) {
+ // TODO:
+ }
+
boolean checkCallingPermission(String permission, String func) {
// Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == Process.myPid()) {
@@ -3246,6 +3311,7 @@
final long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
+ DisplayContent displayContent = null;
WindowToken wtoken = mTokenMap.remove(token);
if (wtoken != null) {
boolean delayed = false;
@@ -3255,6 +3321,7 @@
for (int i=0; i<N; i++) {
WindowState win = wtoken.windows.get(i);
+ displayContent = win.mDisplayContent;
if (win.mWinAnimator.isAnimating()) {
delayed = true;
@@ -3264,13 +3331,12 @@
win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
false);
//TODO (multidisplay): Magnification is supported only for the default
- if (mDisplayMagnifier != null
- && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ if (mDisplayMagnifier != null && win.isDefaultDisplay()) {
mDisplayMagnifier.onWindowTransitionLocked(win,
WindowManagerPolicy.TRANSIT_EXIT);
}
changed = true;
- win.mDisplayContent.layoutNeeded = true;
+ displayContent.layoutNeeded = true;
}
}
@@ -3283,7 +3349,7 @@
}
if (delayed) {
- mExitingTokens.add(wtoken);
+ displayContent.mExitingTokens.add(wtoken);
} else if (wtoken.windowType == TYPE_WALLPAPER) {
mWallpaperTokens.remove(wtoken);
}
@@ -3297,27 +3363,9 @@
Binder.restoreCallingIdentity(origId);
}
- /**
- * Find the location to insert a new AppWindowToken into the window-ordered app token list.
- * Note that mAppTokens.size() == mAnimatingAppTokens.size() + 1.
- * @param addPos The location the token was inserted into in mAppTokens.
- * @param atoken The token to insert.
- */
- private void addAppTokenToAnimating(final int addPos, final AppWindowToken atoken) {
- if (addPos == 0 || addPos == mAnimatingAppTokens.size()) {
- // It was inserted into the beginning or end of mAppTokens. Honor that.
- mAnimatingAppTokens.add(addPos, atoken);
- return;
- }
- // Find the item immediately above the mAppTokens insertion point and put the token
- // immediately below that one in mAnimatingAppTokens.
- final AppWindowToken aboveAnchor = mAppTokens.get(addPos + 1);
- mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), atoken);
- }
-
@Override
- public void addAppToken(int addPos, IApplicationToken token,
- int groupId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked) {
+ public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
+ int requestedOrientation, boolean fullscreen, boolean showWhenLocked) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3345,14 +3393,27 @@
}
atoken = new AppWindowToken(this, token);
atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
- atoken.groupId = groupId;
+ atoken.groupId = taskId;
atoken.appFullscreen = fullscreen;
atoken.showWhenLocked = showWhenLocked;
atoken.requestedOrientation = requestedOrientation;
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
+ " at " + addPos);
- mAppTokens.add(addPos, atoken);
- addAppTokenToAnimating(addPos, atoken);
+
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ TaskStack stack = mStackIdToStack.get(stackId);
+ if (stack == null) {
+ throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);
+ }
+ task = new Task(atoken, stack);
+ stack.addTask(task, true);
+ stack.getDisplayContent().moveStack(stack, true);
+ mTaskIdToTask.put(taskId, task);
+ } else {
+ task.addAppToken(addPos, atoken);
+ }
+
mTokenMap.put(token.asBinder(), atoken);
// Application tokens start out hidden.
@@ -3371,12 +3432,21 @@
}
synchronized(mWindowMap) {
- AppWindowToken atoken = findAppWindowToken(token);
+ final AppWindowToken atoken = findAppWindowToken(token);
if (atoken == null) {
Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token);
return;
}
+ Task oldTask = mTaskIdToTask.get(atoken.groupId);
+ oldTask.removeAppToken(atoken);
+
atoken.groupId = groupId;
+ Task newTask = mTaskIdToTask.get(groupId);
+ if (newTask == null) {
+ throw new IllegalStateException("setAppGroupId: groupId=" + groupId
+ + " does not exist");
+ }
+ newTask.mAppTokens.add(atoken);
}
}
@@ -3417,72 +3487,76 @@
}
public int getOrientationFromAppTokensLocked() {
- int curGroup = 0;
int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean findingBehind = false;
- boolean haveGroup = false;
boolean lastFullscreen = false;
- for (int pos = mAppTokens.size() - 1; pos >= 0; pos--) {
- AppWindowToken atoken = mAppTokens.get(pos);
+ // TODO: Multi window.
+ DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final ArrayList<Task> tasks = displayContent.getTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ final int firstToken = tokens.size() - 1;
+ for (int tokenNdx = firstToken; tokenNdx >= 0; --tokenNdx) {
+ final AppWindowToken atoken = tokens.get(tokenNdx);
- if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + atoken);
+ if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + atoken);
- // if we're about to tear down this window and not seek for
- // the behind activity, don't use it for orientation
- if (!findingBehind
- && (!atoken.hidden && atoken.hiddenRequested)) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
- + " -- going to hide");
- continue;
- }
-
- if (haveGroup == true && curGroup != atoken.groupId) {
- // If we have hit a new application group, and the bottom
- // of the previous group didn't explicitly say to use
- // the orientation behind it, and the last app was
- // full screen, then we'll stick with the
- // user's orientation.
- if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
- && lastFullscreen) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
- + " -- end of group, return " + lastOrientation);
- return lastOrientation;
+ // if we're about to tear down this window and not seek for
+ // the behind activity, don't use it for orientation
+ if (!findingBehind
+ && (!atoken.hidden && atoken.hiddenRequested)) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
+ + " -- going to hide");
+ continue;
}
- }
- // We ignore any hidden applications on the top.
- if (atoken.hiddenRequested || atoken.willBeHidden) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
- + " -- hidden on top");
- continue;
- }
+ if (tokenNdx == firstToken) {
+ // If we have hit a new Task, and the bottom
+ // of the previous group didn't explicitly say to use
+ // the orientation behind it, and the last app was
+ // full screen, then we'll stick with the
+ // user's orientation.
+ if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
+ && lastFullscreen) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
+ + " -- end of group, return " + lastOrientation);
+ return lastOrientation;
+ }
+ }
- if (!haveGroup) {
- haveGroup = true;
- curGroup = atoken.groupId;
- lastOrientation = atoken.requestedOrientation;
- }
+ // We ignore any hidden applications on the top.
+ if (atoken.hiddenRequested || atoken.willBeHidden) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
+ + " -- hidden on top");
+ continue;
+ }
- int or = atoken.requestedOrientation;
- // If this application is fullscreen, and didn't explicitly say
- // to use the orientation behind it, then just take whatever
- // orientation it has and ignores whatever is under it.
- lastFullscreen = atoken.appFullscreen;
- if (lastFullscreen
- && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
- + " -- full screen, return " + or);
- return or;
+ if (tokenNdx == 0) {
+ // Last token in this task.
+ lastOrientation = atoken.requestedOrientation;
+ }
+
+ int or = atoken.requestedOrientation;
+ // If this application is fullscreen, and didn't explicitly say
+ // to use the orientation behind it, then just take whatever
+ // orientation it has and ignores whatever is under it.
+ lastFullscreen = atoken.appFullscreen;
+ if (lastFullscreen
+ && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
+ + " -- full screen, return " + or);
+ return or;
+ }
+ // If this application has requested an explicit orientation,
+ // then use it.
+ if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
+ + " -- explicitly set, return " + or);
+ return or;
+ }
+ findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
}
- // If this application has requested an explicit orientation,
- // then use it.
- if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
- && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
- + " -- explicitly set, return " + or);
- return or;
- }
- findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
}
if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation");
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -3531,9 +3605,10 @@
if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
- getDefaultDisplayContentLocked().layoutNeeded = true;
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ displayContent.layoutNeeded = true;
int anim[] = new int[2];
- if (mAnimator.isDimmingLocked(Display.DEFAULT_DISPLAY)) {
+ if (displayContent.isDimming()) {
anim[0] = anim[1] = 0;
} else {
mPolicy.selectRotationAnimationLw(anim);
@@ -3633,6 +3708,52 @@
}
}
+ /** Call while in a Surface transaction. */
+ void setFocusedStackLayer() {
+ mFocusedStackLayer = 0;
+ if (mFocusedApp != null) {
+ final WindowList windows = mFocusedApp.allAppWindows;
+ for (int i = windows.size() - 1; i >= 0; --i) {
+ final WindowState win = windows.get(i);
+ final int animLayer = win.mWinAnimator.mAnimLayer;
+ if (win.mAttachedWindow == null && win.isVisibleLw() &&
+ animLayer > mFocusedStackLayer) {
+ mFocusedStackLayer = animLayer + LAYER_OFFSET_FOCUSED_STACK;
+ }
+ }
+ }
+ if (DEBUG_LAYERS) Slog.v(TAG, "Setting FocusedStackFrame to layer=" +
+ mFocusedStackLayer);
+ mFocusedStackFrame.setLayer(mFocusedStackLayer);
+ }
+
+ void setFocusedStackFrame() {
+ final TaskStack stack;
+ if (mFocusedApp != null) {
+ Task task = mTaskIdToTask.get(mFocusedApp.groupId);
+ stack = task.mStack;
+ task.getDisplayContent().setTouchExcludeRegion(stack);
+ } else {
+ stack = null;
+ }
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setFocusedStackFrame");
+ SurfaceControl.openTransaction();
+ try {
+ if (stack == null) {
+ mFocusedStackFrame.setVisibility(false);
+ } else {
+ final StackBox box = stack.mStackBox;
+ final Rect bounds = box.mBounds;
+ final boolean multipleStacks = box.mParent != null;
+ mFocusedStackFrame.setBounds(bounds);
+ mFocusedStackFrame.setVisibility(multipleStacks);
+ }
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> CLOSE TRANSACTION setFocusedStackFrame");
+ }
+ }
+
@Override
public void setFocusedApp(IBinder token, boolean moveFocusNow) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
@@ -3767,7 +3888,7 @@
@Override
public void setAppStartingWindow(IBinder token, String pkg,
int theme, CompatibilityInfo compatInfo,
- CharSequence nonLocalizedLabel, int labelRes, int icon,
+ CharSequence nonLocalizedLabel, int labelRes, int icon, int logo,
int windowFlags, IBinder transferFrom, boolean createIfNeeded) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppStartingWindow()")) {
@@ -3968,7 +4089,7 @@
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Creating StartingData");
mStartingIconInTransition = true;
wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
- labelRes, icon, windowFlags);
+ labelRes, icon, logo, windowFlags);
Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
// Note: we really want to do sendMessageAtFrontOfQueue() because we
// want to process the message ASAP, before any other queued
@@ -3997,6 +4118,14 @@
}
}
+ public void setAppFullscreen(IBinder token, boolean toOpaque) {
+ AppWindowToken atoken = findAppWindowToken(token);
+ if (atoken != null) {
+ atoken.appFullscreen = toOpaque;
+ requestTraversal();
+ }
+ }
+
boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
boolean visible, int transit, boolean performLayout) {
boolean delayed = false;
@@ -4346,11 +4475,13 @@
TAG, "Removing app " + wtoken + " delayed=" + delayed
+ " animation=" + wtoken.mAppAnimator.animation
+ " animating=" + wtoken.mAppAnimator.animating);
+ final Task task = mTaskIdToTask.get(wtoken.groupId);
+ DisplayContent displayContent = task.getDisplayContent();
if (delayed) {
// set the token aside because it has an active animation to be finished
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"removeAppToken make exiting: " + wtoken);
- mExitingAppTokens.add(wtoken);
+ displayContent.mExitingAppTokens.add(wtoken);
} else {
// Make sure there is no animation running on this token,
// so any windows associated with it will be removed as
@@ -4360,8 +4491,10 @@
}
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"removeAppToken: " + wtoken);
- mAppTokens.remove(wtoken);
- mAnimatingAppTokens.remove(wtoken);
+
+ if (task.removeAppToken(wtoken)) {
+ mTaskIdToTask.delete(wtoken.groupId);
+ }
wtoken.removed = true;
if (wtoken.startingData != null) {
startingToken = wtoken;
@@ -4413,80 +4546,99 @@
}
void dumpAppTokensLocked() {
- for (int i=mAppTokens.size()-1; i>=0; i--) {
- Slog.v(TAG, " #" + i + ": " + mAppTokens.get(i).token);
- }
- }
-
- void dumpAnimatingAppTokensLocked() {
- for (int i=mAnimatingAppTokens.size()-1; i>=0; i--) {
- Slog.v(TAG, " #" + i + ": " + mAnimatingAppTokens.get(i).token);
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ DisplayContent displayContent = iterator.next();
+ Slog.v(TAG, " Display " + displayContent.getDisplayId());
+ final ArrayList<Task> tasks = displayContent.getTasks();
+ int i = displayContent.numTokens();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
+ final AppWindowToken wtoken = tokens.get(tokenNdx);
+ Slog.v(TAG, " #" + --i + ": " + wtoken.token);
+ }
+ }
}
}
void dumpWindowsLocked() {
int i = 0;
- final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
- while (iterator.hasNext()) {
- final WindowState w = iterator.next();
+ mTmpWindowsIterator.reset(REVERSE_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
+ final WindowState w = mTmpWindowsIterator.next();
Slog.v(TAG, " #" + i++ + ": " + w);
}
}
- private int findWindowOffsetLocked(WindowList windows, int tokenPos) {
+ private int findAppWindowInsertionPointLocked(AppWindowToken target) {
+ final int taskId = target.groupId;
+ Task targetTask = mTaskIdToTask.get(taskId);
+ if (targetTask == null) {
+ Slog.w(TAG, "findAppWindowInsertionPointLocked: no Task for " + target + " taskId="
+ + taskId);
+ return 0;
+ }
+ DisplayContent displayContent = targetTask.getDisplayContent();
+ if (displayContent == null) {
+ Slog.w(TAG, "findAppWindowInsertionPointLocked: no DisplayContent for " + target);
+ return 0;
+ }
+ final WindowList windows = displayContent.getWindowList();
final int NW = windows.size();
- if (tokenPos >= mAnimatingAppTokens.size()) {
- int i = NW;
- while (i > 0) {
- i--;
- WindowState win = windows.get(i);
- if (win.getAppToken() != null) {
- return i+1;
- }
- }
- }
-
- while (tokenPos > 0) {
- // Find the first app token below the new position that has
- // a window displayed.
- final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);
- if (DEBUG_REORDER) Slog.v(TAG, "Looking for lower windows @ "
- + tokenPos + " -- " + wtoken.token);
- if (wtoken.sendingToBottom) {
- if (DEBUG_REORDER) Slog.v(TAG,
- "Skipping token -- currently sending to bottom");
- tokenPos--;
+ boolean found = false;
+ final ArrayList<Task> tasks = displayContent.getTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final Task task = tasks.get(taskNdx);
+ if (!found && task.taskId != taskId) {
continue;
}
- int i = wtoken.windows.size();
- while (i > 0) {
- i--;
- WindowState win = wtoken.windows.get(i);
- int j = win.mChildWindows.size();
- while (j > 0) {
- j--;
- WindowState cwin = win.mChildWindows.get(j);
- if (cwin.mSubLayer >= 0) {
- for (int pos=NW-1; pos>=0; pos--) {
- if (windows.get(pos) == cwin) {
- if (DEBUG_REORDER) Slog.v(TAG,
- "Found child win @" + (pos+1));
- return pos+1;
+ AppTokenList tokens = task.mAppTokens;
+ for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
+ final AppWindowToken wtoken = tokens.get(tokenNdx);
+ if (!found && wtoken == target) {
+ found = true;
+ }
+ if (found) {
+ // Find the first app token below the new position that has
+ // a window displayed.
+ if (DEBUG_REORDER) Slog.v(TAG, "Looking for lower windows in " + wtoken.token);
+ if (wtoken.sendingToBottom) {
+ if (DEBUG_REORDER) Slog.v(TAG, "Skipping token -- currently sending to bottom");
+ continue;
+ }
+ for (int i = wtoken.windows.size() - 1; i >= 0; --i) {
+ WindowState win = wtoken.windows.get(i);
+ for (int j = win.mChildWindows.size() - 1; j >= 0; --j) {
+ WindowState cwin = win.mChildWindows.get(j);
+ if (cwin.mSubLayer >= 0) {
+ for (int pos = NW - 1; pos >= 0; pos--) {
+ if (windows.get(pos) == cwin) {
+ if (DEBUG_REORDER) Slog.v(TAG,
+ "Found child win @" + (pos + 1));
+ return pos + 1;
+ }
+ }
+ }
+ }
+ for (int pos = NW - 1; pos >= 0; pos--) {
+ if (windows.get(pos) == win) {
+ if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos + 1));
+ return pos + 1;
}
}
}
}
- for (int pos=NW-1; pos>=0; pos--) {
- if (windows.get(pos) == win) {
- if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos+1));
- return pos+1;
- }
- }
}
- tokenPos--;
}
-
+ // Never put an app window underneath wallpaper.
+ for (int pos = NW - 1; pos >= 0; pos--) {
+ if (windows.get(pos).mIsWallpaper) {
+ if (DEBUG_REORDER) Slog.v(TAG, "Found wallpaper @" + pos);
+ return pos + 1;
+ }
+ }
return 0;
}
@@ -4533,198 +4685,231 @@
return index;
}
- @Override
- public void moveAppToken(int index, IBinder token) {
- if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
- "moveAppToken()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
-
- synchronized(mWindowMap) {
- if (DEBUG_REORDER) Slog.v(TAG, "Initial app tokens:");
- if (DEBUG_REORDER) dumpAppTokensLocked();
- final AppWindowToken wtoken = findAppWindowToken(token);
- final int oldIndex = mAppTokens.indexOf(wtoken);
- if (DEBUG_TOKEN_MOVEMENT || DEBUG_REORDER) Slog.v(TAG,
- "Start moving token " + wtoken + " initially at "
- + oldIndex);
- if (oldIndex > index && mAppTransition.isTransitionSet()) {
- // animation towards back has not started, copy old list for duration of animation.
- mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
- }
- if (wtoken == null || !mAppTokens.remove(wtoken)) {
- Slog.w(TAG, "Attempting to reorder token that doesn't exist: "
- + token + " (" + wtoken + ")");
- return;
- }
- mAppTokens.add(index, wtoken);
- if (DEBUG_REORDER) Slog.v(TAG, "Moved " + token + " to " + index + ":");
- else if (DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "Moved " + token + " to " + index);
- if (DEBUG_REORDER) dumpAppTokensLocked();
- if (!mAppTransition.isTransitionSet()) {
- // Not animating, bring animating app list in line with mAppTokens.
- mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
-
- // Bring window ordering, window focus and input window in line with new app token
- final long origId = Binder.clearCallingIdentity();
- if (DEBUG_REORDER) Slog.v(TAG, "Removing windows in " + token + ":");
- if (DEBUG_REORDER) dumpWindowsLocked();
- if (tmpRemoveAppWindowsLocked(wtoken)) {
- if (DEBUG_REORDER) Slog.v(TAG, "Adding windows back in:");
- if (DEBUG_REORDER) dumpWindowsLocked();
- DisplayContentsIterator iterator = new DisplayContentsIterator();
- while(iterator.hasNext()) {
- final DisplayContent displayContent = iterator.next();
- final WindowList windows = displayContent.getWindowList();
- final int pos = findWindowOffsetLocked(windows, index);
- final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
- if (pos != newPos) {
- displayContent.layoutNeeded = true;
- }
- }
- if (DEBUG_REORDER) Slog.v(TAG, "Final window list:");
- if (DEBUG_REORDER) dumpWindowsLocked();
- updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/);
- mInputMonitor.setUpdateInputWindowsNeededLw();
- performLayoutAndPlaceSurfacesLocked();
- mInputMonitor.updateInputWindowsLw(false /*force*/);
+ private void moveHomeTasksLocked(boolean toTop) {
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ if (toTop ^ displayContent.homeOnTop()) {
+ final ArrayList<Task> tasks = displayContent.getHomeStack().getTasks();
+ final int numTasks = tasks.size();
+ for (int i = 0; i < numTasks; ++i) {
+ if (toTop) {
+ // Keep pulling the bottom task off and moving it to the top.
+ moveTaskToTop(tasks.get(0).taskId);
+ } else {
+ // Keep pulling the top task off and moving it to the bottom.
+ moveTaskToBottom(tasks.get(numTasks - 1).taskId);
}
- Binder.restoreCallingIdentity(origId);
}
}
}
- private void removeAppTokensLocked(List<IBinder> tokens) {
- // XXX This should be done more efficiently!
- // (take advantage of the fact that both lists should be
- // ordered in the same way.)
- int N = tokens.size();
- for (int i=0; i<N; i++) {
- IBinder token = tokens.get(i);
- final AppWindowToken wtoken = findAppWindowToken(token);
- if (DEBUG_REORDER || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
- "Temporarily removing " + wtoken + " from " + mAppTokens.indexOf(wtoken));
- if (!mAppTokens.remove(wtoken)) {
- Slog.w(TAG, "Attempting to reorder token that doesn't exist: "
- + token + " (" + wtoken + ")");
- i--;
- N--;
- }
- }
- }
+ private void moveStackWindowsLocked(TaskStack stack) {
+ DisplayContent displayContent = stack.getDisplayContent();
- private void moveAppWindowsLocked(List<IBinder> tokens, int tokenPos) {
// First remove all of the windows from the list.
- final int N = tokens.size();
- int i;
- for (i=0; i<N; i++) {
- WindowToken token = mTokenMap.get(tokens.get(i));
- if (token != null) {
- tmpRemoveAppWindowsLocked(token);
+ final ArrayList<Task> tasks = stack.getTasks();
+ final int numTasks = tasks.size();
+ for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
+ AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ final int numTokens = tokens.size();
+ for (int tokenNdx = numTokens - 1; tokenNdx >= 0; --tokenNdx) {
+ tmpRemoveAppWindowsLocked(tokens.get(tokenNdx));
}
}
// And now add them back at the correct place.
- DisplayContentsIterator iterator = new DisplayContentsIterator();
- while (iterator.hasNext()) {
- final DisplayContent displayContent = iterator.next();
- final WindowList windows = displayContent.getWindowList();
- // Where to start adding?
- int pos = findWindowOffsetLocked(windows, tokenPos);
- for (i=0; i<N; i++) {
- WindowToken token = mTokenMap.get(tokens.get(i));
- if (token != null) {
- final int newPos = reAddAppWindowsLocked(displayContent, pos, token);
+ // Where to start adding?
+ for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
+ AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ int pos = findAppWindowInsertionPointLocked(tokens.get(0));
+ final int numTokens = tokens.size();
+ for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
+ final AppWindowToken wtoken = tokens.get(tokenNdx);
+ if (wtoken != null) {
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
if (newPos != pos) {
displayContent.layoutNeeded = true;
}
pos = newPos;
}
}
- if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/)) {
- assignLayersLocked(windows);
- }
+ }
+
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/)) {
+ assignLayersLocked(displayContent.getWindowList());
}
mInputMonitor.setUpdateInputWindowsNeededLw();
-
- // Note that the above updateFocusedWindowLocked used to sit here.
-
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
//dump();
}
- @Override
- public void moveAppTokensToTop(List<IBinder> tokens) {
- if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
- "moveAppTokensToTop()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
-
+ public void moveTaskToTop(int taskId) {
final long origId = Binder.clearCallingIdentity();
- synchronized(mWindowMap) {
- removeAppTokensLocked(tokens);
- final int N = tokens.size();
- for (int i=0; i<N; i++) {
- AppWindowToken wt = findAppWindowToken(tokens.get(i));
- if (wt != null) {
- if (DEBUG_TOKEN_MOVEMENT || DEBUG_REORDER) Slog.v(TAG,
- "Adding next to top: " + wt);
- mAppTokens.add(wt);
- if (mAppTransition.isTransitionSet()) {
- wt.sendingToBottom = false;
- }
+ try {
+ synchronized(mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ Slog.e(TAG, "moveTaskToTop: taskId=" + taskId + " not found in mTaskIdToTask");
+ return;
}
+ final TaskStack stack = task.mStack;
+ final DisplayContent displayContent = task.getDisplayContent();
+ final boolean isHomeStackTask = stack.isHomeStack();
+ final boolean homeIsOnTop = displayContent.homeOnTop();
+ if (!isHomeStackTask && homeIsOnTop) {
+ // First move move the home tasks all to the bottom to rearrange the windows.
+ moveHomeTasksLocked(false);
+ // Now move the stack itself.
+ displayContent.moveHomeStackBox(false);
+ } else if (isHomeStackTask && !homeIsOnTop) {
+ // Move the stack to the top.
+ displayContent.moveHomeStackBox(true);
+ }
+ stack.moveTaskToTop(task);
+ displayContent.moveStack(stack, true);
+ moveStackWindowsLocked(stack);
}
-
- mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
- moveAppWindowsLocked(tokens, mAppTokens.size());
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
- @Override
- public void moveAppTokensToBottom(List<IBinder> tokens) {
- if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
- "moveAppTokensToBottom()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
-
+ public void moveTaskToBottom(int taskId) {
final long origId = Binder.clearCallingIdentity();
- synchronized(mWindowMap) {
- final int N = tokens.size();
- if (N > 0) {
- // animating towards back, hang onto old list for duration of animation.
- mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
+ try {
+ synchronized(mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ Slog.e(TAG, "moveTaskToBottom: taskId=" + taskId
+ + " not found in mTaskIdToTask");
+ return;
+ }
+ final TaskStack stack = task.mStack;
+ stack.moveTaskToBottom(task);
+ task.getDisplayContent().moveStack(stack, false);
+ moveStackWindowsLocked(stack);
}
- removeAppTokensLocked(tokens);
- int pos = 0;
- for (int i=0; i<N; i++) {
- AppWindowToken wt = findAppWindowToken(tokens.get(i));
- if (wt != null) {
- if (DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
- "Adding next to bottom: " + wt + " at " + pos);
- mAppTokens.add(pos, wt);
- if (mAppTransition.isTransitionSet()) {
- wt.sendingToBottom = true;
- }
- pos++;
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ /**
+ * Create a new TaskStack and place it next to an existing stack.
+ * @param stackId The unique identifier of the new stack.
+ * @param relativeStackBoxId The existing stack that this stack goes before or after.
+ * @param position One of:
+ * {@link StackBox#TASK_STACK_GOES_BEFORE}
+ * {@link StackBox#TASK_STACK_GOES_AFTER}
+ * {@link StackBox#TASK_STACK_GOES_ABOVE}
+ * {@link StackBox#TASK_STACK_GOES_BELOW}
+ * {@link StackBox#TASK_STACK_GOES_UNDER}
+ * {@link StackBox#TASK_STACK_GOES_OVER}
+ * @param weight Relative weight for determining how big to make the new TaskStack.
+ */
+ public void createStack(int stackId, int relativeStackBoxId, int position, float weight) {
+ synchronized (mWindowMap) {
+ if (position <= StackBox.TASK_STACK_GOES_BELOW &&
+ (weight < STACK_WEIGHT_MIN || weight > STACK_WEIGHT_MAX)) {
+ throw new IllegalArgumentException(
+ "createStack: weight must be between " + STACK_WEIGHT_MIN + " and " +
+ STACK_WEIGHT_MAX + ", weight=" + weight);
+ }
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ TaskStack stack = displayContent.createStack(this, stackId, relativeStackBoxId,
+ position, weight);
+ if (stack != null) {
+ mStackIdToStack.put(stackId, stack);
+ displayContent.moveStack(stack, true);
+ performLayoutAndPlaceSurfacesLocked();
+ return;
}
}
-
- mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
- moveAppWindowsLocked(tokens, 0);
+ Slog.e(TAG, "createStack: Unable to find relativeStackBoxId=" + relativeStackBoxId);
}
- Binder.restoreCallingIdentity(origId);
+ }
+
+ public int removeStack(int stackId) {
+ synchronized (mWindowMap) {
+ final TaskStack stack = mStackIdToStack.get(stackId);
+ if (stack != null) {
+ mStackIdToStack.delete(stackId);
+ int nextStackId = stack.remove();
+ stack.getDisplayContent().layoutNeeded = true;
+ requestTraversalLocked();
+ return nextStackId;
+ }
+ if (DEBUG_STACK) Slog.i(TAG, "removeStack: could not find stackId=" + stackId);
+ }
+ return HOME_STACK_ID;
+ }
+
+ public void removeTask(int taskId) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ if (DEBUG_STACK) Slog.i(TAG, "removeTask: could not find taskId=" + taskId);
+ return;
+ }
+ final TaskStack stack = task.mStack;
+ stack.removeTask(task);
+ stack.getDisplayContent().layoutNeeded = true;
+ }
+ }
+
+ public void addTask(int taskId, int stackId, boolean toTop) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ return;
+ }
+ TaskStack stack = mStackIdToStack.get(stackId);
+ stack.addTask(task, toTop);
+ final DisplayContent displayContent = stack.getDisplayContent();
+ displayContent.layoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ }
+
+ public void resizeStackBox(int stackBoxId, float weight) {
+ if (weight < STACK_WEIGHT_MIN || weight > STACK_WEIGHT_MAX) {
+ throw new IllegalArgumentException(
+ "resizeStack: weight must be between " + STACK_WEIGHT_MIN + " and " +
+ STACK_WEIGHT_MAX + ", weight=" + weight);
+ }
+ synchronized (mWindowMap) {
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ if (iterator.next().resizeStack(stackBoxId, weight)) {
+ performLayoutAndPlaceSurfacesLocked();
+ return;
+ }
+ }
+ }
+ throw new IllegalArgumentException("resizeStack: stackBoxId " + stackBoxId
+ + " not found.");
+ }
+
+ public ArrayList<StackBoxInfo> getStackBoxInfos() {
+ synchronized(mWindowMap) {
+ return getDefaultDisplayContentLocked().getStackBoxInfos();
+ }
+ }
+
+ public Rect getStackBounds(int stackId) {
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ Rect bounds = iterator.next().getStackBounds(stackId);
+ if (bounds != null) {
+ return bounds;
+ }
+ }
+ return null;
}
// -------------------------------------------------------------
@@ -4846,9 +5031,9 @@
@Override
public void closeSystemDialogs(String reason) {
synchronized(mWindowMap) {
- final AllWindowsIterator iterator = new AllWindowsIterator();
- while (iterator.hasNext()) {
- final WindowState w = iterator.next();
+ mTmpWindowsIterator.reset(FORWARD_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
+ final WindowState w = mTmpWindowsIterator.next();
if (w.mHasSurface) {
try {
w.mClient.closeSystemDialogs(reason);
@@ -4929,6 +5114,16 @@
mAnimatorDurationScale };
}
+ @Override
+ public void registerPointerEventListener(PointerEventListener listener) {
+ mPointerEventDispatcher.registerInputEventListener(listener);
+ }
+
+ @Override
+ public void unregisterPointerEventListener(PointerEventListener listener) {
+ mPointerEventDispatcher.unregisterInputEventListener(listener);
+ }
+
// Called by window manager policy. Not exposed externally.
@Override
public int getLidState() {
@@ -4948,12 +5143,6 @@
// Called by window manager policy. Not exposed externally.
@Override
- public InputChannel monitorInput(String inputChannelName) {
- return mInputManager.monitorInput(inputChannelName);
- }
-
- // Called by window manager policy. Not exposed externally.
- @Override
public void switchKeyboardLayout(int deviceId, int direction) {
mInputManager.switchKeyboardLayout(deviceId, direction);
}
@@ -4980,22 +5169,16 @@
public void setCurrentUser(final int newUserId) {
synchronized (mWindowMap) {
+ int oldUserId = mCurrentUserId;
mCurrentUserId = newUserId;
mPolicy.setCurrentUserLw(newUserId);
// Hide windows that should not be seen by the new user.
DisplayContentsIterator iterator = new DisplayContentsIterator();
while (iterator.hasNext()) {
- final WindowList windows = iterator.next().getWindowList();
- for (int i = 0; i < windows.size(); i++) {
- final WindowState win = windows.get(i);
- if (win.isHiddenFromUserLocked()) {
- Slog.w(TAG, "current user violation " + newUserId + " hiding "
- + win + ", attrs=" + win.mAttrs.type + ", belonging to "
- + win.mOwnerUid);
- win.hideLw(false);
- }
- }
+ DisplayContent displayContent = iterator.next();
+ displayContent.switchUserStacks(oldUserId, newUserId);
+ rebuildAppWindowListLocked(displayContent);
}
performLayoutAndPlaceSurfacesLocked();
}
@@ -5144,8 +5327,9 @@
if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG, "******************** ENABLING SCREEN!");
if (false) {
StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
+ PrintWriter pw = new FastPrintWriter(sw, false, 1024);
this.dump(null, pw, null);
+ pw.flush();
Slog.i(TAG, sw.toString());
}
try {
@@ -5242,9 +5426,9 @@
// the background..)
if (on) {
boolean isVisible = false;
- final AllWindowsIterator iterator = new AllWindowsIterator();
- while (iterator.hasNext()) {
- final WindowState ws = iterator.next();
+ mTmpWindowsIterator.reset(FORWARD_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
+ final WindowState ws = mTmpWindowsIterator.next();
if (ws.mSession.mPid == pid && ws.isVisibleLw()) {
isVisible = true;
break;
@@ -5345,6 +5529,7 @@
boolean including = false;
appWin = null;
final WindowList windows = displayContent.getWindowList();
+ final Rect stackBounds = new Rect();
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState ws = windows.get(i);
if (!ws.mHasSurface) {
@@ -5367,6 +5552,7 @@
continue;
}
appWin = ws;
+ stackBounds.set(ws.getStackBounds());
}
}
@@ -5392,6 +5578,7 @@
int right = wf.right - cr.right;
int bottom = wf.bottom - cr.bottom;
frame.union(left, top, right, bottom);
+ frame.intersect(stackBounds);
}
if (ws.mAppToken != null && ws.mAppToken.token == appToken &&
@@ -5435,9 +5622,11 @@
// Constrain thumbnail to smaller of screen width or height. Assumes aspect
// of thumbnail is the same as the screen (in landscape) or square.
+ scale = Math.max(width / (float) fw, height / (float) fh);
+ /*
float targetWidthScale = width / (float) fw;
float targetHeightScale = height / (float) fh;
- if (dw <= dh) {
+ if (fw <= fh) {
scale = targetWidthScale;
// If aspect of thumbnail is the same as the screen (in landscape),
// select the slightly larger value so we fill the entire bitmap
@@ -5452,6 +5641,7 @@
scale = targetWidthScale;
}
}
+ */
// The screen shot will contain the entire screen.
dw = (int)(dw*scale);
@@ -5486,9 +5676,11 @@
}
Bitmap bm = Bitmap.createBitmap(width, height, rawss.getConfig());
+ frame.scale(scale);
Matrix matrix = new Matrix();
ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix);
- matrix.postTranslate(-FloatMath.ceil(frame.left*scale), -FloatMath.ceil(frame.top*scale));
+ // TODO: Test for RTL vs. LTR and use frame.right-width instead of -frame.left
+ matrix.postTranslate(-FloatMath.ceil(frame.left), -FloatMath.ceil(frame.top));
Canvas canvas = new Canvas(bm);
canvas.drawColor(0xFF000000);
canvas.drawBitmap(rawss, matrix, null);
@@ -5499,14 +5691,16 @@
int[] buffer = new int[bm.getWidth() * bm.getHeight()];
bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
boolean allBlack = true;
+ final int firstColor = buffer[0];
for (int i = 0; i < buffer.length; i++) {
- if (buffer[i] != Color.BLACK) {
+ if (buffer[i] != firstColor) {
allBlack = false;
break;
}
}
if (allBlack) {
- Slog.i(TAG, "Screenshot " + appWin + " was all black! mSurfaceLayer=" +
+ Slog.i(TAG, "Screenshot " + appWin + " was monochrome(" +
+ Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
(appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null") +
" minLayer=" + minLayer + " maxLayer=" + maxLayer);
}
@@ -5698,9 +5892,10 @@
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION);
mWaitingForConfig = true;
- getDefaultDisplayContentLocked().layoutNeeded = true;
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ displayContent.layoutNeeded = true;
final int[] anim = new int[2];
- if (mAnimator.isDimmingLocked(Display.DEFAULT_DISPLAY)) {
+ if (displayContent.isDimming()) {
anim[0] = anim[1] = 0;
} else {
mPolicy.selectRotationAnimationLw(anim);
@@ -5718,7 +5913,6 @@
// the rotation animation for the new rotation.
computeScreenConfigurationLocked(null);
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (!inTransaction) {
if (SHOW_TRANSACTIONS) {
@@ -6227,9 +6421,9 @@
}
synchronized (mWindowMap) {
- final AllWindowsIterator iterator = new AllWindowsIterator();
- while (iterator.hasNext()) {
- final WindowState w = iterator.next();
+ mTmpWindowsIterator.reset(FORWARD_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
+ final WindowState w = mTmpWindowsIterator.next();
if (System.identityHashCode(w) == hashCode) {
return w;
}
@@ -6425,8 +6619,9 @@
displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
displayInfo.appWidth = appWidth;
displayInfo.appHeight = appHeight;
- displayInfo.getLogicalMetrics(mRealDisplayMetrics, null);
- displayInfo.getAppMetrics(mDisplayMetrics, null);
+ displayInfo.getLogicalMetrics(mRealDisplayMetrics,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ displayInfo.getAppMetrics(mDisplayMetrics);
mDisplayManagerService.setDisplayInfoOverrideFromWindowManager(
displayContent.getDisplayId(), displayInfo);
}
@@ -6767,6 +6962,8 @@
displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth;
displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;
displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
+ displayContent.mBaseDisplayRect.set(0, 0,
+ displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight);
}
}
}
@@ -6779,10 +6976,10 @@
// TODO(multidisplay): Call isScreenOn for each display.
private void sendScreenStatusToClientsLocked() {
final boolean on = mPowerManager.isScreenOn();
- final AllWindowsIterator iterator = new AllWindowsIterator();
- while (iterator.hasNext()) {
+ mTmpWindowsIterator.reset(FORWARD_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
try {
- iterator.next().mClient.dispatchScreenState(on);
+ mTmpWindowsIterator.next().mClient.dispatchScreenState(on);
} catch (RemoteException e) {
// Ignored
}
@@ -6824,6 +7021,8 @@
public static final int DO_DISPLAY_CHANGED = 29;
public static final int CLIENT_FREEZE_TIMEOUT = 30;
+ public static final int TAP_OUTSIDE_STACK = 31;
+ public static final int NOTIFY_ACTIVITY_DRAWN = 32;
@Override
public void handleMessage(Message msg) {
@@ -6907,7 +7106,7 @@
try {
view = mPolicy.addStartingWindow(
wtoken.token, sd.pkg, sd.theme, sd.compatInfo,
- sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.windowFlags);
+ sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo, sd.windowFlags);
} catch (Exception e) {
Slog.w(TAG, "Exception when adding starting window", e);
}
@@ -7068,8 +7267,6 @@
if (mAppTransition.isTransitionSet()) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** APP TRANSITION TIMEOUT");
mAppTransition.setTimeout();
- mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -7114,13 +7311,16 @@
case APP_FREEZE_TIMEOUT: {
synchronized (mWindowMap) {
Slog.w(TAG, "App freeze timeout expired.");
- int i = mAppTokens.size();
- while (i > 0) {
- i--;
- AppWindowToken tok = mAppTokens.get(i);
- if (tok.mAppAnimator.freezingScreen) {
- Slog.w(TAG, "Force clearing freeze: " + tok);
- unsetAppFreezingScreenLocked(tok, true, true);
+ DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final ArrayList<Task> tasks = displayContent.getTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
+ AppWindowToken tok = tokens.get(tokenNdx);
+ if (tok.mAppAnimator.freezingScreen) {
+ Slog.w(TAG, "Force clearing freeze: " + tok);
+ unsetAppFreezingScreenLocked(tok, true, true);
+ }
}
}
}
@@ -7242,6 +7442,26 @@
handleDisplayChangedLocked(msg.arg1);
}
break;
+
+ case TAP_OUTSIDE_STACK: {
+ int stackId;
+ synchronized (mWindowMap) {
+ stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2);
+ }
+ if (stackId >= 0) {
+ try {
+ mActivityManager.setFocusedStack(stackId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ break;
+ case NOTIFY_ACTIVITY_DRAWN:
+ try {
+ mActivityManager.notifyActivityDrawn((IBinder) msg.obj);
+ } catch (RemoteException e) {
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -7327,7 +7547,7 @@
public void getInitialDisplaySize(int displayId, Point size) {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent != null) {
+ if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
synchronized(displayContent.mDisplaySizeLock) {
size.x = displayContent.mInitialDisplayWidth;
size.y = displayContent.mInitialDisplayHeight;
@@ -7340,7 +7560,7 @@
public void getBaseDisplaySize(int displayId, Point size) {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent != null) {
+ if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
synchronized(displayContent.mDisplaySizeLock) {
size.x = displayContent.mBaseDisplayWidth;
size.y = displayContent.mBaseDisplayHeight;
@@ -7455,7 +7675,7 @@
public int getInitialDisplayDensity(int displayId) {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent != null) {
+ if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
synchronized(displayContent.mDisplaySizeLock) {
return displayContent.mInitialDisplayDensity;
}
@@ -7468,7 +7688,7 @@
public int getBaseDisplayDensity(int displayId) {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent != null) {
+ if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
synchronized(displayContent.mDisplaySizeLock) {
return displayContent.mBaseDisplayDensity;
}
@@ -7585,11 +7805,6 @@
}
}
- @Override
- public boolean hasSystemNavBar() {
- return mPolicy.hasSystemNavBar();
- }
-
// -------------------------------------------------------------
// Internals
// -------------------------------------------------------------
@@ -7654,8 +7869,7 @@
win.mRebuilding = true;
mRebuildTmp[numRemoved] = win;
mWindowsChanged = true;
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
- "Rebuild removing window: " + win);
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Rebuild removing window: " + win);
NW--;
numRemoved++;
continue;
@@ -7676,26 +7890,33 @@
// in the main app list, but still have windows shown. We put them
// in the back because now that the animation is over we no longer
// will care about them.
- int NT = mExitingAppTokens.size();
+ AppTokenList exitingAppTokens = displayContent.mExitingAppTokens;
+ int NT = exitingAppTokens.size();
for (int j=0; j<NT; j++) {
- i = reAddAppWindowsLocked(displayContent, i, mExitingAppTokens.get(j));
+ i = reAddAppWindowsLocked(displayContent, i, exitingAppTokens.get(j));
}
// And add in the still active app tokens in Z order.
- NT = mAnimatingAppTokens.size();
- for (int j=0; j<NT; j++) {
- i = reAddAppWindowsLocked(displayContent, i, mAnimatingAppTokens.get(j));
+ final ArrayList<Task> tasks = displayContent.getTasks();
+ final int numTasks = tasks.size();
+ for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
+ final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ final int numTokens = tokens.size();
+ for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
+ final AppWindowToken wtoken = tokens.get(tokenNdx);
+ i = reAddAppWindowsLocked(displayContent, i, wtoken);
+ }
}
i -= lastBelow;
if (i != numRemoved) {
- Slog.w(TAG, "Rebuild removed " + numRemoved
- + " windows but added " + i);
+ Slog.w(TAG, "Rebuild removed " + numRemoved + " windows but added " + i,
+ new RuntimeException("here").fillInStackTrace());
for (i=0; i<numRemoved; i++) {
WindowState ws = mRebuildTmp[i];
if (ws.mRebuilding) {
StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
+ PrintWriter pw = new FastPrintWriter(sw, false, 1024);
ws.dump(pw, "", true);
pw.flush();
Slog.w(TAG, "This window was lost: " + ws);
@@ -7704,7 +7925,7 @@
}
}
Slog.w(TAG, "Current app token list:");
- dumpAnimatingAppTokensLocked();
+ dumpAppTokensLocked();
Slog.w(TAG, "Final window list:");
dumpWindowsLocked();
}
@@ -7741,13 +7962,14 @@
layerChanged = true;
anyLayerChanged = true;
}
+ final AppWindowToken wtoken = w.mAppToken;
oldLayer = winAnimator.mAnimLayer;
if (w.mTargetAppToken != null) {
winAnimator.mAnimLayer =
w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;
- } else if (w.mAppToken != null) {
+ } else if (wtoken != null) {
winAnimator.mAnimLayer =
- w.mLayer + w.mAppToken.mAppAnimator.animLayerAdjustment;
+ w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
} else {
winAnimator.mAnimLayer = w.mLayer;
}
@@ -7760,15 +7982,15 @@
layerChanged = true;
anyLayerChanged = true;
}
- if (layerChanged && mAnimator.isDimmingLocked(winAnimator)) {
- // Force an animation pass just to update the mDimAnimator layer.
+ if (layerChanged && w.getStack().isDimming(winAnimator)) {
+ // Force an animation pass just to update the mDimLayer layer.
scheduleAnimationLocked();
}
if (DEBUG_LAYERS) Slog.v(TAG, "Assign layer " + w + ": "
+ "mBase=" + w.mBaseLayer
+ " mLayer=" + w.mLayer
- + (w.mAppToken == null ?
- "" : " mAppLayer=" + w.mAppToken.mAppAnimator.animLayerAdjustment)
+ + (wtoken == null ?
+ "" : " mAppLayer=" + wtoken.mAppAnimator.animLayerAdjustment)
+ " =mAnimLayer=" + winAnimator.mAnimLayer);
//System.out.println(
// "Assigned layer " + curLayer + " to " + w.mClient.asBinder());
@@ -7906,6 +8128,10 @@
mScreenRect.set(0, 0, dw, dh);
}
+ Rect contentRect = new Rect();
+ mPolicy.getContentRectLw(contentRect);
+ displayContent.setStackBoxSize(contentRect);
+
int seq = mLayoutSeq+1;
if (seq < 0) seq = 0;
mLayoutSeq = seq;
@@ -8355,11 +8581,17 @@
mAppTransition.setIdle();
// Restore window app tokens to the ActivityManager views
- for (int i = mAnimatingAppTokens.size() - 1; i >= 0; i--) {
- mAnimatingAppTokens.get(i).sendingToBottom = false;
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final ArrayList<Task> tasks = displayContent.getTasks();
+ final int numTasks = tasks.size();
+ for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
+ final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ final int numTokens = tokens.size();
+ for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
+ final AppWindowToken wtoken = tokens.get(tokenNdx);
+ wtoken.sendingToBottom = false;
+ }
}
- mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
rebuildAppWindowListLocked();
changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
@@ -8398,7 +8630,7 @@
|| winAnimator.mSurfaceResized
|| configChanged) {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
- Slog.v(TAG, "Resize reasons: "
+ Slog.v(TAG, "Resize reasons for w=" + w + ": "
+ " contentInsetsChanged=" + w.mContentInsetsChanged
+ " " + w.mContentInsets.toShortString()
+ " visibleInsetsChanged=" + w.mVisibleInsetsChanged
@@ -8509,30 +8741,36 @@
if ((attrs.flags & FLAG_DIM_BEHIND) != 0
&& w.isDisplayedLw()
&& !w.mExiting) {
- mInnerFields.mDimming = true;
final WindowStateAnimator winAnimator = w.mWinAnimator;
- if (!mAnimator.isDimmingLocked(winAnimator)) {
+ final TaskStack stack = w.getStack();
+ stack.setDimmingTag();
+ if (!stack.isDimming(winAnimator)) {
if (localLOGV) Slog.v(TAG, "Win " + w + " start dimming.");
- startDimmingLocked(winAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount);
+ stack.startDimmingIfNeeded(winAnimator);
}
}
}
- private void updateAllDrawnLocked() {
+ private void updateAllDrawnLocked(DisplayContent displayContent) {
// See if any windows have been drawn, so they (and others
// associated with them) can now be shown.
- final ArrayList<AppWindowToken> appTokens = mAnimatingAppTokens;
- final int NT = appTokens.size();
- for (int i=0; i<NT; i++) {
- AppWindowToken wtoken = appTokens.get(i);
- if (!wtoken.allDrawn) {
- int numInteresting = wtoken.numInterestingWindows;
- if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
- if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
- "allDrawn: " + wtoken
- + " interesting=" + numInteresting
- + " drawn=" + wtoken.numDrawnWindows);
- wtoken.allDrawn = true;
+ final ArrayList<Task> tasks = displayContent.getTasks();
+ final int numTasks = tasks.size();
+ for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
+ final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ final int numTokens = tokens.size();
+ for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
+ final AppWindowToken wtoken = tokens.get(tokenNdx);
+ if (!wtoken.allDrawn) {
+ int numInteresting = wtoken.numInterestingWindows;
+ if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG,
+ "allDrawn: " + wtoken
+ + " interesting=" + numInteresting
+ + " drawn=" + wtoken.numDrawnWindows);
+ wtoken.allDrawn = true;
+ mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, wtoken.token).sendToTarget();
+ }
}
}
}
@@ -8556,13 +8794,17 @@
}
// Initialize state of exiting tokens.
- for (i=mExitingTokens.size()-1; i>=0; i--) {
- mExitingTokens.get(i).hasVisible = false;
- }
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ for (i=displayContent.mExitingTokens.size()-1; i>=0; i--) {
+ displayContent.mExitingTokens.get(i).hasVisible = false;
+ }
- // Initialize state of exiting applications.
- for (i=mExitingAppTokens.size()-1; i>=0; i--) {
- mExitingAppTokens.get(i).hasVisible = false;
+ // Initialize state of exiting applications.
+ for (i=displayContent.mExitingAppTokens.size()-1; i>=0; i--) {
+ displayContent.mExitingAppTokens.get(i).hasVisible = false;
+ }
}
mInnerFields.mHoldScreen = null;
@@ -8591,10 +8833,10 @@
}
boolean focusDisplayed = false;
- boolean updateAllDrawn = false;
- DisplayContentsIterator iterator = new DisplayContentsIterator();
+ iterator = new DisplayContentsIterator();
while (iterator.hasNext()) {
+ boolean updateAllDrawn = false;
final DisplayContent displayContent = iterator.next();
WindowList windows = displayContent.getWindowList();
DisplayInfo displayInfo = displayContent.getDisplayInfo();
@@ -8674,8 +8916,8 @@
} while (displayContent.pendingLayoutChanges != 0);
mInnerFields.mObscured = false;
- mInnerFields.mDimming = false;
mInnerFields.mSyswin = false;
+ displayContent.resetDimming();
// Only used if default window
final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
@@ -8692,7 +8934,7 @@
handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
}
- if (!mInnerFields.mDimming) {
+ if (!w.getStack().testDimmingTag()) {
handleFlagDimBehind(w, innerDw, innerDh);
}
@@ -8740,7 +8982,7 @@
// make this happen.
displayContent.pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ if (DEBUG_LAYOUT_REPEATS) {
debugLayoutRepeats(
"dream and commitFinishDrawingLocked true",
displayContent.pendingLayoutChanges);
@@ -8752,7 +8994,7 @@
mInnerFields.mWallpaperMayChange = true;
displayContent.pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ if (DEBUG_LAYOUT_REPEATS) {
debugLayoutRepeats(
"wallpaper and commitFinishDrawingLocked true",
displayContent.pendingLayoutChanges);
@@ -8778,8 +9020,7 @@
}
if ((w.isOnScreen() || winAnimator.mAttrType == TYPE_BASE_APPLICATION)
&& !w.mExiting && !w.mDestroying) {
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) {
+ if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
+ ", isAnimating=" + winAnimator.isAnimating());
if (!w.isDrawnLw()) {
@@ -8796,8 +9037,7 @@
atoken.numInterestingWindows++;
if (w.isDrawnLw()) {
atoken.numDrawnWindows++;
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+ if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG,
"tokenMayBeDrawn: " + atoken
+ " freezingScreen=" + atoken.mAppAnimator.freezingScreen
+ " mAppFreezing=" + w.mAppFreezing);
@@ -8835,13 +9075,11 @@
mDisplayManagerService.setDisplayHasContent(displayId, hasUniqueContent,
true /* inTraversal, must call performTraversalInTrans... below */);
- if (!mInnerFields.mDimming && mAnimator.isDimmingLocked(displayId)) {
- stopDimmingLocked(displayId);
- }
- }
+ getDisplayContentLocked(displayId).stopDimmingIfNeeded();
- if (updateAllDrawn) {
- updateAllDrawnLocked();
+ if (updateAllDrawn) {
+ updateAllDrawnLocked(displayContent);
+ }
}
if (focusDisplayed) {
@@ -8898,8 +9136,7 @@
mInnerFields.mWallpaperForceHidingChanged = false;
if (mInnerFields.mWallpaperMayChange) {
- if (WindowManagerService.DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
- "Wallpaper may change! Adjusting");
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
defaultDisplay.pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("WallpaperMayChange",
@@ -9012,30 +9249,38 @@
}
// Time to remove any exiting tokens?
- for (i=mExitingTokens.size()-1; i>=0; i--) {
- WindowToken token = mExitingTokens.get(i);
- if (!token.hasVisible) {
- mExitingTokens.remove(i);
- if (token.windowType == TYPE_WALLPAPER) {
- mWallpaperTokens.remove(token);
+ iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ ArrayList<WindowToken> exitingTokens = displayContent.mExitingTokens;
+ for (i = exitingTokens.size() - 1; i >= 0; i--) {
+ WindowToken token = exitingTokens.get(i);
+ if (!token.hasVisible) {
+ exitingTokens.remove(i);
+ if (token.windowType == TYPE_WALLPAPER) {
+ mWallpaperTokens.remove(token);
+ }
}
}
- }
- // Time to remove any exiting applications?
- for (i=mExitingAppTokens.size()-1; i>=0; i--) {
- AppWindowToken token = mExitingAppTokens.get(i);
- if (!token.hasVisible && !mClosingApps.contains(token)) {
- // Make sure there is no animation running on this token,
- // so any windows associated with it will be removed as
- // soon as their animations are complete
- token.mAppAnimator.clearAnimation();
- token.mAppAnimator.animating = false;
- if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
- "performLayout: App token exiting now removed" + token);
- mAppTokens.remove(token);
- mAnimatingAppTokens.remove(token);
- mExitingAppTokens.remove(i);
+ // Time to remove any exiting applications?
+ AppTokenList exitingAppTokens = displayContent.mExitingAppTokens;
+ for (i = exitingAppTokens.size() - 1; i >= 0; i--) {
+ AppWindowToken token = exitingAppTokens.get(i);
+ if (!token.hasVisible && !mClosingApps.contains(token)) {
+ // Make sure there is no animation running on this token,
+ // so any windows associated with it will be removed as
+ // soon as their animations are complete
+ token.mAppAnimator.clearAnimation();
+ token.mAppAnimator.animating = false;
+ if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
+ "performLayout: App token exiting now removed" + token);
+ final Task task = mTaskIdToTask.get(token.groupId);
+ if (task != null && task.removeAppToken(token)) {
+ mTaskIdToTask.delete(token.groupId);
+ }
+ exitingAppTokens.remove(i);
+ }
}
}
@@ -9055,7 +9300,7 @@
defaultDisplay.layoutNeeded = true;
}
- DisplayContentsIterator iterator = new DisplayContentsIterator();
+ iterator = new DisplayContentsIterator();
while (iterator.hasNext()) {
DisplayContent displayContent = iterator.next();
if (displayContent.pendingLayoutChanges != 0) {
@@ -9126,6 +9371,8 @@
}
}
+ setFocusedStackFrame();
+
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
@@ -9235,14 +9482,6 @@
}
}
- void startDimmingLocked(final WindowStateAnimator winAnimator, final float target) {
- mAnimator.setDimWinAnimatorLocked(winAnimator.mWin.getDisplayId(), winAnimator);
- }
-
- void stopDimmingLocked(int displayId) {
- mAnimator.setDimWinAnimatorLocked(displayId, null);
- }
-
private boolean needsLayout() {
DisplayContentsIterator iterator = new DisplayContentsIterator();
while (iterator.hasNext()) {
@@ -9288,6 +9527,24 @@
return doRequest;
}
+ /** If a window that has an animation specifying a colored background is the current wallpaper
+ * target, then the color goes *below* the wallpaper so we don't cause the wallpaper to
+ * suddenly disappear. */
+ int adjustAnimationBackground(WindowStateAnimator winAnimator) {
+ final WindowState win = winAnimator.mWin;
+ if (mWallpaperTarget == win || mLowerWallpaperTarget == win
+ || mUpperWallpaperTarget == win) {
+ WindowList windows = win.getWindowList();
+ for (int i = windows.size() - 1; i >= 0; --i) {
+ WindowState testWin = windows.get(i);
+ if (testWin.mIsWallpaper) {
+ return testWin.mWinAnimator.mAnimLayer;
+ }
+ }
+ }
+ return winAnimator.mAnimLayer;
+ }
+
boolean reclaimSomeSurfaceMemoryLocked(WindowStateAnimator winAnimator, String operation,
boolean secure) {
final SurfaceControl surface = winAnimator.mSurfaceControl;
@@ -9307,10 +9564,10 @@
// window list to make sure we haven't left any dangling surfaces
// around.
- AllWindowsIterator iterator = new AllWindowsIterator();
+ mTmpWindowsIterator.reset(FORWARD_ITERATOR);
Slog.i(TAG, "Out of memory for surface! Looking for leaks...");
- while (iterator.hasNext()) {
- WindowState ws = iterator.next();
+ while (mTmpWindowsIterator.hasNext()) {
+ WindowState ws = mTmpWindowsIterator.next();
WindowStateAnimator wsa = ws.mWinAnimator;
if (wsa.mSurfaceControl != null) {
if (!mSessions.contains(wsa.mSession)) {
@@ -9343,9 +9600,9 @@
if (!leakedSurface) {
Slog.w(TAG, "No leaked surfaces; killing applicatons!");
SparseIntArray pidCandidates = new SparseIntArray();
- iterator = new AllWindowsIterator();
- while (iterator.hasNext()) {
- WindowState ws = iterator.next();
+ mTmpWindowsIterator.reset(FORWARD_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
+ WindowState ws = mTmpWindowsIterator.next();
if (mForceRemoves.contains(ws)) {
continue;
}
@@ -9401,24 +9658,25 @@
// change message pending.
mH.removeMessages(H.REPORT_FOCUS_CHANGE);
mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
+ // TODO(multidisplay): Focused windows on default display only.
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final boolean imWindowChanged = moveInputMethodWindowsIfNeededLocked(
+ mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
+ && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES);
+ if (imWindowChanged) {
+ displayContent.layoutNeeded = true;
+ newFocus = computeFocusedWindowLocked();
+ }
+
if (localLOGV) Slog.v(
TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus);
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
- mAnimator.setCurrentFocus(newFocus);
mLosingFocus.remove(newFocus);
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
- // TODO(multidisplay): Focused windows on default display only.
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
-
- final WindowState imWindow = mInputMethodWindow;
- if (newFocus != imWindow && oldFocus != imWindow) {
- if (moveInputMethodWindowsIfNeededLocked(
- mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS &&
- mode != UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
- displayContent.layoutNeeded = true;
- }
+ if (imWindowChanged && oldFocus != mInputMethodWindow) {
+ // Focus of the input method window changed. Perform layout if needed.
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
focusChanged &= ~WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -9471,8 +9729,20 @@
}
private WindowState findFocusedWindowLocked(DisplayContent displayContent) {
- int nextAppIndex = mAppTokens.size()-1;
- WindowToken nextApp = nextAppIndex >= 0 ? mAppTokens.get(nextAppIndex) : null;
+ // Set nextApp to the first app and set taskNdx and tokenNdx to point to the app following.
+ final ArrayList<Task> tasks = displayContent.getTasks();
+ int taskNdx = tasks.size() - 1;
+ AppTokenList tokens = taskNdx >= 0 ? tasks.get(taskNdx).mAppTokens : null;
+ int tokenNdx = tokens != null ? tokens.size() - 1 : -1;
+ WindowToken nextApp = tokenNdx >= 0 ? tokens.get(tokenNdx) : null;
+ --tokenNdx;
+ if (tokenNdx < 0) {
+ --taskNdx;
+ if (taskNdx >= 0) {
+ tokens = tasks.get(taskNdx).mAppTokens;
+ tokenNdx = tokens.size() - 1;
+ }
+ }
final WindowList windows = displayContent.getWindowList();
for (int i = windows.size() - 1; i >= 0; i--) {
@@ -9488,8 +9758,8 @@
// If this window's application has been removed, just skip it.
if (thisApp != null && (thisApp.removed || thisApp.sendingToBottom)) {
- if (DEBUG_FOCUS) Slog.v(TAG, "Skipping app because " + (thisApp.removed
- ? "removed" : "sendingToBottom"));
+ if (DEBUG_FOCUS) Slog.v(TAG, "Skipping " + thisApp + " because "
+ + (thisApp.removed ? "removed" : "sendingToBottom"));
continue;
}
@@ -9498,18 +9768,25 @@
// through the app tokens until we find its app.
if (thisApp != null && nextApp != null && thisApp != nextApp
&& win.mAttrs.type != TYPE_APPLICATION_STARTING) {
- int origAppIndex = nextAppIndex;
- while (nextAppIndex > 0) {
- if (nextApp == mFocusedApp) {
- // Whoops, we are below the focused app... no focus
- // for you!
- if (localLOGV || DEBUG_FOCUS) Slog.v(
- TAG, "Reached focused app: " + mFocusedApp);
- return null;
+ final WindowToken origAppToken = nextApp;
+ final int origTaskNdx = taskNdx;
+ final int origTokenNdx = tokenNdx;
+ for ( ; taskNdx >= 0; --taskNdx) {
+ tokens = tasks.get(taskNdx).mAppTokens;
+ for ( ; tokenNdx >= 0; --tokenNdx) {
+ if (nextApp == mFocusedApp) {
+ // Whoops, we are below the focused app... no focus
+ // for you!
+ if (localLOGV || DEBUG_FOCUS) Slog.v(
+ TAG, "Reached focused app: " + mFocusedApp);
+ return null;
+ }
+ nextApp = tokens.get(tokenNdx);
+ if (nextApp == thisApp) {
+ break;
+ }
}
- nextAppIndex--;
- nextApp = mAppTokens.get(nextAppIndex);
- if (nextApp == thisApp) {
+ if (thisApp == nextApp) {
break;
}
}
@@ -9517,8 +9794,10 @@
// Uh oh, the app token doesn't exist! This shouldn't
// happen, but if it does we can get totally hosed...
// so restart at the original app.
- nextAppIndex = origAppIndex;
- nextApp = mAppTokens.get(nextAppIndex);
+ nextApp = origAppToken;
+ // return indices to same place.
+ taskNdx = origTaskNdx;
+ tokenNdx = origTokenNdx;
}
}
@@ -9629,7 +9908,7 @@
// TODO(multidisplay): rotation on main screen only.
DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Get rotation animation again, with new top window
- boolean isDimming = mAnimator.isDimmingLocked(Display.DEFAULT_DISPLAY);
+ boolean isDimming = displayContent.isDimming();
if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
mExitAnimId = mEnterAnimId = 0;
}
@@ -9899,15 +10178,6 @@
}
}
}
- if (mAppTokens.size() > 0) {
- pw.println();
- pw.println(" Application tokens in Z order:");
- for (int i=mAppTokens.size()-1; i>=0; i--) {
- pw.print(" App #"); pw.print(i);
- pw.print(' '); pw.print(mAppTokens.get(i)); pw.println(":");
- mAppTokens.get(i).dump(pw, " ");
- }
- }
if (mFinishedStarting.size() > 0) {
pw.println();
pw.println(" Finishing start of application tokens:");
@@ -9923,51 +10193,6 @@
}
}
}
- if (mExitingTokens.size() > 0) {
- pw.println();
- pw.println(" Exiting tokens:");
- for (int i=mExitingTokens.size()-1; i>=0; i--) {
- WindowToken token = mExitingTokens.get(i);
- pw.print(" Exiting #"); pw.print(i);
- pw.print(' '); pw.print(token);
- if (dumpAll) {
- pw.println(':');
- token.dump(pw, " ");
- } else {
- pw.println();
- }
- }
- }
- if (mExitingAppTokens.size() > 0) {
- pw.println();
- pw.println(" Exiting application tokens:");
- for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
- WindowToken token = mExitingAppTokens.get(i);
- pw.print(" Exiting App #"); pw.print(i);
- pw.print(' '); pw.print(token);
- if (dumpAll) {
- pw.println(':');
- token.dump(pw, " ");
- } else {
- pw.println();
- }
- }
- }
- if (mAppTransition.isRunning() && mAnimatingAppTokens.size() > 0) {
- pw.println();
- pw.println(" Application tokens during animation:");
- for (int i=mAnimatingAppTokens.size()-1; i>=0; i--) {
- WindowToken token = mAnimatingAppTokens.get(i);
- pw.print(" App moving to bottom #"); pw.print(i);
- pw.print(' '); pw.print(token);
- if (dumpAll) {
- pw.println(':');
- token.dump(pw, " ");
- } else {
- pw.println();
- }
- }
- }
if (mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
pw.println();
if (mOpeningApps.size() > 0) {
@@ -10012,9 +10237,9 @@
void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll,
ArrayList<WindowState> windows) {
int j = 0;
- final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
- while (iterator.hasNext()) {
- final WindowState w = iterator.next();
+ mTmpWindowsIterator.reset(REVERSE_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
+ final WindowState w = mTmpWindowsIterator.next();
if (windows == null || windows.contains(w)) {
pw.print(" Window #"); pw.print(j++); pw.print(' ');
pw.print(w); pw.println(":");
@@ -10207,9 +10432,9 @@
WindowList windows = new WindowList();
if ("visible".equals(name)) {
synchronized(mWindowMap) {
- final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
- while (iterator.hasNext()) {
- final WindowState w = iterator.next();
+ mTmpWindowsIterator.reset(REVERSE_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
+ final WindowState w = mTmpWindowsIterator.next();
if (w.mWinAnimator.mSurfaceShown) {
windows.add(w);
}
@@ -10224,9 +10449,9 @@
} catch (RuntimeException e) {
}
synchronized(mWindowMap) {
- final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
- while (iterator.hasNext()) {
- final WindowState w = iterator.next();
+ mTmpWindowsIterator.reset(REVERSE_ITERATOR);
+ while (mTmpWindowsIterator.hasNext()) {
+ final WindowState w = mTmpWindowsIterator.next();
if (name != null) {
if (w.mAttrs.getTitle().toString().contains(name)) {
windows.add(w);
@@ -10267,7 +10492,7 @@
*/
public void saveANRStateLocked(AppWindowToken appWindowToken, WindowState windowState) {
StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
+ PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(" ANR time: " + DateFormat.getInstance().format(new Date()));
if (appWindowToken != null) {
pw.println(" Application at fault: " + appWindowToken.stringName);
@@ -10437,7 +10662,8 @@
private DisplayContent newDisplayContentLocked(final Display display) {
DisplayContent displayContent = new DisplayContent(display);
- mDisplayContents.put(display.getDisplayId(), displayContent);
+ final int displayId = display.getDisplayId();
+ mDisplayContents.put(displayId, displayContent);
final Rect rect = new Rect();
DisplayInfo info = displayContent.getDisplayInfo();
mDisplaySettings.getOverscanLocked(info.name, rect);
@@ -10449,6 +10675,13 @@
rect.right, rect.bottom);
mPolicy.setDisplayOverscan(displayContent.getDisplay(), rect.left, rect.top,
rect.right, rect.bottom);
+
+ // TODO: Create an input channel for each display with touch capability.
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ displayContent.mTapDetector = new StackTapPointerEventListener(this, displayContent);
+ registerPointerEventListener(displayContent.mTapDetector);
+ }
+
return displayContent;
}
@@ -10479,6 +10712,10 @@
class DisplayContentsIterator implements Iterator<DisplayContent> {
private int cur;
+ void reset() {
+ cur = 0;
+ }
+
@Override
public boolean hasNext() {
return cur < mDisplayContents.size();
@@ -10498,7 +10735,6 @@
}
}
- final static boolean REVERSE_ITERATOR = true;
class AllWindowsIterator implements Iterator<WindowState> {
private DisplayContent mDisplayContent;
private DisplayContentsIterator mDisplayContentsIterator;
@@ -10507,19 +10743,33 @@
private boolean mReverse;
AllWindowsIterator() {
- mDisplayContentsIterator = new DisplayContentsIterator();
- mDisplayContent = mDisplayContentsIterator.next();
- mWindowList = mDisplayContent.getWindowList();
+ this(false);
}
AllWindowsIterator(boolean reverse) {
- this();
+ mDisplayContentsIterator = new DisplayContentsIterator();
+ reset(reverse);
+ }
+
+ void reset(boolean reverse) {
mReverse = reverse;
- mWindowListIndex = reverse ? mWindowList.size() - 1 : 0;
+ mDisplayContentsIterator.reset();
+ if (mDisplayContentsIterator.hasNext()) {
+ mDisplayContent = mDisplayContentsIterator.next();
+ mWindowList = mDisplayContent.getWindowList();
+ mWindowListIndex = reverse ? mWindowList.size() - 1 : 0;
+ } else {
+ mDisplayContent = null;
+ mWindowList = null;
+ mWindowListIndex = 0;
+ }
}
@Override
public boolean hasNext() {
+ if (mDisplayContent == null) {
+ return false;
+ }
if (mReverse) {
return mWindowListIndex >= 0;
}
@@ -10611,6 +10861,10 @@
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
mDisplayContents.delete(displayId);
+
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ unregisterPointerEventListener(displayContent.mTapDetector);
+ }
WindowList windows = displayContent.getWindowList();
while (!windows.isEmpty()) {
final WindowState win = windows.get(windows.size() - 1);
@@ -10631,4 +10885,9 @@
displayContent.updateDisplayInfo();
}
}
+
+ @Override
+ public Object getWindowManagerLock() {
+ return mWindowMap;
+ }
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index da15856..dd19f89 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
+
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
@@ -65,11 +68,6 @@
final class WindowState implements WindowManagerPolicy.WindowState {
static final String TAG = "WindowState";
- static final boolean DEBUG_VISIBILITY = WindowManagerService.DEBUG_VISIBILITY;
- static final boolean SHOW_TRANSACTIONS = WindowManagerService.SHOW_TRANSACTIONS;
- static final boolean SHOW_LIGHT_TRANSACTIONS = WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
- static final boolean SHOW_SURFACE_ALLOC = WindowManagerService.SHOW_SURFACE_ALLOC;
-
final WindowManagerService mService;
final WindowManagerPolicy mPolicy;
final Context mContext;
@@ -300,6 +298,10 @@
/** When true this window can be displayed on screens owther than mOwnerUid's */
private boolean mShowToOwnerOnly;
+ /** When true this window is at the top of the screen and should be layed out to extend under
+ * the status bar */
+ boolean mUnderStatusBar = true;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, final DisplayContent displayContent) {
@@ -459,14 +461,19 @@
public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf) {
mHaveFrame = true;
- final Rect container = mContainingFrame;
- container.set(pf);
+ if (mAppToken != null) {
+ mContainingFrame.set(getStackBounds());
+ if (mUnderStatusBar) {
+ mContainingFrame.top = pf.top;
+ }
+ } else {
+ mContainingFrame.set(pf);
+ }
- final Rect display = mDisplayFrame;
- display.set(df);
+ mDisplayFrame.set(df);
- final int pw = container.right - container.left;
- final int ph = container.bottom - container.top;
+ final int pw = mContainingFrame.width();
+ final int ph = mContainingFrame.height();
int w,h;
if ((mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0) {
@@ -513,18 +520,12 @@
mContentChanged = true;
}
- final Rect overscan = mOverscanFrame;
- overscan.set(of);
+ mOverscanFrame.set(of);
+ mContentFrame.set(cf);
+ mVisibleFrame.set(vf);
- final Rect content = mContentFrame;
- content.set(cf);
-
- final Rect visible = mVisibleFrame;
- visible.set(vf);
-
- final Rect frame = mFrame;
- final int fw = frame.width();
- final int fh = frame.height();
+ final int fw = mFrame.width();
+ final int fh = mFrame.height();
//System.out.println("In: w=" + w + " h=" + h + " container=" +
// container + " x=" + mAttrs.x + " y=" + mAttrs.y);
@@ -538,75 +539,69 @@
y = mAttrs.y;
}
- Gravity.apply(mAttrs.gravity, w, h, container,
+ Gravity.apply(mAttrs.gravity, w, h, mContainingFrame,
(int) (x + mAttrs.horizontalMargin * pw),
- (int) (y + mAttrs.verticalMargin * ph), frame);
+ (int) (y + mAttrs.verticalMargin * ph), mFrame);
//System.out.println("Out: " + mFrame);
// Now make sure the window fits in the overall display.
- Gravity.applyDisplay(mAttrs.gravity, df, frame);
+ Gravity.applyDisplay(mAttrs.gravity, df, mFrame);
// Make sure the content and visible frames are inside of the
// final window frame.
- if (content.left < frame.left) content.left = frame.left;
- if (content.top < frame.top) content.top = frame.top;
- if (content.right > frame.right) content.right = frame.right;
- if (content.bottom > frame.bottom) content.bottom = frame.bottom;
- if (visible.left < frame.left) visible.left = frame.left;
- if (visible.top < frame.top) visible.top = frame.top;
- if (visible.right > frame.right) visible.right = frame.right;
- if (visible.bottom > frame.bottom) visible.bottom = frame.bottom;
+ mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
+ Math.max(mContentFrame.top, mFrame.top),
+ Math.min(mContentFrame.right, mFrame.right),
+ Math.min(mContentFrame.bottom, mFrame.bottom));
- final Rect overscanInsets = mOverscanInsets;
- overscanInsets.left = overscan.left > frame.left ? overscan.left-frame.left : 0;
- overscanInsets.top = overscan.top > frame.top ? overscan.top-frame.top : 0;
- overscanInsets.right = overscan.right < frame.right ? frame.right-overscan.right : 0;
- overscanInsets.bottom = overscan.bottom < frame.bottom ? frame.bottom-overscan.bottom : 0;
+ mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left),
+ Math.max(mVisibleFrame.top, mFrame.top),
+ Math.min(mVisibleFrame.right, mFrame.right),
+ Math.min(mVisibleFrame.bottom, mFrame.bottom));
- final Rect contentInsets = mContentInsets;
- contentInsets.left = content.left-frame.left;
- contentInsets.top = content.top-frame.top;
- contentInsets.right = frame.right-content.right;
- contentInsets.bottom = frame.bottom-content.bottom;
+ mOverscanInsets.set(Math.max(mOverscanFrame.left - mFrame.left, 0),
+ Math.max(mOverscanFrame.top - mFrame.top, 0),
+ Math.max(mFrame.right - mOverscanFrame.right, 0),
+ Math.max(mFrame.bottom - mOverscanFrame.bottom, 0));
- final Rect visibleInsets = mVisibleInsets;
- visibleInsets.left = visible.left-frame.left;
- visibleInsets.top = visible.top-frame.top;
- visibleInsets.right = frame.right-visible.right;
- visibleInsets.bottom = frame.bottom-visible.bottom;
+ mContentInsets.set(mContentFrame.left - mFrame.left,
+ mContentFrame.top - mFrame.top,
+ mFrame.right - mContentFrame.right,
+ mFrame.bottom - mContentFrame.bottom);
- mCompatFrame.set(frame);
+ mVisibleInsets.set(mVisibleFrame.left - mFrame.left,
+ mVisibleFrame.top - mFrame.top,
+ mFrame.right - mVisibleFrame.right,
+ mFrame.bottom - mVisibleFrame.bottom);
+
+ mCompatFrame.set(mFrame);
if (mEnforceSizeCompat) {
// If there is a size compatibility scale being applied to the
// window, we need to apply this to its insets so that they are
// reported to the app in its coordinate space.
- overscanInsets.scale(mInvGlobalScale);
- contentInsets.scale(mInvGlobalScale);
- visibleInsets.scale(mInvGlobalScale);
+ mOverscanInsets.scale(mInvGlobalScale);
+ mContentInsets.scale(mInvGlobalScale);
+ mVisibleInsets.scale(mInvGlobalScale);
// Also the scaled frame that we report to the app needs to be
// adjusted to be in its coordinate space.
mCompatFrame.scale(mInvGlobalScale);
}
- if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
+ if (mIsWallpaper && (fw != mFrame.width() || fh != mFrame.height())) {
final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
mService.updateWallpaperOffsetLocked(this, displayInfo.appWidth, displayInfo.appHeight,
false);
}
- if (WindowManagerService.localLOGV) {
- //if ("com.google.android.youtube".equals(mAttrs.packageName)
- // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
- Slog.v(TAG, "Resolving (mRequestedWidth="
- + mRequestedWidth + ", mRequestedheight="
- + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
- + "): frame=" + mFrame.toShortString()
- + " ci=" + contentInsets.toShortString()
- + " vi=" + visibleInsets.toShortString());
- //}
- }
+ if (DEBUG_LAYOUT || WindowManagerService.localLOGV) Slog.v(TAG,
+ "Resolving (mRequestedWidth="
+ + mRequestedWidth + ", mRequestedheight="
+ + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+ + "): frame=" + mFrame.toShortString()
+ + " ci=" + mContentInsets.toShortString()
+ + " vi=" + mVisibleInsets.toShortString());
}
@Override
@@ -707,6 +702,25 @@
return mDisplayContent.getDisplayId();
}
+ TaskStack getStack() {
+ AppWindowToken wtoken = mAppToken == null ? mService.mFocusedApp : mAppToken;
+ if (wtoken != null) {
+ Task task = mService.mTaskIdToTask.get(wtoken.groupId);
+ if (task != null) {
+ return task.mStack;
+ }
+ }
+ return mDisplayContent.getHomeStack();
+ }
+
+ Rect getStackBounds() {
+ TaskStack stack = getStack();
+ if (stack != null) {
+ return stack.mStackBox.mBounds;
+ }
+ return mFrame;
+ }
+
public long getInputDispatchingTimeoutNanos() {
return mAppToken != null
? mAppToken.inputDispatchingTimeoutNanos
@@ -901,7 +915,8 @@
|| (atoken == null && mRootToken.hidden)
|| (atoken != null && (atoken.hiddenRequested || atoken.hidden))
|| mAttachedHidden
- || mExiting || mDestroying;
+ || (mExiting && !isAnimatingLw())
+ || mDestroying;
}
/**
@@ -968,12 +983,6 @@
return configChanged;
}
- boolean isConfigDiff(int mask) {
- return mConfiguration != mService.mCurConfiguration
- && mConfiguration != null
- && (mConfiguration.diff(mService.mCurConfiguration) & mask) != 0;
- }
-
void removeLocked() {
disposeInputChannel();
@@ -1026,7 +1035,7 @@
Slog.i(TAG, "WIN DEATH: " + win);
if (win != null) {
mService.removeWindowLocked(mSession, win);
- } else if (WindowState.this.mHasSurface) {
+ } else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
mService.removeWindowLocked(mSession, WindowState.this);
}
@@ -1138,7 +1147,7 @@
return true;
}
- public boolean setAppOpVisibilityLw(boolean state) {
+ public void setAppOpVisibilityLw(boolean state) {
if (mAppOpVisibility != state) {
mAppOpVisibility = state;
if (state) {
@@ -1148,13 +1157,11 @@
// ops modifies they should only be hidden by policy due to the
// lock screen, and the user won't be changing this if locked.
// Plus it will quickly be fixed the next time we do a layout.
- showLw(true, false);
+ showLw(true, true);
} else {
- hideLw(true, false);
+ hideLw(true, true);
}
- return true;
}
- return false;
}
@Override
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index c07174b..a52d85c 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -3,6 +3,16 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS;
+import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerService.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerService.DEBUG_SURFACE_TRACE;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
+import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_TURN_ON_SCREEN;
@@ -19,7 +29,6 @@
import android.view.Display;
import android.view.DisplayInfo;
import android.view.MagnificationSpec;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -35,30 +44,12 @@
import java.util.ArrayList;
class WinAnimatorList extends ArrayList<WindowStateAnimator> {
- public WinAnimatorList() {
- super();
- }
-
- public WinAnimatorList(WinAnimatorList other) {
- super(other);
- }
}
/**
* Keep track of animations and surface operations for a single WindowState.
**/
class WindowStateAnimator {
- static final boolean DEBUG_VISIBILITY = WindowManagerService.DEBUG_VISIBILITY;
- static final boolean DEBUG_ANIM = WindowManagerService.DEBUG_ANIM;
- static final boolean DEBUG_LAYERS = WindowManagerService.DEBUG_LAYERS;
- static final boolean DEBUG_STARTING_WINDOW = WindowManagerService.DEBUG_STARTING_WINDOW;
- static final boolean SHOW_TRANSACTIONS = WindowManagerService.SHOW_TRANSACTIONS;
- static final boolean SHOW_LIGHT_TRANSACTIONS = WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
- static final boolean SHOW_SURFACE_ALLOC = WindowManagerService.SHOW_SURFACE_ALLOC;
- static final boolean localLOGV = WindowManagerService.localLOGV;
- static final boolean DEBUG_ORIENTATION = WindowManagerService.DEBUG_ORIENTATION;
- static final boolean DEBUG_SURFACE_TRACE = WindowManagerService.DEBUG_SURFACE_TRACE;
-
static final String TAG = "WindowStateAnimator";
// Unchanging local convenience fields.
@@ -340,7 +331,7 @@
mHasTransformation = false;
mHasLocalTransformation = false;
if (mWin.mPolicyVisibility != mWin.mPolicyVisibilityAfterAnim) {
- if (WindowState.DEBUG_VISIBILITY) {
+ if (DEBUG_VISIBILITY) {
Slog.v(TAG, "Policy visibility changing after anim in " + this + ": "
+ mWin.mPolicyVisibilityAfterAnim);
}
@@ -407,7 +398,7 @@
if (mSurfaceControl != null) {
mService.mDestroySurface.add(mWin);
mWin.mDestroying = true;
- if (WindowState.SHOW_TRANSACTIONS) WindowManagerService.logSurface(
+ if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(
mWin, "HIDE (finishExit)", null);
hide();
}
@@ -648,7 +639,7 @@
if ((attrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
flags |= SurfaceControl.SECURE;
}
- if (WindowState.DEBUG_VISIBILITY) Slog.v(
+ if (DEBUG_VISIBILITY) Slog.v(
TAG, "Creating surface in session "
+ mSession.mSurfaceSession + " window " + this
+ " w=" + mWin.mCompatFrame.width()
@@ -1185,7 +1176,7 @@
mAnimator.setPendingLayoutChanges(displayId,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
- mService.startDimmingLocked(this, w.mExiting ? 0 : w.mAttrs.dimAmount);
+ w.getStack().startDimmingIfNeeded(this);
}
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
diff --git a/services/java/com/android/server/wm/WindowToken.java b/services/java/com/android/server/wm/WindowToken.java
index bd0ace8..2267123 100644
--- a/services/java/com/android/server/wm/WindowToken.java
+++ b/services/java/com/android/server/wm/WindowToken.java
@@ -19,7 +19,6 @@
import android.os.IBinder;
import java.io.PrintWriter;
-import java.util.ArrayList;
/**
* Container of a set of related windows in the window manager. Often this
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index b313d48..f332350 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -3,6 +3,7 @@
LOCAL_SRC_FILES:= \
com_android_server_AlarmManagerService.cpp \
+ com_android_server_AssetAtlasService.cpp \
com_android_server_BatteryService.cpp \
com_android_server_input_InputApplicationHandle.cpp \
com_android_server_input_InputManagerService.cpp \
@@ -40,10 +41,15 @@
libutils \
libui \
libinput \
+ libinputservice \
libskia \
libgui \
libusbhost \
- libsuspend
+ libsuspend \
+ libEGL \
+ libGLESv2
+
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
diff --git a/services/jni/com_android_server_AssetAtlasService.cpp b/services/jni/com_android_server_AssetAtlasService.cpp
new file mode 100644
index 0000000..62e950f
--- /dev/null
+++ b/services/jni/com_android_server_AssetAtlasService.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AssetAtlasService"
+
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <android_view_GraphicBuffer.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <SkCanvas.h>
+#include <SkBitmap.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+// Defines
+// ----------------------------------------------------------------------------
+
+// Defines how long to wait for the GPU when uploading the atlas
+// This timeout is defined in nanoseconds (see EGL_KHR_fence_sync extension)
+#define FENCE_TIMEOUT 2000000000
+
+// ----------------------------------------------------------------------------
+// JNI Helpers
+// ----------------------------------------------------------------------------
+
+static struct {
+ jfieldID mFinalizer;
+ jfieldID mNativeCanvas;
+} gCanvasClassInfo;
+
+static struct {
+ jfieldID mNativeCanvas;
+} gCanvasFinalizerClassInfo;
+
+#define GET_INT(object, field) \
+ env->GetIntField(object, field)
+
+#define SET_INT(object, field, value) \
+ env->SetIntField(object, field, value)
+
+// ----------------------------------------------------------------------------
+// Canvas management
+// ----------------------------------------------------------------------------
+
+static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
+ jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
+ SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
+ GET_INT(canvasObj, gCanvasClassInfo.mNativeCanvas));
+ SET_INT(canvasObj, gCanvasClassInfo.mNativeCanvas, (int) newCanvas);
+ SET_INT(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int) newCanvas);
+ SkSafeUnref(previousCanvas);
+}
+
+static SkBitmap* com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, jobject,
+ jobject canvas, jint width, jint height) {
+
+ SkBitmap* bitmap = new SkBitmap;
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap->allocPixels();
+ bitmap->eraseColor(0);
+
+ SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (*bitmap));
+ swapCanvasPtr(env, canvas, nativeCanvas);
+
+ return bitmap;
+}
+
+static void com_android_server_AssetAtlasService_releaseCanvas(JNIEnv* env, jobject,
+ jobject canvas, SkBitmap* bitmap) {
+
+ SkCanvas* nativeCanvas = SkNEW(SkCanvas);
+ swapCanvasPtr(env, canvas, nativeCanvas);
+
+ delete bitmap;
+}
+
+#define CLEANUP_GL_AND_RETURN(result) \
+ if (fence != EGL_NO_SYNC_KHR) eglDestroySyncKHR(display, fence); \
+ if (image) eglDestroyImageKHR(display, image); \
+ if (texture) glDeleteTextures(1, &texture); \
+ if (surface != EGL_NO_SURFACE) eglDestroySurface(display, surface); \
+ if (context != EGL_NO_CONTEXT) eglDestroyContext(display, context); \
+ eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); \
+ eglReleaseThread(); \
+ eglTerminate(display); \
+ return result;
+
+static jboolean com_android_server_AssetAtlasService_upload(JNIEnv* env, jobject,
+ jobject graphicBuffer, SkBitmap* bitmap) {
+
+ // The goal of this method is to copy the bitmap into the GraphicBuffer
+ // using the GPU to swizzle the texture content
+ sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
+
+ if (buffer != NULL) {
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (display == EGL_NO_DISPLAY) return false;
+
+ EGLint major;
+ EGLint minor;
+ if (!eglInitialize(display, &major, &minor)) {
+ ALOGW("Could not initialize EGL");
+ return false;
+ }
+
+ // We're going to use a 1x1 pbuffer surface later on
+ // The configuration doesn't really matter for what we're trying to do
+ EGLint configAttrs[] = {
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 0,
+ EGL_DEPTH_SIZE, 0,
+ EGL_STENCIL_SIZE, 0,
+ EGL_NONE
+ };
+ EGLConfig configs[1];
+ EGLint configCount;
+ if (!eglChooseConfig(display, configAttrs, configs, 1, &configCount)) {
+ ALOGW("Could not select EGL configuration");
+ eglReleaseThread();
+ eglTerminate(display);
+ return false;
+ }
+ if (configCount <= 0) {
+ ALOGW("Could not find EGL configuration");
+ eglReleaseThread();
+ eglTerminate(display);
+ return false;
+ }
+
+ // These objects are initialized below but the default "null"
+ // values are used to cleanup properly at any point in the
+ // initialization sequence
+ GLuint texture = 0;
+ EGLImageKHR image = EGL_NO_IMAGE_KHR;
+ EGLSurface surface = EGL_NO_SURFACE;
+ EGLSyncKHR fence = EGL_NO_SYNC_KHR;
+
+ EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ EGLContext context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, attrs);
+ if (context == EGL_NO_CONTEXT) {
+ ALOGW("Could not create EGL context");
+ CLEANUP_GL_AND_RETURN(false);
+ }
+
+ // Create the 1x1 pbuffer
+ EGLint surfaceAttrs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+ surface = eglCreatePbufferSurface(display, configs[0], surfaceAttrs);
+ if (surface == EGL_NO_SURFACE) {
+ ALOGW("Could not create EGL surface");
+ CLEANUP_GL_AND_RETURN(false);
+ }
+
+ if (!eglMakeCurrent(display, surface, surface, context)) {
+ ALOGW("Could not change current EGL context");
+ CLEANUP_GL_AND_RETURN(false);
+ }
+
+ // We use an EGLImage to access the content of the GraphicBuffer
+ // The EGL image is later bound to a 2D texture
+ EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer->getNativeBuffer();
+ EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
+ image = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
+ if (image == EGL_NO_IMAGE_KHR) {
+ ALOGW("Could not create EGL image");
+ CLEANUP_GL_AND_RETURN(false);
+ }
+
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+ if (glGetError() != GL_NO_ERROR) {
+ ALOGW("Could not create/bind texture");
+ CLEANUP_GL_AND_RETURN(false);
+ }
+
+ // Upload the content of the bitmap in the GraphicBuffer
+ glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap->width(), bitmap->height(),
+ GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+ if (glGetError() != GL_NO_ERROR) {
+ ALOGW("Could not upload to texture");
+ CLEANUP_GL_AND_RETURN(false);
+ }
+
+ // The fence is used to wait for the texture upload to finish
+ // properly. We cannot rely on glFlush() and glFinish() as
+ // some drivers completely ignore these API calls
+ fence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL);
+ if (fence == EGL_NO_SYNC_KHR) {
+ ALOGW("Could not create sync fence %#x", eglGetError());
+ CLEANUP_GL_AND_RETURN(false);
+ }
+
+ // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a
+ // pipeline flush (similar to what a glFlush() would do.)
+ EGLint waitStatus = eglClientWaitSyncKHR(display, fence,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT);
+ if (waitStatus != EGL_CONDITION_SATISFIED_KHR) {
+ ALOGW("Failed to wait for the fence %#x", eglGetError());
+ CLEANUP_GL_AND_RETURN(false);
+ }
+
+ CLEANUP_GL_AND_RETURN(true);
+ }
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+const char* const kClassPathName = "com/android/server/AssetAtlasService";
+
+static JNINativeMethod gMethods[] = {
+ { "nAcquireAtlasCanvas", "(Landroid/graphics/Canvas;II)I",
+ (void*) com_android_server_AssetAtlasService_acquireCanvas },
+ { "nReleaseAtlasCanvas", "(Landroid/graphics/Canvas;I)V",
+ (void*) com_android_server_AssetAtlasService_releaseCanvas },
+ { "nUploadAtlas", "(Landroid/view/GraphicBuffer;I)Z",
+ (void*) com_android_server_AssetAtlasService_upload },
+};
+
+int register_android_server_AssetAtlasService(JNIEnv* env) {
+ jclass clazz;
+
+ FIND_CLASS(clazz, "android/graphics/Canvas");
+ GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer",
+ "Landroid/graphics/Canvas$CanvasFinalizer;");
+ GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
+
+ FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer");
+ GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
+
+ return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp
index 433950d..0c8b4a5 100644
--- a/services/jni/com_android_server_BatteryService.cpp
+++ b/services/jni/com_android_server_BatteryService.cpp
@@ -371,6 +371,9 @@
if (access(path, R_OK) == 0)
gPaths.batteryTechnologyPath = path;
break;
+
+ case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
+ break;
}
}
closedir(dir);
@@ -378,19 +381,19 @@
if (!gChargerNames.size())
ALOGE("No charger supplies found");
- if (!gPaths.batteryStatusPath)
+ if (gPaths.batteryStatusPath.isEmpty())
ALOGE("batteryStatusPath not found");
- if (!gPaths.batteryHealthPath)
+ if (gPaths.batteryHealthPath.isEmpty())
ALOGE("batteryHealthPath not found");
- if (!gPaths.batteryPresentPath)
+ if (gPaths.batteryPresentPath.isEmpty())
ALOGE("batteryPresentPath not found");
- if (!gPaths.batteryCapacityPath)
+ if (gPaths.batteryCapacityPath.isEmpty())
ALOGE("batteryCapacityPath not found");
- if (!gPaths.batteryVoltagePath)
+ if (gPaths.batteryVoltagePath.isEmpty())
ALOGE("batteryVoltagePath not found");
- if (!gPaths.batteryTemperaturePath)
+ if (gPaths.batteryTemperaturePath.isEmpty())
ALOGE("batteryTemperaturePath not found");
- if (!gPaths.batteryTechnologyPath)
+ if (gPaths.batteryTechnologyPath.isEmpty())
ALOGE("batteryTechnologyPath not found");
jclass clazz = env->FindClass("com/android/server/BatteryService");
diff --git a/services/jni/com_android_server_power_PowerManagerService.cpp b/services/jni/com_android_server_power_PowerManagerService.cpp
index 23c33af..88b13b5 100644
--- a/services/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/jni/com_android_server_power_PowerManagerService.cpp
@@ -32,7 +32,6 @@
#include <utils/Log.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
-#include <cutils/android_reboot.h>
#include <suspend/autosuspend.h>
#include "com_android_server_power_PowerManagerService.h"
@@ -189,22 +188,6 @@
}
}
-static void nativeShutdown(JNIEnv *env, jclass clazz) {
- android_reboot(ANDROID_RB_POWEROFF, 0, 0);
-}
-
-static void nativeReboot(JNIEnv *env, jclass clazz, jstring reason) {
- if (reason == NULL) {
- android_reboot(ANDROID_RB_RESTART, 0, 0);
- } else {
- const char *chars = env->GetStringUTFChars(reason, NULL);
- android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);
- env->ReleaseStringUTFChars(reason, chars); // In case it fails.
- }
- jniThrowIOException(env, errno);
-}
-
-
// ----------------------------------------------------------------------------
static JNINativeMethod gPowerManagerServiceMethods[] = {
@@ -221,10 +204,6 @@
(void*) nativeSetInteractive },
{ "nativeSetAutoSuspend", "(Z)V",
(void*) nativeSetAutoSuspend },
- { "nativeShutdown", "()V",
- (void*) nativeShutdown },
- { "nativeReboot", "(Ljava/lang/String;)V",
- (void*) nativeReboot },
};
#define FIND_CLASS(var, className) \
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index 423ebd1..bb679aa 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -34,6 +34,7 @@
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
+int register_android_server_AssetAtlasService(JNIEnv* env);
};
using namespace android;
@@ -63,6 +64,7 @@
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);
register_android_server_connectivity_Vpn(env);
+ register_android_server_AssetAtlasService(env);
return JNI_VERSION_1_4;
}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 228a630..6978551 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -49,10 +49,6 @@
private static final String TAG = "CallerInfo";
private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
- public static final String UNKNOWN_NUMBER = "-1";
- public static final String PRIVATE_NUMBER = "-2";
- public static final String PAYPHONE_NUMBER = "-3";
-
/**
* Please note that, any one of these member variables can be null,
* and any accesses to them should be prepared to handle such a case.
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 29d6e4d..44077ae 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -39,7 +39,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
-import android.view.CompatibilityInfoHolder;
+import android.view.DisplayAdjustments;
import android.view.Display;
import java.io.File;
@@ -574,7 +574,7 @@
/** @hide */
@Override
- public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
+ public DisplayAdjustments getDisplayAdjustments(int displayId) {
throw new UnsupportedOperationException();
}
}
diff --git a/test-runner/src/android/test/mock/MockCursor.java b/test-runner/src/android/test/mock/MockCursor.java
index baa150a..5b8a4f4 100644
--- a/test-runner/src/android/test/mock/MockCursor.java
+++ b/test-runner/src/android/test/mock/MockCursor.java
@@ -177,17 +177,18 @@
throw new UnsupportedOperationException("unimplemented mock method");
}
- @SuppressWarnings("deprecation")
public void setNotificationUri(ContentResolver cr, Uri uri) {
throw new UnsupportedOperationException("unimplemented mock method");
}
- @SuppressWarnings("deprecation")
+ public Uri getNotificationUri() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
public void unregisterContentObserver(ContentObserver observer) {
throw new UnsupportedOperationException("unimplemented mock method");
}
- @SuppressWarnings("deprecation")
public void unregisterDataSetObserver(DataSetObserver observer) {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 20a26ab..4683534 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -45,6 +45,8 @@
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Binder;
+import android.os.UserHandle;
import java.util.List;
@@ -585,6 +587,23 @@
* @hide
*/
@Override
+ public boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+ UserHandle user) {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean getApplicationBlockedSettingAsUser(String packageName, UserHandle user) {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public int installExistingPackage(String packageName)
throws NameNotFoundException {
throw new UnsupportedOperationException();
diff --git a/tests/AccessoryDisplay/Android.mk b/tests/AccessoryDisplay/Android.mk
new file mode 100644
index 0000000..85cb309
--- /dev/null
+++ b/tests/AccessoryDisplay/Android.mk
@@ -0,0 +1,17 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/AccessoryDisplay/README b/tests/AccessoryDisplay/README
new file mode 100644
index 0000000..5ce558c
--- /dev/null
+++ b/tests/AccessoryDisplay/README
@@ -0,0 +1,50 @@
+This directory contains sample code to test the use of virtual
+displays created over an Android Open Accessories Protocol link.
+
+--- DESCRIPTION ---
+
+There are two applications with two distinct roles: a sink
+and a source.
+
+1. Sink Application
+
+The role of the sink is to emulate an external display that happens
+to be connected using the USB accessory protocol. Think of it as
+a monitor or video dock that the user will want to plug a phone into.
+
+The sink application uses the UsbDevice APIs to receive connections
+from the source device over USB. The sink acts as a USB host
+in this arrangement and will provide power to the source.
+
+The sink application decodes encoded video from the source and
+displays it in a SurfaceView. The sink also injects passes touch
+events to the source over USB HID.
+
+2. Source Application
+
+The role of the source is to present some content onto an external
+display that happens to be attached over USB. This is the typical
+role that a phone or tablet might have when the user is trying to
+play content to an external monitor.
+
+The source application uses the UsbAccessory APIs to connect
+to the sink device over USB. The source acts as a USB peripheral
+in this arrangement and will receive power from the sink.
+
+The source application uses the DisplayManager APIs to create
+a private virtual display which passes the framebuffer through
+an encoder and streams the output to the sink over USB. Then
+the application opens a Presentation on the new virtual display
+and shows a silly cube animation.
+
+--- USAGE ---
+
+These applications should be installed on two separate Android
+devices which are then connected using a USB OTG cable.
+Remember that the sink device is functioning as the USB host
+so the USB OTG cable should be plugged directly into it.
+
+When connected, the applications should automatically launch
+on each device. The source will then begin to project display
+contents to the sink.
+
diff --git a/tests/AccessoryDisplay/common/Android.mk b/tests/AccessoryDisplay/common/Android.mk
new file mode 100644
index 0000000..2d4de15
--- /dev/null
+++ b/tests/AccessoryDisplay/common/Android.mk
@@ -0,0 +1,23 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Build the application.
+include $(CLEAR_VARS)
+LOCAL_MODULE := AccessoryDisplayCommon
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/BufferPool.java b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/BufferPool.java
new file mode 100644
index 0000000..a6bb5c1
--- /dev/null
+++ b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/BufferPool.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.common;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Maintains a bounded pool of buffers. Attempts to acquire buffers beyond the maximum
+ * count will block until other buffers are released.
+ */
+final class BufferPool {
+ private final int mInitialBufferSize;
+ private final int mMaxBufferSize;
+ private final ByteBuffer[] mBuffers;
+ private int mAllocated;
+ private int mAvailable;
+
+ public BufferPool(int initialBufferSize, int maxBufferSize, int maxBuffers) {
+ mInitialBufferSize = initialBufferSize;
+ mMaxBufferSize = maxBufferSize;
+ mBuffers = new ByteBuffer[maxBuffers];
+ }
+
+ public ByteBuffer acquire(int needed) {
+ synchronized (this) {
+ for (;;) {
+ if (mAvailable != 0) {
+ mAvailable -= 1;
+ return grow(mBuffers[mAvailable], needed);
+ }
+
+ if (mAllocated < mBuffers.length) {
+ mAllocated += 1;
+ return ByteBuffer.allocate(chooseCapacity(mInitialBufferSize, needed));
+ }
+
+ try {
+ wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+
+ public void release(ByteBuffer buffer) {
+ synchronized (this) {
+ buffer.clear();
+ mBuffers[mAvailable++] = buffer;
+ notifyAll();
+ }
+ }
+
+ public ByteBuffer grow(ByteBuffer buffer, int needed) {
+ int capacity = buffer.capacity();
+ if (capacity < needed) {
+ final ByteBuffer oldBuffer = buffer;
+ capacity = chooseCapacity(capacity, needed);
+ buffer = ByteBuffer.allocate(capacity);
+ oldBuffer.flip();
+ buffer.put(oldBuffer);
+ }
+ return buffer;
+ }
+
+ private int chooseCapacity(int capacity, int needed) {
+ while (capacity < needed) {
+ capacity *= 2;
+ }
+ if (capacity > mMaxBufferSize) {
+ if (needed > mMaxBufferSize) {
+ throw new IllegalArgumentException("Requested size " + needed
+ + " is larger than maximum buffer size " + mMaxBufferSize + ".");
+ }
+ capacity = mMaxBufferSize;
+ }
+ return capacity;
+ }
+}
diff --git a/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Logger.java b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Logger.java
new file mode 100644
index 0000000..e0b7e82
--- /dev/null
+++ b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Logger.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.common;
+
+public abstract class Logger {
+ public abstract void log(String message);
+
+ public void logError(String message) {
+ log("ERROR: " + message);
+ }
+}
diff --git a/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Protocol.java b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Protocol.java
new file mode 100644
index 0000000..46fee32
--- /dev/null
+++ b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Protocol.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.common;
+
+/**
+ * Defines message types.
+ */
+public class Protocol {
+ // Message header.
+ // 0: service id (16 bits)
+ // 2: what (16 bits)
+ // 4: content size (32 bits)
+ // 8: ... content follows ...
+ static final int HEADER_SIZE = 8;
+
+ // Maximum size of a message envelope including the header and contents.
+ static final int MAX_ENVELOPE_SIZE = 64 * 1024;
+
+ /**
+ * Maximum message content size.
+ */
+ public static final int MAX_CONTENT_SIZE = MAX_ENVELOPE_SIZE - HEADER_SIZE;
+
+ public static final class DisplaySinkService {
+ private DisplaySinkService() { }
+
+ public static final int ID = 1;
+
+ // Query sink capabilities.
+ // Replies with sink available or not available.
+ public static final int MSG_QUERY = 1;
+
+ // Send MPEG2-TS H.264 encoded content.
+ public static final int MSG_CONTENT = 2;
+ }
+
+ public static final class DisplaySourceService {
+ private DisplaySourceService() { }
+
+ public static final int ID = 2;
+
+ // Sink is now available for use.
+ // 0: width (32 bits)
+ // 4: height (32 bits)
+ // 8: density dpi (32 bits)
+ public static final int MSG_SINK_AVAILABLE = 1;
+
+ // Sink is no longer available for use.
+ public static final int MSG_SINK_NOT_AVAILABLE = 2;
+ }
+}
diff --git a/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Service.java b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Service.java
new file mode 100644
index 0000000..70b3806
--- /dev/null
+++ b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Service.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.common;
+
+import com.android.accessorydisplay.common.Transport;
+
+import android.content.Context;
+import android.os.Looper;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Base implementation of a service that communicates over a transport.
+ * <p>
+ * This object's interface is single-threaded. It is only intended to be
+ * accessed from the {@link Looper} thread on which the transport was created.
+ * </p>
+ */
+public abstract class Service implements Transport.Callback {
+ private final Context mContext;
+ private final Transport mTransport;
+ private final int mServiceId;
+
+ public Service(Context context, Transport transport, int serviceId) {
+ mContext = context;
+ mTransport = transport;
+ mServiceId = serviceId;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public int getServiceId() {
+ return mServiceId;
+ }
+
+ public Transport getTransport() {
+ return mTransport;
+ }
+
+ public Logger getLogger() {
+ return mTransport.getLogger();
+ }
+
+ public void start() {
+ mTransport.registerService(mServiceId, this);
+ }
+
+ public void stop() {
+ mTransport.unregisterService(mServiceId);
+ }
+
+ @Override
+ public void onMessageReceived(int service, int what, ByteBuffer content) {
+ }
+}
diff --git a/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Transport.java b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Transport.java
new file mode 100644
index 0000000..84897d3
--- /dev/null
+++ b/tests/AccessoryDisplay/common/src/com/android/accessorydisplay/common/Transport.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.common;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.SparseArray;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * A simple message transport.
+ * <p>
+ * This object's interface is thread-safe, however incoming messages
+ * are always delivered on the {@link Looper} thread on which the transport
+ * was created.
+ * </p>
+ */
+public abstract class Transport {
+ private static final int MAX_INPUT_BUFFERS = 8;
+
+ private final Logger mLogger;
+
+ // The transport thread looper and handler.
+ private final TransportHandler mHandler;
+
+ // Lock to guard all mutable state.
+ private final Object mLock = new Object();
+
+ // The output buffer. Set to null when the transport is closed.
+ private ByteBuffer mOutputBuffer;
+
+ // The input buffer pool.
+ private BufferPool mInputBufferPool;
+
+ // The reader thread. Initialized when reading starts.
+ private ReaderThread mThread;
+
+ // The list of callbacks indexed by service id.
+ private final SparseArray<Callback> mServices = new SparseArray<Callback>();
+
+ public Transport(Logger logger, int maxPacketSize) {
+ mLogger = logger;
+ mHandler = new TransportHandler();
+ mOutputBuffer = ByteBuffer.allocate(maxPacketSize);
+ mInputBufferPool = new BufferPool(
+ maxPacketSize, Protocol.MAX_ENVELOPE_SIZE, MAX_INPUT_BUFFERS);
+ }
+
+ /**
+ * Gets the logger for debugging.
+ */
+ public Logger getLogger() {
+ return mLogger;
+ }
+
+ /**
+ * Gets the handler on the transport's thread.
+ */
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ /**
+ * Closes the transport.
+ */
+ public void close() {
+ synchronized (mLock) {
+ if (mOutputBuffer != null) {
+ if (mThread == null) {
+ ioClose();
+ } else {
+ // If the thread was started then it will be responsible for
+ // closing the stream when it quits because it may currently
+ // be in the process of reading from the stream so we can't simply
+ // shut it down right now.
+ mThread.quit();
+ }
+ mOutputBuffer = null;
+ }
+ }
+ }
+
+ /**
+ * Sends a message.
+ *
+ * @param service The service to whom the message is addressed.
+ * @param what The message type.
+ * @param content The content, or null if there is none.
+ * @return True if the message was sent successfully, false if an error occurred.
+ */
+ public boolean sendMessage(int service, int what, ByteBuffer content) {
+ checkServiceId(service);
+ checkMessageId(what);
+
+ try {
+ synchronized (mLock) {
+ if (mOutputBuffer == null) {
+ mLogger.logError("Send message failed because transport was closed.");
+ return false;
+ }
+
+ final byte[] outputArray = mOutputBuffer.array();
+ final int capacity = mOutputBuffer.capacity();
+ mOutputBuffer.clear();
+ mOutputBuffer.putShort((short)service);
+ mOutputBuffer.putShort((short)what);
+ if (content == null) {
+ mOutputBuffer.putInt(0);
+ } else {
+ final int contentLimit = content.limit();
+ int contentPosition = content.position();
+ int contentRemaining = contentLimit - contentPosition;
+ if (contentRemaining > Protocol.MAX_CONTENT_SIZE) {
+ throw new IllegalArgumentException("Message content too large: "
+ + contentRemaining + " > " + Protocol.MAX_CONTENT_SIZE);
+ }
+ mOutputBuffer.putInt(contentRemaining);
+ while (contentRemaining != 0) {
+ final int outputAvailable = capacity - mOutputBuffer.position();
+ if (contentRemaining <= outputAvailable) {
+ mOutputBuffer.put(content);
+ break;
+ }
+ content.limit(contentPosition + outputAvailable);
+ mOutputBuffer.put(content);
+ content.limit(contentLimit);
+ ioWrite(outputArray, 0, capacity);
+ contentPosition += outputAvailable;
+ contentRemaining -= outputAvailable;
+ mOutputBuffer.clear();
+ }
+ }
+ ioWrite(outputArray, 0, mOutputBuffer.position());
+ return true;
+ }
+ } catch (IOException ex) {
+ mLogger.logError("Send message failed: " + ex);
+ return false;
+ }
+ }
+
+ /**
+ * Starts reading messages on a separate thread.
+ */
+ public void startReading() {
+ synchronized (mLock) {
+ if (mOutputBuffer == null) {
+ throw new IllegalStateException("Transport has been closed");
+ }
+
+ mThread = new ReaderThread();
+ mThread.start();
+ }
+ }
+
+ /**
+ * Registers a service and provides a callback to receive messages.
+ *
+ * @param service The service id.
+ * @param callback The callback to use.
+ */
+ public void registerService(int service, Callback callback) {
+ checkServiceId(service);
+ if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
+ }
+
+ synchronized (mLock) {
+ mServices.put(service, callback);
+ }
+ }
+
+ /**
+ * Unregisters a service.
+ *
+ * @param service The service to unregister.
+ */
+ public void unregisterService(int service) {
+ checkServiceId(service);
+
+ synchronized (mLock) {
+ mServices.remove(service);
+ }
+ }
+
+ private void dispatchMessageReceived(int service, int what, ByteBuffer content) {
+ final Callback callback;
+ synchronized (mLock) {
+ callback = mServices.get(service);
+ }
+ if (callback != null) {
+ callback.onMessageReceived(service, what, content);
+ } else {
+ mLogger.log("Discarding message " + what
+ + " for unregistered service " + service);
+ }
+ }
+
+ private static void checkServiceId(int service) {
+ if (service < 0 || service > 0xffff) {
+ throw new IllegalArgumentException("service id out of range: " + service);
+ }
+ }
+
+ private static void checkMessageId(int what) {
+ if (what < 0 || what > 0xffff) {
+ throw new IllegalArgumentException("message id out of range: " + what);
+ }
+ }
+
+ // The IO methods must be safe to call on any thread.
+ // They may be called concurrently.
+ protected abstract void ioClose();
+ protected abstract int ioRead(byte[] buffer, int offset, int count)
+ throws IOException;
+ protected abstract void ioWrite(byte[] buffer, int offset, int count)
+ throws IOException;
+
+ /**
+ * Callback for services that handle received messages.
+ */
+ public interface Callback {
+ /**
+ * Indicates that a message was received.
+ *
+ * @param service The service to whom the message is addressed.
+ * @param what The message type.
+ * @param content The content, or null if there is none.
+ */
+ public void onMessageReceived(int service, int what, ByteBuffer content);
+ }
+
+ final class TransportHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ final ByteBuffer buffer = (ByteBuffer)msg.obj;
+ try {
+ final int limit = buffer.limit();
+ while (buffer.position() < limit) {
+ final int service = buffer.getShort() & 0xffff;
+ final int what = buffer.getShort() & 0xffff;
+ final int contentSize = buffer.getInt();
+ if (contentSize == 0) {
+ dispatchMessageReceived(service, what, null);
+ } else {
+ final int end = buffer.position() + contentSize;
+ buffer.limit(end);
+ dispatchMessageReceived(service, what, buffer);
+ buffer.limit(limit);
+ buffer.position(end);
+ }
+ }
+ } finally {
+ mInputBufferPool.release(buffer);
+ }
+ }
+ }
+
+ final class ReaderThread extends Thread {
+ // Set to true when quitting.
+ private volatile boolean mQuitting;
+
+ public ReaderThread() {
+ super("Accessory Display Transport");
+ }
+
+ @Override
+ public void run() {
+ loop();
+ ioClose();
+ }
+
+ private void loop() {
+ ByteBuffer buffer = null;
+ int length = Protocol.HEADER_SIZE;
+ int contentSize = -1;
+ outer: while (!mQuitting) {
+ // Get a buffer.
+ if (buffer == null) {
+ buffer = mInputBufferPool.acquire(length);
+ } else {
+ buffer = mInputBufferPool.grow(buffer, length);
+ }
+
+ // Read more data until needed number of bytes obtained.
+ int position = buffer.position();
+ int count;
+ try {
+ count = ioRead(buffer.array(), position, buffer.capacity() - position);
+ if (count < 0) {
+ break; // end of stream
+ }
+ } catch (IOException ex) {
+ mLogger.logError("Read failed: " + ex);
+ break; // error
+ }
+ position += count;
+ buffer.position(position);
+ if (contentSize < 0 && position >= Protocol.HEADER_SIZE) {
+ contentSize = buffer.getInt(4);
+ if (contentSize < 0 || contentSize > Protocol.MAX_CONTENT_SIZE) {
+ mLogger.logError("Encountered invalid content size: " + contentSize);
+ break; // malformed stream
+ }
+ length += contentSize;
+ }
+ if (position < length) {
+ continue; // need more data
+ }
+
+ // There is at least one complete message in the buffer.
+ // Find the end of a contiguous chunk of complete messages.
+ int next = length;
+ int remaining;
+ for (;;) {
+ length = Protocol.HEADER_SIZE;
+ remaining = position - next;
+ if (remaining < length) {
+ contentSize = -1;
+ break; // incomplete header, need more data
+ }
+ contentSize = buffer.getInt(next + 4);
+ if (contentSize < 0 || contentSize > Protocol.MAX_CONTENT_SIZE) {
+ mLogger.logError("Encountered invalid content size: " + contentSize);
+ break outer; // malformed stream
+ }
+ length += contentSize;
+ if (remaining < length) {
+ break; // incomplete content, need more data
+ }
+ next += length;
+ }
+
+ // Post the buffer then don't modify it anymore.
+ // Now this is kind of sneaky. We know that no other threads will
+ // be acquiring buffers from the buffer pool so we can keep on
+ // referring to this buffer as long as we don't modify its contents.
+ // This allows us to operate in a single-buffered mode if desired.
+ buffer.limit(next);
+ buffer.rewind();
+ mHandler.obtainMessage(0, buffer).sendToTarget();
+
+ // If there is an incomplete message at the end, then we will need
+ // to copy it to a fresh buffer before continuing. In the single-buffered
+ // case, we may acquire the same buffer as before which is fine.
+ if (remaining == 0) {
+ buffer = null;
+ } else {
+ final ByteBuffer oldBuffer = buffer;
+ buffer = mInputBufferPool.acquire(length);
+ System.arraycopy(oldBuffer.array(), next, buffer.array(), 0, remaining);
+ buffer.position(remaining);
+ }
+ }
+
+ if (buffer != null) {
+ mInputBufferPool.release(buffer);
+ }
+ }
+
+ public void quit() {
+ mQuitting = true;
+ }
+ }
+}
diff --git a/tests/AccessoryDisplay/sink/Android.mk b/tests/AccessoryDisplay/sink/Android.mk
new file mode 100644
index 0000000..772ce0c
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Build the application.
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := AccessoryDisplaySink
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR = $(LOCAL_PATH)/res
+LOCAL_STATIC_JAVA_LIBRARIES := AccessoryDisplayCommon
+include $(BUILD_PACKAGE)
diff --git a/tests/AccessoryDisplay/sink/AndroidManifest.xml b/tests/AccessoryDisplay/sink/AndroidManifest.xml
new file mode 100644
index 0000000..72d498f
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.accessorydisplay.sink" >
+
+ <uses-feature android:name="android.hardware.usb.host"/>
+ <uses-sdk android:minSdkVersion="18" />
+
+ <application android:label="@string/app_name"
+ android:icon="@drawable/ic_app"
+ android:hardwareAccelerated="true">
+
+ <activity android:name=".SinkActivity"
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.Holo">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+ android:resource="@xml/usb_device_filter"/>
+ </activity>
+
+ </application>
+</manifest>
diff --git a/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png
new file mode 100755
index 0000000..66a1984
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png
Binary files differ
diff --git a/tests/AccessoryDisplay/sink/res/drawable-mdpi/ic_app.png b/tests/AccessoryDisplay/sink/res/drawable-mdpi/ic_app.png
new file mode 100644
index 0000000..5ae7701
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/res/drawable-mdpi/ic_app.png
Binary files differ
diff --git a/tests/AccessoryDisplay/sink/res/layout/sink_activity.xml b/tests/AccessoryDisplay/sink/res/layout/sink_activity.xml
new file mode 100644
index 0000000..6afb850
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/res/layout/sink_activity.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <TextView android:id="@+id/fpsTextView"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:padding="4dp" />
+
+ <TextView android:id="@+id/logTextView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="2">
+ <SurfaceView android:id="@+id/surfaceView"
+ android:layout_width="640px"
+ android:layout_height="480px" />
+ </FrameLayout>
+</LinearLayout>
diff --git a/tests/AccessoryDisplay/sink/res/values/strings.xml b/tests/AccessoryDisplay/sink/res/values/strings.xml
new file mode 100644
index 0000000..29cd001
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name">Accessory Display Sink</string>
+</resources>
diff --git a/tests/AccessoryDisplay/sink/res/xml/usb_device_filter.xml b/tests/AccessoryDisplay/sink/res/xml/usb_device_filter.xml
new file mode 100644
index 0000000..e8fe929
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/res/xml/usb_device_filter.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <!-- Match all devices -->
+ <usb-device />
+
+ <!-- Android USB accessory: accessory -->
+ <usb-device vendor-id="16601" product-id="11520" />
+ <!-- Android USB accessory: accessory + adb -->
+ <usb-device vendor-id="16601" product-id="11521" />
+</resources>
diff --git a/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/DisplaySinkService.java b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/DisplaySinkService.java
new file mode 100644
index 0000000..daec845
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/DisplaySinkService.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.sink;
+
+import com.android.accessorydisplay.common.Protocol;
+import com.android.accessorydisplay.common.Service;
+import com.android.accessorydisplay.common.Transport;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaFormat;
+import android.os.Handler;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import java.nio.ByteBuffer;
+
+public class DisplaySinkService extends Service implements SurfaceHolder.Callback {
+ private final ByteBuffer mBuffer = ByteBuffer.allocate(12);
+ private final Handler mTransportHandler;
+ private final int mDensityDpi;
+
+ private SurfaceView mSurfaceView;
+
+ // These fields are guarded by the following lock.
+ // This is to ensure that the surface lifecycle is respected. Although decoding
+ // happens on the transport thread, we are not allowed to access the surface after
+ // it is destroyed by the UI thread so we need to stop the codec immediately.
+ private final Object mSurfaceAndCodecLock = new Object();
+ private Surface mSurface;
+ private int mSurfaceWidth;
+ private int mSurfaceHeight;
+ private MediaCodec mCodec;
+ private ByteBuffer[] mCodecInputBuffers;
+ private BufferInfo mCodecBufferInfo;
+
+ public DisplaySinkService(Context context, Transport transport, int densityDpi) {
+ super(context, transport, Protocol.DisplaySinkService.ID);
+ mTransportHandler = transport.getHandler();
+ mDensityDpi = densityDpi;
+ }
+
+ public void setSurfaceView(final SurfaceView surfaceView) {
+ if (mSurfaceView != surfaceView) {
+ final SurfaceView oldSurfaceView = mSurfaceView;
+ mSurfaceView = surfaceView;
+
+ if (oldSurfaceView != null) {
+ oldSurfaceView.post(new Runnable() {
+ @Override
+ public void run() {
+ final SurfaceHolder holder = oldSurfaceView.getHolder();
+ holder.removeCallback(DisplaySinkService.this);
+ updateSurfaceFromUi(null);
+ }
+ });
+ }
+
+ if (surfaceView != null) {
+ surfaceView.post(new Runnable() {
+ @Override
+ public void run() {
+ final SurfaceHolder holder = surfaceView.getHolder();
+ holder.addCallback(DisplaySinkService.this);
+ updateSurfaceFromUi(holder);
+ }
+ });
+ }
+ }
+ }
+
+ @Override
+ public void onMessageReceived(int service, int what, ByteBuffer content) {
+ switch (what) {
+ case Protocol.DisplaySinkService.MSG_QUERY: {
+ getLogger().log("Received MSG_QUERY.");
+ sendSinkStatus();
+ break;
+ }
+
+ case Protocol.DisplaySinkService.MSG_CONTENT: {
+ decode(content);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ // Ignore. Wait for surface changed event that follows.
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ updateSurfaceFromUi(holder);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ updateSurfaceFromUi(null);
+ }
+
+ private void updateSurfaceFromUi(SurfaceHolder holder) {
+ Surface surface = null;
+ int width = 0, height = 0;
+ if (holder != null && !holder.isCreating()) {
+ surface = holder.getSurface();
+ if (surface.isValid()) {
+ final Rect frame = holder.getSurfaceFrame();
+ width = frame.width();
+ height = frame.height();
+ } else {
+ surface = null;
+ }
+ }
+
+ synchronized (mSurfaceAndCodecLock) {
+ if (mSurface == surface && mSurfaceWidth == width && mSurfaceHeight == height) {
+ return;
+ }
+
+ mSurface = surface;
+ mSurfaceWidth = width;
+ mSurfaceHeight = height;
+
+ if (mCodec != null) {
+ mCodec.stop();
+ mCodec = null;
+ mCodecInputBuffers = null;
+ mCodecBufferInfo = null;
+ }
+
+ if (mSurface != null) {
+ MediaFormat format = MediaFormat.createVideoFormat(
+ "video/avc", mSurfaceWidth, mSurfaceHeight);
+ mCodec = MediaCodec.createDecoderByType("video/avc");
+ mCodec.configure(format, mSurface, null, 0);
+ mCodec.start();
+ mCodecBufferInfo = new BufferInfo();
+ }
+
+ mTransportHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ sendSinkStatus();
+ }
+ });
+ }
+ }
+
+ private void decode(ByteBuffer content) {
+ if (content == null) {
+ return;
+ }
+ synchronized (mSurfaceAndCodecLock) {
+ if (mCodec == null) {
+ return;
+ }
+
+ while (content.hasRemaining()) {
+ if (!provideCodecInputLocked(content)) {
+ getLogger().log("Dropping content because there are no available buffers.");
+ return;
+ }
+
+ consumeCodecOutputLocked();
+ }
+ }
+ }
+
+ private boolean provideCodecInputLocked(ByteBuffer content) {
+ final int index = mCodec.dequeueInputBuffer(0);
+ if (index < 0) {
+ return false;
+ }
+ if (mCodecInputBuffers == null) {
+ mCodecInputBuffers = mCodec.getInputBuffers();
+ }
+ final ByteBuffer buffer = mCodecInputBuffers[index];
+ final int capacity = buffer.capacity();
+ buffer.clear();
+ if (content.remaining() <= capacity) {
+ buffer.put(content);
+ } else {
+ final int limit = content.limit();
+ content.limit(content.position() + capacity);
+ buffer.put(content);
+ content.limit(limit);
+ }
+ buffer.flip();
+ mCodec.queueInputBuffer(index, 0, buffer.limit(), 0, 0);
+ return true;
+ }
+
+ private void consumeCodecOutputLocked() {
+ for (;;) {
+ final int index = mCodec.dequeueOutputBuffer(mCodecBufferInfo, 0);
+ if (index >= 0) {
+ mCodec.releaseOutputBuffer(index, true /*render*/);
+ } else if (index != MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED
+ && index != MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ break;
+ }
+ }
+ }
+
+ private void sendSinkStatus() {
+ synchronized (mSurfaceAndCodecLock) {
+ if (mCodec != null) {
+ mBuffer.clear();
+ mBuffer.putInt(mSurfaceWidth);
+ mBuffer.putInt(mSurfaceHeight);
+ mBuffer.putInt(mDensityDpi);
+ mBuffer.flip();
+ getTransport().sendMessage(Protocol.DisplaySourceService.ID,
+ Protocol.DisplaySourceService.MSG_SINK_AVAILABLE, mBuffer);
+ } else {
+ getTransport().sendMessage(Protocol.DisplaySourceService.ID,
+ Protocol.DisplaySourceService.MSG_SINK_NOT_AVAILABLE, null);
+ }
+ }
+ }
+}
diff --git a/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/SinkActivity.java b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/SinkActivity.java
new file mode 100644
index 0000000..6fe2cfb
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/SinkActivity.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.sink;
+
+import com.android.accessorydisplay.common.Logger;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaFormat;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.TextView;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.Map;
+
+public class SinkActivity extends Activity {
+ private static final String TAG = "SinkActivity";
+
+ private static final String ACTION_USB_DEVICE_PERMISSION =
+ "com.android.accessorydisplay.sink.ACTION_USB_DEVICE_PERMISSION";
+
+ private static final String MANUFACTURER = "Android";
+ private static final String MODEL = "Accessory Display";
+ private static final String DESCRIPTION = "Accessory Display Sink Test Application";
+ private static final String VERSION = "1.0";
+ private static final String URI = "http://www.android.com/";
+ private static final String SERIAL = "0000000012345678";
+
+ private static final int MULTITOUCH_DEVICE_ID = 0;
+ private static final int MULTITOUCH_REPORT_ID = 1;
+ private static final int MULTITOUCH_MAX_CONTACTS = 1;
+
+ private UsbManager mUsbManager;
+ private DeviceReceiver mReceiver;
+ private TextView mLogTextView;
+ private TextView mFpsTextView;
+ private SurfaceView mSurfaceView;
+ private Logger mLogger;
+
+ private boolean mConnected;
+ private int mProtocolVersion;
+ private UsbDevice mDevice;
+ private UsbInterface mAccessoryInterface;
+ private UsbDeviceConnection mAccessoryConnection;
+ private UsbEndpoint mControlEndpoint;
+ private UsbAccessoryBulkTransport mTransport;
+
+ private boolean mAttached;
+ private DisplaySinkService mDisplaySinkService;
+
+ private final ByteBuffer mHidBuffer = ByteBuffer.allocate(4096);
+ private UsbHid.Multitouch mMultitouch;
+ private boolean mMultitouchEnabled;
+ private UsbHid.Multitouch.Contact[] mMultitouchContacts;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+
+ setContentView(R.layout.sink_activity);
+
+ mLogTextView = (TextView) findViewById(R.id.logTextView);
+ mLogTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
+ mLogger = new TextLogger();
+
+ mFpsTextView = (TextView) findViewById(R.id.fpsTextView);
+
+ mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
+ mSurfaceView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ sendHidTouch(event);
+ return true;
+ }
+ });
+
+ mLogger.log("Waiting for accessory display source to be attached to USB...");
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ filter.addAction(ACTION_USB_DEVICE_PERMISSION);
+ mReceiver = new DeviceReceiver();
+ registerReceiver(mReceiver, filter);
+
+ Intent intent = getIntent();
+ if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+ UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (device != null) {
+ onDeviceAttached(device);
+ }
+ } else {
+ Map<String, UsbDevice> devices = mUsbManager.getDeviceList();
+ if (devices != null) {
+ for (UsbDevice device : devices.values()) {
+ onDeviceAttached(device);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ unregisterReceiver(mReceiver);
+ }
+
+ private void onDeviceAttached(UsbDevice device) {
+ mLogger.log("USB device attached: " + device);
+ if (!mConnected) {
+ connect(device);
+ }
+ }
+
+ private void onDeviceDetached(UsbDevice device) {
+ mLogger.log("USB device detached: " + device);
+ if (mConnected && device.equals(mDevice)) {
+ disconnect();
+ }
+ }
+
+ private void connect(UsbDevice device) {
+ if (mConnected) {
+ disconnect();
+ }
+
+ // Check whether we have permission to access the device.
+ if (!mUsbManager.hasPermission(device)) {
+ mLogger.log("Prompting the user for access to the device.");
+ Intent intent = new Intent(ACTION_USB_DEVICE_PERMISSION);
+ intent.setPackage(getPackageName());
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ mUsbManager.requestPermission(device, pendingIntent);
+ return;
+ }
+
+ // Claim the device.
+ UsbDeviceConnection conn = mUsbManager.openDevice(device);
+ if (conn == null) {
+ mLogger.logError("Could not obtain device connection.");
+ return;
+ }
+ UsbInterface iface = device.getInterface(0);
+ UsbEndpoint controlEndpoint = iface.getEndpoint(0);
+ if (!conn.claimInterface(iface, true)) {
+ mLogger.logError("Could not claim interface.");
+ return;
+ }
+ try {
+ // If already in accessory mode, then connect to the device.
+ if (isAccessory(device)) {
+ mLogger.log("Connecting to accessory...");
+
+ int protocolVersion = getProtocol(conn);
+ if (protocolVersion < 1) {
+ mLogger.logError("Device does not support accessory protocol.");
+ return;
+ }
+ mLogger.log("Protocol version: " + protocolVersion);
+
+ // Setup bulk endpoints.
+ UsbEndpoint bulkIn = null;
+ UsbEndpoint bulkOut = null;
+ for (int i = 0; i < iface.getEndpointCount(); i++) {
+ UsbEndpoint ep = iface.getEndpoint(i);
+ if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
+ if (bulkIn == null) {
+ mLogger.log(String.format("Bulk IN endpoint: %d", i));
+ bulkIn = ep;
+ }
+ } else {
+ if (bulkOut == null) {
+ mLogger.log(String.format("Bulk OUT endpoint: %d", i));
+ bulkOut = ep;
+ }
+ }
+ }
+ if (bulkIn == null || bulkOut == null) {
+ mLogger.logError("Unable to find bulk endpoints");
+ return;
+ }
+
+ mLogger.log("Connected");
+ mConnected = true;
+ mDevice = device;
+ mProtocolVersion = protocolVersion;
+ mAccessoryInterface = iface;
+ mAccessoryConnection = conn;
+ mControlEndpoint = controlEndpoint;
+ mTransport = new UsbAccessoryBulkTransport(mLogger, conn, bulkIn, bulkOut);
+ if (mProtocolVersion >= 2) {
+ registerHid();
+ }
+ startServices();
+ mTransport.startReading();
+ return;
+ }
+
+ // Do accessory negotiation.
+ mLogger.log("Attempting to switch device to accessory mode...");
+
+ // Send get protocol.
+ int protocolVersion = getProtocol(conn);
+ if (protocolVersion < 1) {
+ mLogger.logError("Device does not support accessory protocol.");
+ return;
+ }
+ mLogger.log("Protocol version: " + protocolVersion);
+
+ // Send identifying strings.
+ sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_MANUFACTURER, MANUFACTURER);
+ sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_MODEL, MODEL);
+ sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_DESCRIPTION, DESCRIPTION);
+ sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_VERSION, VERSION);
+ sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_URI, URI);
+ sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_SERIAL, SERIAL);
+
+ // Send start.
+ // The device should re-enumerate as an accessory.
+ mLogger.log("Sending accessory start request.");
+ int len = conn.controlTransfer(UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
+ UsbAccessoryConstants.ACCESSORY_START, 0, 0, null, 0, 10000);
+ if (len != 0) {
+ mLogger.logError("Device refused to switch to accessory mode.");
+ } else {
+ mLogger.log("Waiting for device to re-enumerate...");
+ }
+ } finally {
+ if (!mConnected) {
+ conn.releaseInterface(iface);
+ }
+ }
+ }
+
+ private void disconnect() {
+ mLogger.log("Disconnecting from device: " + mDevice);
+ stopServices();
+ unregisterHid();
+
+ mLogger.log("Disconnected.");
+ mConnected = false;
+ mDevice = null;
+ mAccessoryConnection = null;
+ mAccessoryInterface = null;
+ mControlEndpoint = null;
+ if (mTransport != null) {
+ mTransport.close();
+ mTransport = null;
+ }
+ }
+
+ private void registerHid() {
+ mLogger.log("Registering HID multitouch device.");
+
+ mMultitouch = new UsbHid.Multitouch(MULTITOUCH_REPORT_ID, MULTITOUCH_MAX_CONTACTS,
+ mSurfaceView.getWidth(), mSurfaceView.getHeight());
+
+ mHidBuffer.clear();
+ mMultitouch.generateDescriptor(mHidBuffer);
+ mHidBuffer.flip();
+
+ mLogger.log("HID descriptor size: " + mHidBuffer.limit());
+ mLogger.log("HID report size: " + mMultitouch.getReportSize());
+
+ final int maxPacketSize = mControlEndpoint.getMaxPacketSize();
+ mLogger.log("Control endpoint max packet size: " + maxPacketSize);
+ if (mMultitouch.getReportSize() > maxPacketSize) {
+ mLogger.logError("HID report is too big for this accessory.");
+ return;
+ }
+
+ int len = mAccessoryConnection.controlTransfer(
+ UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
+ UsbAccessoryConstants.ACCESSORY_REGISTER_HID,
+ MULTITOUCH_DEVICE_ID, mHidBuffer.limit(), null, 0, 10000);
+ if (len != 0) {
+ mLogger.logError("Device rejected ACCESSORY_REGISTER_HID request.");
+ return;
+ }
+
+ while (mHidBuffer.hasRemaining()) {
+ int position = mHidBuffer.position();
+ int count = Math.min(mHidBuffer.remaining(), maxPacketSize);
+ len = mAccessoryConnection.controlTransfer(
+ UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
+ UsbAccessoryConstants.ACCESSORY_SET_HID_REPORT_DESC,
+ MULTITOUCH_DEVICE_ID, 0,
+ mHidBuffer.array(), position, count, 10000);
+ if (len != count) {
+ mLogger.logError("Device rejected ACCESSORY_SET_HID_REPORT_DESC request.");
+ return;
+ }
+ mHidBuffer.position(position + count);
+ }
+
+ mLogger.log("HID device registered.");
+
+ mMultitouchEnabled = true;
+ if (mMultitouchContacts == null) {
+ mMultitouchContacts = new UsbHid.Multitouch.Contact[MULTITOUCH_MAX_CONTACTS];
+ for (int i = 0; i < MULTITOUCH_MAX_CONTACTS; i++) {
+ mMultitouchContacts[i] = new UsbHid.Multitouch.Contact();
+ }
+ }
+ }
+
+ private void unregisterHid() {
+ mMultitouch = null;
+ mMultitouchContacts = null;
+ mMultitouchEnabled = false;
+ }
+
+ private void sendHidTouch(MotionEvent event) {
+ if (mMultitouchEnabled) {
+ mLogger.log("Sending touch event: " + event);
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE: {
+ final int pointerCount =
+ Math.min(MULTITOUCH_MAX_CONTACTS, event.getPointerCount());
+ final int historySize = event.getHistorySize();
+ for (int p = 0; p < pointerCount; p++) {
+ mMultitouchContacts[p].id = event.getPointerId(p);
+ }
+ for (int h = 0; h < historySize; h++) {
+ for (int p = 0; p < pointerCount; p++) {
+ mMultitouchContacts[p].x = (int)event.getHistoricalX(p, h);
+ mMultitouchContacts[p].y = (int)event.getHistoricalY(p, h);
+ }
+ sendHidTouchReport(pointerCount);
+ }
+ for (int p = 0; p < pointerCount; p++) {
+ mMultitouchContacts[p].x = (int)event.getX(p);
+ mMultitouchContacts[p].y = (int)event.getY(p);
+ }
+ sendHidTouchReport(pointerCount);
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ sendHidTouchReport(0);
+ break;
+ }
+ }
+ }
+
+ private void sendHidTouchReport(int contactCount) {
+ mHidBuffer.clear();
+ mMultitouch.generateReport(mHidBuffer, mMultitouchContacts, contactCount);
+ mHidBuffer.flip();
+
+ int count = mHidBuffer.limit();
+ int len = mAccessoryConnection.controlTransfer(
+ UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
+ UsbAccessoryConstants.ACCESSORY_SEND_HID_EVENT,
+ MULTITOUCH_DEVICE_ID, 0,
+ mHidBuffer.array(), 0, count, 10000);
+ if (len != count) {
+ mLogger.logError("Device rejected ACCESSORY_SEND_HID_EVENT request.");
+ return;
+ }
+ }
+
+ private void startServices() {
+ mDisplaySinkService = new DisplaySinkService(this, mTransport,
+ getResources().getConfiguration().densityDpi);
+ mDisplaySinkService.start();
+
+ if (mAttached) {
+ mDisplaySinkService.setSurfaceView(mSurfaceView);
+ }
+ }
+
+ private void stopServices() {
+ if (mDisplaySinkService != null) {
+ mDisplaySinkService.stop();
+ mDisplaySinkService = null;
+ }
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mAttached = true;
+ if (mDisplaySinkService != null) {
+ mDisplaySinkService.setSurfaceView(mSurfaceView);
+ }
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ mAttached = false;
+ if (mDisplaySinkService != null) {
+ mDisplaySinkService.setSurfaceView(null);
+ }
+ }
+
+ private int getProtocol(UsbDeviceConnection conn) {
+ byte buffer[] = new byte[2];
+ int len = conn.controlTransfer(
+ UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_VENDOR,
+ UsbAccessoryConstants.ACCESSORY_GET_PROTOCOL, 0, 0, buffer, 2, 10000);
+ if (len != 2) {
+ return -1;
+ }
+ return (buffer[1] << 8) | buffer[0];
+ }
+
+ private void sendString(UsbDeviceConnection conn, int index, String string) {
+ byte[] buffer = (string + "\0").getBytes();
+ int len = conn.controlTransfer(UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
+ UsbAccessoryConstants.ACCESSORY_SEND_STRING, 0, index,
+ buffer, buffer.length, 10000);
+ if (len != buffer.length) {
+ mLogger.logError("Failed to send string " + index + ": \"" + string + "\"");
+ } else {
+ mLogger.log("Sent string " + index + ": \"" + string + "\"");
+ }
+ }
+
+ private static boolean isAccessory(UsbDevice device) {
+ final int vid = device.getVendorId();
+ final int pid = device.getProductId();
+ return vid == UsbAccessoryConstants.USB_ACCESSORY_VENDOR_ID
+ && (pid == UsbAccessoryConstants.USB_ACCESSORY_PRODUCT_ID
+ || pid == UsbAccessoryConstants.USB_ACCESSORY_ADB_PRODUCT_ID);
+ }
+
+ class TextLogger extends Logger {
+ @Override
+ public void log(final String message) {
+ Log.d(TAG, message);
+
+ mLogTextView.post(new Runnable() {
+ @Override
+ public void run() {
+ mLogTextView.append(message);
+ mLogTextView.append("\n");
+ }
+ });
+ }
+ }
+
+ class DeviceReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (device != null) {
+ String action = intent.getAction();
+ if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+ onDeviceAttached(device);
+ } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
+ onDeviceDetached(device);
+ } else if (action.equals(ACTION_USB_DEVICE_PERMISSION)) {
+ if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
+ mLogger.log("Device permission granted: " + device);
+ onDeviceAttached(device);
+ } else {
+ mLogger.logError("Device permission denied: " + device);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/UsbAccessoryBulkTransport.java b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/UsbAccessoryBulkTransport.java
new file mode 100644
index 0000000..a15bfad
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/UsbAccessoryBulkTransport.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.sink;
+
+import com.android.accessorydisplay.common.Logger;
+import com.android.accessorydisplay.common.Transport;
+
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+
+import java.io.IOException;
+
+/**
+ * Sends or receives messages using bulk endpoints associated with a {@link UsbDevice}
+ * that represents a USB accessory.
+ */
+public class UsbAccessoryBulkTransport extends Transport {
+ private static final int TIMEOUT_MILLIS = 1000;
+
+ private UsbDeviceConnection mConnection;
+ private UsbEndpoint mBulkInEndpoint;
+ private UsbEndpoint mBulkOutEndpoint;
+
+ public UsbAccessoryBulkTransport(Logger logger, UsbDeviceConnection connection,
+ UsbEndpoint bulkInEndpoint, UsbEndpoint bulkOutEndpoint) {
+ super(logger, 16384);
+ mConnection = connection;
+ mBulkInEndpoint = bulkInEndpoint;
+ mBulkOutEndpoint = bulkOutEndpoint;
+ }
+
+ @Override
+ protected void ioClose() {
+ mConnection = null;
+ mBulkInEndpoint = null;
+ mBulkOutEndpoint = null;
+ }
+
+ @Override
+ protected int ioRead(byte[] buffer, int offset, int count) throws IOException {
+ if (mConnection == null) {
+ throw new IOException("Connection was closed.");
+ }
+ return mConnection.bulkTransfer(mBulkInEndpoint, buffer, offset, count, -1);
+ }
+
+ @Override
+ protected void ioWrite(byte[] buffer, int offset, int count) throws IOException {
+ if (mConnection == null) {
+ throw new IOException("Connection was closed.");
+ }
+ int result = mConnection.bulkTransfer(mBulkOutEndpoint,
+ buffer, offset, count, TIMEOUT_MILLIS);
+ if (result < 0) {
+ throw new IOException("Bulk transfer failed.");
+ }
+ }
+}
diff --git a/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/UsbAccessoryConstants.java b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/UsbAccessoryConstants.java
new file mode 100644
index 0000000..8197d6b
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/UsbAccessoryConstants.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.sink;
+
+// Constants from kernel include/linux/usb/f_accessory.h
+final class UsbAccessoryConstants {
+ /* Use Google Vendor ID when in accessory mode */
+ public static final int USB_ACCESSORY_VENDOR_ID = 0x18D1;
+
+ /* Product ID to use when in accessory mode */
+ public static final int USB_ACCESSORY_PRODUCT_ID = 0x2D00;
+
+ /* Product ID to use when in accessory mode and adb is enabled */
+ public static final int USB_ACCESSORY_ADB_PRODUCT_ID = 0x2D01;
+
+ /* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */
+ public static final int ACCESSORY_STRING_MANUFACTURER = 0;
+ public static final int ACCESSORY_STRING_MODEL = 1;
+ public static final int ACCESSORY_STRING_DESCRIPTION = 2;
+ public static final int ACCESSORY_STRING_VERSION = 3;
+ public static final int ACCESSORY_STRING_URI = 4;
+ public static final int ACCESSORY_STRING_SERIAL = 5;
+
+ /* Control request for retrieving device's protocol version
+ *
+ * requestType: USB_DIR_IN | USB_TYPE_VENDOR
+ * request: ACCESSORY_GET_PROTOCOL
+ * value: 0
+ * index: 0
+ * data version number (16 bits little endian)
+ * 1 for original accessory support
+ * 2 adds HID and device to host audio support
+ */
+ public static final int ACCESSORY_GET_PROTOCOL = 51;
+
+ /* Control request for host to send a string to the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_STRING
+ * value: 0
+ * index: string ID
+ * data zero terminated UTF8 string
+ *
+ * The device can later retrieve these strings via the
+ * ACCESSORY_GET_STRING_* ioctls
+ */
+ public static final int ACCESSORY_SEND_STRING = 52;
+
+ /* Control request for starting device in accessory mode.
+ * The host sends this after setting all its strings to the device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_START
+ * value: 0
+ * index: 0
+ * data none
+ */
+ public static final int ACCESSORY_START = 53;
+
+ /* Control request for registering a HID device.
+ * Upon registering, a unique ID is sent by the accessory in the
+ * value parameter. This ID will be used for future commands for
+ * the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID_DEVICE
+ * value: Accessory assigned ID for the HID device
+ * index: total length of the HID report descriptor
+ * data none
+ */
+ public static final int ACCESSORY_REGISTER_HID = 54;
+
+ /* Control request for unregistering a HID device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data none
+ */
+ public static final int ACCESSORY_UNREGISTER_HID = 55;
+
+ /* Control request for sending the HID report descriptor.
+ * If the HID descriptor is longer than the endpoint zero max packet size,
+ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
+ * commands. The data for the descriptor must be sent sequentially
+ * if multiple packets are needed.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_HID_REPORT_DESC
+ * value: Accessory assigned ID for the HID device
+ * index: offset of data in descriptor
+ * (needed when HID descriptor is too big for one packet)
+ * data the HID report descriptor
+ */
+ public static final int ACCESSORY_SET_HID_REPORT_DESC = 56;
+
+ /* Control request for sending HID events.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_HID_EVENT
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data the HID report for the event
+ */
+ public static final int ACCESSORY_SEND_HID_EVENT = 57;
+
+ /* Control request for setting the audio mode.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_AUDIO_MODE
+ * value: 0 - no audio
+ * 1 - device to host, 44100 16-bit stereo PCM
+ * index: 0
+ * data none
+ */
+ public static final int ACCESSORY_SET_AUDIO_MODE = 58;
+
+ private UsbAccessoryConstants() {
+ }
+}
diff --git a/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/UsbHid.java b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/UsbHid.java
new file mode 100644
index 0000000..b4fa1fd
--- /dev/null
+++ b/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/UsbHid.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.sink;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Helper for creating USB HID descriptors and reports.
+ */
+final class UsbHid {
+ private UsbHid() {
+ }
+
+ /**
+ * Generates basic Windows 7 compatible HID multitouch descriptors and reports
+ * that should be supported by recent versions of the Linux hid-multitouch driver.
+ */
+ public static final class Multitouch {
+ private final int mReportId;
+ private final int mMaxContacts;
+ private final int mWidth;
+ private final int mHeight;
+
+ public Multitouch(int reportId, int maxContacts, int width, int height) {
+ mReportId = reportId;
+ mMaxContacts = maxContacts;
+ mWidth = width;
+ mHeight = height;
+ }
+
+ public void generateDescriptor(ByteBuffer buffer) {
+ buffer.put(new byte[] {
+ 0x05, 0x0d, // USAGE_PAGE (Digitizers)
+ 0x09, 0x04, // USAGE (Touch Screen)
+ (byte)0xa1, 0x01, // COLLECTION (Application)
+ (byte)0x85, (byte)mReportId, // REPORT_ID (Touch)
+ 0x09, 0x22, // USAGE (Finger)
+ (byte)0xa1, 0x00, // COLLECTION (Physical)
+ 0x09, 0x55, // USAGE (Contact Count Maximum)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x25, (byte)mMaxContacts, // LOGICAL_MAXIMUM (...)
+ 0x75, 0x08, // REPORT_SIZE (8)
+ (byte)0x95, 0x01, // REPORT_COUNT (1)
+ (byte)0xb1, (byte)mMaxContacts, // FEATURE (Data,Var,Abs)
+ 0x09, 0x54, // USAGE (Contact Count)
+ (byte)0x81, 0x02, // INPUT (Data,Var,Abs)
+ });
+ byte maxXLsb = (byte)(mWidth - 1);
+ byte maxXMsb = (byte)((mWidth - 1) >> 8);
+ byte maxYLsb = (byte)(mHeight - 1);
+ byte maxYMsb = (byte)((mHeight - 1) >> 8);
+ byte[] collection = new byte[] {
+ 0x05, 0x0d, // USAGE_PAGE (Digitizers)
+ 0x09, 0x22, // USAGE (Finger)
+ (byte)0xa1, 0x02, // COLLECTION (Logical)
+ 0x09, 0x42, // USAGE (Tip Switch)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x25, 0x01, // LOGICAL_MAXIMUM (1)
+ 0x75, 0x01, // REPORT_SIZE (1)
+ (byte)0x81, 0x02, // INPUT (Data,Var,Abs)
+ 0x09, 0x32, // USAGE (In Range)
+ (byte)0x81, 0x02, // INPUT (Data,Var,Abs)
+ 0x09, 0x51, // USAGE (Contact Identifier)
+ 0x25, 0x3f, // LOGICAL_MAXIMUM (63)
+ 0x75, 0x06, // REPORT_SIZE (6)
+ (byte)0x81, 0x02, // INPUT (Data,Var,Abs)
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x30, // USAGE (X)
+ 0x26, maxXLsb, maxXMsb, // LOGICAL_MAXIMUM (...)
+ 0x75, 0x10, // REPORT_SIZE (16)
+ (byte)0x81, 0x02, // INPUT (Data,Var,Abs)
+ 0x09, 0x31, // USAGE (Y)
+ 0x26, maxYLsb, maxYMsb, // LOGICAL_MAXIMUM (...)
+ (byte)0x81, 0x02, // INPUT (Data,Var,Abs)
+ (byte)0xc0, // END_COLLECTION
+ };
+ for (int i = 0; i < mMaxContacts; i++) {
+ buffer.put(collection);
+ }
+ buffer.put(new byte[] {
+ (byte)0xc0, // END_COLLECTION
+ (byte)0xc0, // END_COLLECTION
+ });
+ }
+
+ public void generateReport(ByteBuffer buffer, Contact[] contacts, int contactCount) {
+ // Report Id
+ buffer.put((byte)mReportId);
+ // Contact Count
+ buffer.put((byte)contactCount);
+
+ for (int i = 0; i < contactCount; i++) {
+ final Contact contact = contacts[i];
+ // Tip Switch, In Range, Contact Identifier
+ buffer.put((byte)((contact.id << 2) | 0x03));
+ // X
+ buffer.put((byte)contact.x).put((byte)(contact.x >> 8));
+ // Y
+ buffer.put((byte)contact.y).put((byte)(contact.y >> 8));
+ }
+ for (int i = contactCount; i < mMaxContacts; i++) {
+ buffer.put((byte)0).put((byte)0).put((byte)0).put((byte)0).put((byte)0);
+ }
+ }
+
+ public int getReportSize() {
+ return 2 + mMaxContacts * 5;
+ }
+
+ public static final class Contact {
+ public int id; // range 0..63
+ public int x;
+ public int y;
+ }
+ }
+}
diff --git a/tests/AccessoryDisplay/source/Android.mk b/tests/AccessoryDisplay/source/Android.mk
new file mode 100644
index 0000000..5d1085d
--- /dev/null
+++ b/tests/AccessoryDisplay/source/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Build the application.
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := AccessoryDisplaySource
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR = $(LOCAL_PATH)/res
+LOCAL_STATIC_JAVA_LIBRARIES := AccessoryDisplayCommon
+include $(BUILD_PACKAGE)
diff --git a/tests/AccessoryDisplay/source/AndroidManifest.xml b/tests/AccessoryDisplay/source/AndroidManifest.xml
new file mode 100644
index 0000000..d3edcb8
--- /dev/null
+++ b/tests/AccessoryDisplay/source/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.accessorydisplay.source" >
+
+ <uses-feature android:name="android.hardware.usb.accessory"/>
+ <uses-sdk android:minSdkVersion="18" />
+
+ <application android:label="@string/app_name"
+ android:icon="@drawable/ic_app"
+ android:hardwareAccelerated="true">
+
+ <activity android:name=".SourceActivity"
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.Holo">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
+ android:resource="@xml/usb_accessory_filter"/>
+ </activity>
+
+ </application>
+</manifest>
diff --git a/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png
new file mode 100755
index 0000000..66a1984
--- /dev/null
+++ b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png
Binary files differ
diff --git a/tests/AccessoryDisplay/source/res/drawable-mdpi/ic_app.png b/tests/AccessoryDisplay/source/res/drawable-mdpi/ic_app.png
new file mode 100644
index 0000000..5ae7701
--- /dev/null
+++ b/tests/AccessoryDisplay/source/res/drawable-mdpi/ic_app.png
Binary files differ
diff --git a/tests/AccessoryDisplay/source/res/layout/presentation_content.xml b/tests/AccessoryDisplay/source/res/layout/presentation_content.xml
new file mode 100644
index 0000000..bf9566a
--- /dev/null
+++ b/tests/AccessoryDisplay/source/res/layout/presentation_content.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <android.opengl.GLSurfaceView android:id="@+id/surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <Button android:id="@+id/explode_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:text="Explode!" />
+</RelativeLayout>
diff --git a/tests/AccessoryDisplay/source/res/layout/source_activity.xml b/tests/AccessoryDisplay/source/res/layout/source_activity.xml
new file mode 100644
index 0000000..ff2b818
--- /dev/null
+++ b/tests/AccessoryDisplay/source/res/layout/source_activity.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView android:id="@+id/logTextView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</LinearLayout>
diff --git a/tests/AccessoryDisplay/source/res/values/strings.xml b/tests/AccessoryDisplay/source/res/values/strings.xml
new file mode 100644
index 0000000..0b488df
--- /dev/null
+++ b/tests/AccessoryDisplay/source/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name">Accessory Display Source</string>
+</resources>
diff --git a/tests/AccessoryDisplay/source/res/xml/usb_accessory_filter.xml b/tests/AccessoryDisplay/source/res/xml/usb_accessory_filter.xml
new file mode 100644
index 0000000..5313b4e
--- /dev/null
+++ b/tests/AccessoryDisplay/source/res/xml/usb_accessory_filter.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <!-- Match all devices -->
+ <usb-accessory manufacturer="Android" model="Accessory Display" />
+</resources>
diff --git a/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/DisplaySourceService.java b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/DisplaySourceService.java
new file mode 100644
index 0000000..ccead44
--- /dev/null
+++ b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/DisplaySourceService.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.source;
+
+import com.android.accessorydisplay.common.Protocol;
+import com.android.accessorydisplay.common.Service;
+import com.android.accessorydisplay.common.Transport;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.os.Handler;
+import android.os.Message;
+import android.view.Display;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+
+public class DisplaySourceService extends Service {
+ private static final int MSG_DISPATCH_DISPLAY_ADDED = 1;
+ private static final int MSG_DISPATCH_DISPLAY_REMOVED = 2;
+
+ private static final String DISPLAY_NAME = "Accessory Display";
+ private static final int BIT_RATE = 6000000;
+ private static final int FRAME_RATE = 30;
+ private static final int I_FRAME_INTERVAL = 10;
+
+ private final Callbacks mCallbacks;
+ private final ServiceHandler mHandler;
+ private final DisplayManager mDisplayManager;
+
+ private boolean mSinkAvailable;
+ private int mSinkWidth;
+ private int mSinkHeight;
+ private int mSinkDensityDpi;
+
+ private VirtualDisplayThread mVirtualDisplayThread;
+
+ public DisplaySourceService(Context context, Transport transport, Callbacks callbacks) {
+ super(context, transport, Protocol.DisplaySourceService.ID);
+ mCallbacks = callbacks;
+ mHandler = new ServiceHandler();
+ mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+ }
+
+ @Override
+ public void start() {
+ super.start();
+
+ getLogger().log("Sending MSG_QUERY.");
+ getTransport().sendMessage(Protocol.DisplaySinkService.ID,
+ Protocol.DisplaySinkService.MSG_QUERY, null);
+ }
+
+ @Override
+ public void stop() {
+ super.stop();
+
+ handleSinkNotAvailable();
+ }
+
+ @Override
+ public void onMessageReceived(int service, int what, ByteBuffer content) {
+ switch (what) {
+ case Protocol.DisplaySourceService.MSG_SINK_AVAILABLE: {
+ getLogger().log("Received MSG_SINK_AVAILABLE");
+ if (content.remaining() >= 12) {
+ final int width = content.getInt();
+ final int height = content.getInt();
+ final int densityDpi = content.getInt();
+ if (width >= 0 && width <= 4096
+ && height >= 0 && height <= 4096
+ && densityDpi >= 60 && densityDpi <= 640) {
+ handleSinkAvailable(width, height, densityDpi);
+ return;
+ }
+ }
+ getLogger().log("Receive invalid MSG_SINK_AVAILABLE message.");
+ break;
+ }
+
+ case Protocol.DisplaySourceService.MSG_SINK_NOT_AVAILABLE: {
+ getLogger().log("Received MSG_SINK_NOT_AVAILABLE");
+ handleSinkNotAvailable();
+ break;
+ }
+ }
+ }
+
+ private void handleSinkAvailable(int width, int height, int densityDpi) {
+ if (mSinkAvailable && mSinkWidth == width && mSinkHeight == height
+ && mSinkDensityDpi == densityDpi) {
+ return;
+ }
+
+ getLogger().log("Accessory display sink available: "
+ + "width=" + width + ", height=" + height
+ + ", densityDpi=" + densityDpi);
+ mSinkAvailable = true;
+ mSinkWidth = width;
+ mSinkHeight = height;
+ mSinkDensityDpi = densityDpi;
+ createVirtualDisplay();
+ }
+
+ private void handleSinkNotAvailable() {
+ getLogger().log("Accessory display sink not available.");
+
+ mSinkAvailable = false;
+ mSinkWidth = 0;
+ mSinkHeight = 0;
+ mSinkDensityDpi = 0;
+ releaseVirtualDisplay();
+ }
+
+ private void createVirtualDisplay() {
+ releaseVirtualDisplay();
+
+ mVirtualDisplayThread = new VirtualDisplayThread(
+ mSinkWidth, mSinkHeight, mSinkDensityDpi);
+ mVirtualDisplayThread.start();
+ }
+
+ private void releaseVirtualDisplay() {
+ if (mVirtualDisplayThread != null) {
+ mVirtualDisplayThread.quit();
+ mVirtualDisplayThread = null;
+ }
+ }
+
+ public interface Callbacks {
+ public void onDisplayAdded(Display display);
+ public void onDisplayRemoved(Display display);
+ }
+
+ private final class ServiceHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DISPATCH_DISPLAY_ADDED: {
+ mCallbacks.onDisplayAdded((Display)msg.obj);
+ break;
+ }
+
+ case MSG_DISPATCH_DISPLAY_REMOVED: {
+ mCallbacks.onDisplayRemoved((Display)msg.obj);
+ break;
+ }
+ }
+ }
+ }
+
+ private final class VirtualDisplayThread extends Thread {
+ private static final int TIMEOUT_USEC = 1000000;
+
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+
+ private volatile boolean mQuitting;
+
+ public VirtualDisplayThread(int width, int height, int densityDpi) {
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ }
+
+ @Override
+ public void run() {
+ MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL);
+
+ MediaCodec codec = MediaCodec.createEncoderByType("video/avc");
+ codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ Surface surface = codec.createInputSurface();
+ codec.start();
+
+ VirtualDisplay virtualDisplay = mDisplayManager.createPrivateVirtualDisplay(
+ DISPLAY_NAME, mWidth, mHeight, mDensityDpi, surface);
+ if (virtualDisplay != null) {
+ mHandler.obtainMessage(MSG_DISPATCH_DISPLAY_ADDED,
+ virtualDisplay.getDisplay()).sendToTarget();
+
+ stream(codec);
+
+ mHandler.obtainMessage(MSG_DISPATCH_DISPLAY_REMOVED,
+ virtualDisplay.getDisplay()).sendToTarget();
+ virtualDisplay.release();
+ }
+
+ codec.signalEndOfInputStream();
+ codec.stop();
+ }
+
+ public void quit() {
+ mQuitting = true;
+ }
+
+ private void stream(MediaCodec codec) {
+ BufferInfo info = new BufferInfo();
+ ByteBuffer[] buffers = null;
+ while (!mQuitting) {
+ int index = codec.dequeueOutputBuffer(info, TIMEOUT_USEC);
+ if (index >= 0) {
+ if (buffers == null) {
+ buffers = codec.getOutputBuffers();
+ }
+
+ ByteBuffer buffer = buffers[index];
+ buffer.limit(info.offset + info.size);
+ buffer.position(info.offset);
+
+ getTransport().sendMessage(Protocol.DisplaySinkService.ID,
+ Protocol.DisplaySinkService.MSG_CONTENT, buffer);
+ codec.releaseOutputBuffer(index, false);
+ } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ buffers = null;
+ } else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ getLogger().log("Codec dequeue buffer timed out.");
+ }
+ }
+ }
+ }
+}
diff --git a/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/SourceActivity.java b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/SourceActivity.java
new file mode 100644
index 0000000..c59c958
--- /dev/null
+++ b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/SourceActivity.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.source;
+
+import com.android.accessorydisplay.common.Logger;
+import com.android.accessorydisplay.source.presentation.DemoPresentation;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.Display;
+import android.widget.TextView;
+
+public class SourceActivity extends Activity {
+ private static final String TAG = "SourceActivity";
+
+ private static final String ACTION_USB_ACCESSORY_PERMISSION =
+ "com.android.accessorydisplay.source.ACTION_USB_ACCESSORY_PERMISSION";
+
+ private static final String MANUFACTURER = "Android";
+ private static final String MODEL = "Accessory Display";
+
+ private UsbManager mUsbManager;
+ private AccessoryReceiver mReceiver;
+ private TextView mLogTextView;
+ private Logger mLogger;
+ private Presenter mPresenter;
+
+ private boolean mConnected;
+ private UsbAccessory mAccessory;
+ private UsbAccessoryStreamTransport mTransport;
+
+ private DisplaySourceService mDisplaySourceService;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+
+ setContentView(R.layout.source_activity);
+
+ mLogTextView = (TextView) findViewById(R.id.logTextView);
+ mLogTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
+ mLogger = new TextLogger();
+ mPresenter = new Presenter();
+
+ mLogger.log("Waiting for accessory display sink to be attached to USB...");
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
+ filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
+ filter.addAction(ACTION_USB_ACCESSORY_PERMISSION);
+ mReceiver = new AccessoryReceiver();
+ registerReceiver(mReceiver, filter);
+
+ Intent intent = getIntent();
+ if (intent.getAction().equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+ UsbAccessory accessory =
+ (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+ if (accessory != null) {
+ onAccessoryAttached(accessory);
+ }
+ } else {
+ UsbAccessory[] accessories = mUsbManager.getAccessoryList();
+ if (accessories != null) {
+ for (UsbAccessory accessory : accessories) {
+ onAccessoryAttached(accessory);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ //new DemoPresentation(this, getWindowManager().getDefaultDisplay()).show();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ private void onAccessoryAttached(UsbAccessory accessory) {
+ mLogger.log("USB accessory attached: " + accessory);
+ if (!mConnected) {
+ connect(accessory);
+ }
+ }
+
+ private void onAccessoryDetached(UsbAccessory accessory) {
+ mLogger.log("USB accessory detached: " + accessory);
+ if (mConnected && accessory.equals(mAccessory)) {
+ disconnect();
+ }
+ }
+
+ private void connect(UsbAccessory accessory) {
+ if (!isSink(accessory)) {
+ mLogger.log("Not connecting to USB accessory because it is not an accessory display sink: "
+ + accessory);
+ return;
+ }
+
+ if (mConnected) {
+ disconnect();
+ }
+
+ // Check whether we have permission to access the accessory.
+ if (!mUsbManager.hasPermission(accessory)) {
+ mLogger.log("Prompting the user for access to the accessory.");
+ Intent intent = new Intent(ACTION_USB_ACCESSORY_PERMISSION);
+ intent.setPackage(getPackageName());
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ mUsbManager.requestPermission(accessory, pendingIntent);
+ return;
+ }
+
+ // Open the accessory.
+ ParcelFileDescriptor fd = mUsbManager.openAccessory(accessory);
+ if (fd == null) {
+ mLogger.logError("Could not obtain accessory connection.");
+ return;
+ }
+
+ // All set.
+ mLogger.log("Connected.");
+ mConnected = true;
+ mAccessory = accessory;
+ mTransport = new UsbAccessoryStreamTransport(mLogger, fd);
+ startServices();
+ mTransport.startReading();
+ }
+
+ private void disconnect() {
+ mLogger.log("Disconnecting from accessory: " + mAccessory);
+ stopServices();
+
+ mLogger.log("Disconnected.");
+ mConnected = false;
+ mAccessory = null;
+ if (mTransport != null) {
+ mTransport.close();
+ mTransport = null;
+ }
+ }
+
+ private void startServices() {
+ mDisplaySourceService = new DisplaySourceService(this, mTransport, mPresenter);
+ mDisplaySourceService.start();
+ }
+
+ private void stopServices() {
+ if (mDisplaySourceService != null) {
+ mDisplaySourceService.stop();
+ mDisplaySourceService = null;
+ }
+ }
+
+ private static boolean isSink(UsbAccessory accessory) {
+ return MANUFACTURER.equals(accessory.getManufacturer())
+ && MODEL.equals(accessory.getModel());
+ }
+
+ class TextLogger extends Logger {
+ @Override
+ public void log(final String message) {
+ Log.d(TAG, message);
+
+ mLogTextView.post(new Runnable() {
+ @Override
+ public void run() {
+ mLogTextView.append(message);
+ mLogTextView.append("\n");
+ }
+ });
+ }
+ }
+
+ class AccessoryReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ UsbAccessory accessory = intent.<UsbAccessory>getParcelableExtra(
+ UsbManager.EXTRA_ACCESSORY);
+ if (accessory != null) {
+ String action = intent.getAction();
+ if (action.equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+ onAccessoryAttached(accessory);
+ } else if (action.equals(UsbManager.ACTION_USB_ACCESSORY_DETACHED)) {
+ onAccessoryDetached(accessory);
+ } else if (action.equals(ACTION_USB_ACCESSORY_PERMISSION)) {
+ if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
+ mLogger.log("Accessory permission granted: " + accessory);
+ onAccessoryAttached(accessory);
+ } else {
+ mLogger.logError("Accessory permission denied: " + accessory);
+ }
+ }
+ }
+ }
+ }
+
+ class Presenter implements DisplaySourceService.Callbacks {
+ private DemoPresentation mPresentation;
+
+ @Override
+ public void onDisplayAdded(Display display) {
+ mLogger.log("Accessory display added: " + display);
+
+ mPresentation = new DemoPresentation(SourceActivity.this, display, mLogger);
+ mPresentation.show();
+ }
+
+ @Override
+ public void onDisplayRemoved(Display display) {
+ mLogger.log("Accessory display removed: " + display);
+
+ if (mPresentation != null) {
+ mPresentation.dismiss();
+ mPresentation = null;
+ }
+ }
+ }
+}
diff --git a/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/UsbAccessoryStreamTransport.java b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/UsbAccessoryStreamTransport.java
new file mode 100644
index 0000000..c28f4359
--- /dev/null
+++ b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/UsbAccessoryStreamTransport.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.source;
+
+import com.android.accessorydisplay.common.Logger;
+import com.android.accessorydisplay.common.Transport;
+
+import android.hardware.usb.UsbAccessory;
+import android.os.ParcelFileDescriptor;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Sends or receives messages over a file descriptor associated with a {@link UsbAccessory}.
+ */
+public class UsbAccessoryStreamTransport extends Transport {
+ private ParcelFileDescriptor mFd;
+ private FileInputStream mInputStream;
+ private FileOutputStream mOutputStream;
+
+ public UsbAccessoryStreamTransport(Logger logger, ParcelFileDescriptor fd) {
+ super(logger, 16384);
+ mFd = fd;
+ mInputStream = new FileInputStream(fd.getFileDescriptor());
+ mOutputStream = new FileOutputStream(fd.getFileDescriptor());
+ }
+
+ @Override
+ protected void ioClose() {
+ try {
+ mFd.close();
+ } catch (IOException ex) {
+ }
+ mFd = null;
+ mInputStream = null;
+ mOutputStream = null;
+ }
+
+ @Override
+ protected int ioRead(byte[] buffer, int offset, int count) throws IOException {
+ if (mInputStream == null) {
+ throw new IOException("Stream was closed.");
+ }
+ return mInputStream.read(buffer, offset, count);
+ }
+
+ @Override
+ protected void ioWrite(byte[] buffer, int offset, int count) throws IOException {
+ if (mOutputStream == null) {
+ throw new IOException("Stream was closed.");
+ }
+ mOutputStream.write(buffer, offset, count);
+ }
+}
diff --git a/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/presentation/Cube.java b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/presentation/Cube.java
new file mode 100644
index 0000000..51d8da9
--- /dev/null
+++ b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/presentation/Cube.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.source.presentation;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * A vertex shaded cube.
+ */
+class Cube
+{
+ public Cube()
+ {
+ int one = 0x10000;
+ int vertices[] = {
+ -one, -one, -one,
+ one, -one, -one,
+ one, one, -one,
+ -one, one, -one,
+ -one, -one, one,
+ one, -one, one,
+ one, one, one,
+ -one, one, one,
+ };
+
+ int colors[] = {
+ 0, 0, 0, one,
+ one, 0, 0, one,
+ one, one, 0, one,
+ 0, one, 0, one,
+ 0, 0, one, one,
+ one, 0, one, one,
+ one, one, one, one,
+ 0, one, one, one,
+ };
+
+ byte indices[] = {
+ 0, 4, 5, 0, 5, 1,
+ 1, 5, 6, 1, 6, 2,
+ 2, 6, 7, 2, 7, 3,
+ 3, 7, 4, 3, 4, 0,
+ 4, 7, 6, 4, 6, 5,
+ 3, 0, 1, 3, 1, 2
+ };
+
+ // Buffers to be passed to gl*Pointer() functions
+ // must be direct, i.e., they must be placed on the
+ // native heap where the garbage collector cannot
+ // move them.
+ //
+ // Buffers with multi-byte datatypes (e.g., short, int, float)
+ // must have their byte order set to native order
+
+ ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
+ vbb.order(ByteOrder.nativeOrder());
+ mVertexBuffer = vbb.asIntBuffer();
+ mVertexBuffer.put(vertices);
+ mVertexBuffer.position(0);
+
+ ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
+ cbb.order(ByteOrder.nativeOrder());
+ mColorBuffer = cbb.asIntBuffer();
+ mColorBuffer.put(colors);
+ mColorBuffer.position(0);
+
+ mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
+ mIndexBuffer.put(indices);
+ mIndexBuffer.position(0);
+ }
+
+ public void draw(GL10 gl)
+ {
+ gl.glFrontFace(GL10.GL_CW);
+ gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);
+ gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer);
+ gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
+ }
+
+ private IntBuffer mVertexBuffer;
+ private IntBuffer mColorBuffer;
+ private ByteBuffer mIndexBuffer;
+}
diff --git a/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/presentation/CubeRenderer.java b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/presentation/CubeRenderer.java
new file mode 100644
index 0000000..51dc82a
--- /dev/null
+++ b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/presentation/CubeRenderer.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.source.presentation;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLSurfaceView;
+
+/**
+ * Render a pair of tumbling cubes.
+ */
+
+public class CubeRenderer implements GLSurfaceView.Renderer {
+ private boolean mTranslucentBackground;
+ private Cube mCube;
+ private float mAngle;
+ private float mScale = 1.0f;
+ private boolean mExploding;
+
+ public CubeRenderer(boolean useTranslucentBackground) {
+ mTranslucentBackground = useTranslucentBackground;
+ mCube = new Cube();
+ }
+
+ public void explode() {
+ mExploding = true;
+ }
+
+ public void onDrawFrame(GL10 gl) {
+ /*
+ * Usually, the first thing one might want to do is to clear
+ * the screen. The most efficient way of doing this is to use
+ * glClear().
+ */
+
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+ /*
+ * Now we're ready to draw some 3D objects
+ */
+
+ gl.glMatrixMode(GL10.GL_MODELVIEW);
+ gl.glLoadIdentity();
+ gl.glTranslatef(0, 0, -3.0f);
+ gl.glRotatef(mAngle, 0, 1, 0);
+ gl.glRotatef(mAngle*0.25f, 1, 0, 0);
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+ gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
+
+ gl.glScalef(mScale, mScale, mScale);
+ mCube.draw(gl);
+
+ gl.glRotatef(mAngle*2.0f, 0, 1, 1);
+ gl.glTranslatef(0.5f, 0.5f, 0.5f);
+
+ mCube.draw(gl);
+
+ mAngle += 1.2f;
+
+ if (mExploding) {
+ mScale *= 1.02f;
+ if (mScale > 4.0f) {
+ mScale = 1.0f;
+ mExploding = false;
+ }
+ }
+ }
+
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ gl.glViewport(0, 0, width, height);
+
+ /*
+ * Set our projection matrix. This doesn't have to be done
+ * each time we draw, but usually a new projection needs to
+ * be set when the viewport is resized.
+ */
+
+ float ratio = (float) width / height;
+ gl.glMatrixMode(GL10.GL_PROJECTION);
+ gl.glLoadIdentity();
+ gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+ }
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ gl.glDisable(GL10.GL_DITHER);
+
+ /*
+ * Some one-time OpenGL initialization can be made here
+ * probably based on features of this particular context
+ */
+ gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
+ GL10.GL_FASTEST);
+
+ if (mTranslucentBackground) {
+ gl.glClearColor(0,0,0,0);
+ } else {
+ gl.glClearColor(1,1,1,1);
+ }
+ gl.glEnable(GL10.GL_CULL_FACE);
+ gl.glShadeModel(GL10.GL_SMOOTH);
+ gl.glEnable(GL10.GL_DEPTH_TEST);
+ }
+}
diff --git a/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/presentation/DemoPresentation.java b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/presentation/DemoPresentation.java
new file mode 100644
index 0000000..517b7fc
--- /dev/null
+++ b/tests/AccessoryDisplay/source/src/com/android/accessorydisplay/source/presentation/DemoPresentation.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.accessorydisplay.source.presentation;
+
+import com.android.accessorydisplay.common.Logger;
+import com.android.accessorydisplay.source.R;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.content.res.Resources;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.Button;
+
+/**
+ * The presentation to show on the accessory display.
+ * <p>
+ * Note that this display may have different metrics from the display on which
+ * the main activity is showing so we must be careful to use the presentation's
+ * own {@link Context} whenever we load resources.
+ * </p>
+ */
+public final class DemoPresentation extends Presentation {
+ private final Logger mLogger;
+
+ private GLSurfaceView mSurfaceView;
+ private CubeRenderer mRenderer;
+ private Button mExplodeButton;
+
+ public DemoPresentation(Context context, Display display, Logger logger) {
+ super(context, display);
+ mLogger = logger;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // Get the resources for the context of the presentation.
+ // Notice that we are getting the resources from the context of the presentation.
+ Resources r = getContext().getResources();
+
+ // Inflate the layout.
+ setContentView(R.layout.presentation_content);
+
+ // Set up the surface view for visual interest.
+ mRenderer = new CubeRenderer(false);
+ mSurfaceView = (GLSurfaceView)findViewById(R.id.surface_view);
+ mSurfaceView.setRenderer(mRenderer);
+
+ // Add a button.
+ mExplodeButton = (Button)findViewById(R.id.explode_button);
+ mExplodeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mRenderer.explode();
+ }
+ });
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ mLogger.log("Received touch event: " + event);
+ return super.onTouchEvent(event);
+ }
+}
\ No newline at end of file
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index c7cd975..d7a6c1d 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -182,7 +182,7 @@
menu.add("Send!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
Intent intent = new Intent(ActivityTestMain.this, SingleUserReceiver.class);
- sendOrderedBroadcast(intent, null, new BroadcastResultReceiver(),
+ sendOrderedBroadcast(intent, null, new BroadcastResultReceiver(),
null, Activity.RESULT_OK, null, null);
return true;
}
@@ -285,6 +285,12 @@
return true;
}
});
+ menu.add("HashArray").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override public boolean onMenuItemClick(MenuItem item) {
+ ArrayMapTests.run();
+ return true;
+ }
+ });
return true;
}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ArrayMapTests.java b/tests/ActivityTests/src/com/google/android/test/activity/ArrayMapTests.java
new file mode 100644
index 0000000..9b54927
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ArrayMapTests.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activity;
+
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class ArrayMapTests {
+ static final int OP_ADD = 1;
+ static final int OP_REM = 2;
+
+ static int[] OPS = new int[] {
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+ };
+
+ static int[] KEYS = new int[] {
+ // General adding and removing.
+ 100, 1900, 600, 200, 1200, 1500, 1800, 100, 1900,
+ 2100, 300, 800, 600, 1100, 1300, 2000, 1000, 1400,
+ 600, 100, 1900, 600, 300, 2100, 200, 800, 800,
+ 1800, 1500, 1300, 1100, 2000, 1400, 1000, 1200, 1900,
+
+ // Shrink when removing item from end.
+ 100, 200, 300, 400, 500, 600, 700, 800, 900,
+ 900, 800, 700, 600, 500, 400, 300, 200, 100,
+
+ // Shrink when removing item from middle.
+ 100, 200, 300, 400, 500, 600, 700, 800, 900,
+ 900, 800, 700, 600, 500, 400, 200, 300, 100,
+
+ // Shrink when removing item from front.
+ 100, 200, 300, 400, 500, 600, 700, 800, 900,
+ 900, 800, 700, 600, 500, 400, 100, 200, 300,
+
+ // Test hash collisions.
+ 105, 106, 108, 104, 102, 102, 107, 5, 205,
+ 4, 202, 203, 3, 5, 101, 109, 200, 201,
+ 106, 108, 104, 102, 103, 105, 107, 101, 109,
+ 4, 5, 3, 5, 200, 203, 202, 201, 205,
+ };
+
+ static class ControlledHash {
+ final int mValue;
+
+ ControlledHash(int value) {
+ mValue = value;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ return mValue == ((ControlledHash)o).mValue;
+ }
+
+ @Override
+ public final int hashCode() {
+ return mValue/100;
+ }
+
+ @Override
+ public final String toString() {
+ return Integer.toString(mValue);
+ }
+ }
+
+ private static boolean compare(Object v1, Object v2) {
+ if (v1 == null) {
+ return v2 == null;
+ }
+ if (v2 == null) {
+ return false;
+ }
+ return v1.equals(v2);
+ }
+
+ private static boolean compareMaps(HashMap map, ArrayMap array) {
+ if (map.size() != array.size()) {
+ Log.e("test", "Bad size: expected " + map.size() + ", got " + array.size());
+ return false;
+ }
+
+ Set<Map.Entry> mapSet = map.entrySet();
+ for (Map.Entry entry : mapSet) {
+ Object expValue = entry.getValue();
+ Object gotValue = array.get(entry.getKey());
+ if (!compare(expValue, gotValue)) {
+ Log.e("test", "Bad value: expected " + expValue + ", got " + gotValue
+ + " at key " + entry.getKey());
+ return false;
+ }
+ }
+
+ for (int i=0; i<array.size(); i++) {
+ Object gotValue = array.valueAt(i);
+ Object key = array.keyAt(i);
+ Object expValue = map.get(key);
+ if (!compare(expValue, gotValue)) {
+ Log.e("test", "Bad value: expected " + expValue + ", got " + gotValue
+ + " at key " + key);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean compareSets(HashSet set, ArraySet array) {
+ if (set.size() != array.size()) {
+ Log.e("test", "Bad size: expected " + set.size() + ", got " + array.size());
+ return false;
+ }
+
+ for (Object entry : set) {
+ if (!array.contains(entry)) {
+ Log.e("test", "Bad value: expected " + entry + " not found in ArraySet");
+ return false;
+ }
+ }
+
+ for (int i=0; i<array.size(); i++) {
+ Object entry = array.valueAt(i);
+ if (!set.contains(entry)) {
+ Log.e("test", "Bad value: unexpected " + entry + " in ArraySet");
+ return false;
+ }
+ }
+
+ int index = 0;
+ for (Object entry : array) {
+ Object realEntry = array.valueAt(index);
+ if (!compare(entry, realEntry)) {
+ Log.e("test", "Bad iterator: expected value " + realEntry + ", got " + entry
+ + " at index " + index);
+ return false;
+ }
+ index++;
+ }
+
+ return true;
+ }
+
+ private static boolean validateArrayMap(ArrayMap array) {
+ Set<Map.Entry> entrySet = array.entrySet();
+ int index=0;
+ Iterator<Map.Entry> entryIt = entrySet.iterator();
+ while (entryIt.hasNext()) {
+ Map.Entry entry = entryIt.next();
+ Object value = entry.getKey();
+ Object realValue = array.keyAt(index);
+ if (!compare(realValue, value)) {
+ Log.e("test", "Bad array map entry set: expected key " + realValue
+ + ", got " + value + " at index " + index);
+ return false;
+ }
+ value = entry.getValue();
+ realValue = array.valueAt(index);
+ if (!compare(realValue, value)) {
+ Log.e("test", "Bad array map entry set: expected value " + realValue
+ + ", got " + value + " at index " + index);
+ return false;
+ }
+ index++;
+ }
+
+ index = 0;
+ Set keySet = array.keySet();
+ Iterator keyIt = keySet.iterator();
+ while (keyIt.hasNext()) {
+ Object value = keyIt.next();
+ Object realValue = array.keyAt(index);
+ if (!compare(realValue, value)) {
+ Log.e("test", "Bad array map key set: expected key " + realValue
+ + ", got " + value + " at index " + index);
+ return false;
+ }
+ index++;
+ }
+
+ index = 0;
+ Collection valueCol = array.values();
+ Iterator valueIt = valueCol.iterator();
+ while (valueIt.hasNext()) {
+ Object value = valueIt.next();
+ Object realValue = array.valueAt(index);
+ if (!compare(realValue, value)) {
+ Log.e("test", "Bad array map value col: expected value " + realValue
+ + ", got " + value + " at index " + index);
+ return false;
+ }
+ index++;
+ }
+
+ return true;
+ }
+
+ private static void dump(Map map, ArrayMap array) {
+ Log.e("test", "HashMap of " + map.size() + " entries:");
+ Set<Map.Entry> mapSet = map.entrySet();
+ for (Map.Entry entry : mapSet) {
+ Log.e("test", " " + entry.getKey() + " -> " + entry.getValue());
+ }
+ Log.e("test", "ArrayMap of " + array.size() + " entries:");
+ for (int i=0; i<array.size(); i++) {
+ Log.e("test", " " + array.keyAt(i) + " -> " + array.valueAt(i));
+ }
+ }
+
+ private static void dump(Set set, ArraySet array) {
+ Log.e("test", "HashSet of " + set.size() + " entries:");
+ for (Object entry : set) {
+ Log.e("test", " " + entry);
+ }
+ Log.e("test", "ArraySet of " + array.size() + " entries:");
+ for (int i=0; i<array.size(); i++) {
+ Log.e("test", " " + array.valueAt(i));
+ }
+ }
+
+ private static void dump(ArrayMap map1, ArrayMap map2) {
+ Log.e("test", "ArrayMap of " + map1.size() + " entries:");
+ Set<Map.Entry> mapSet = map1.entrySet();
+ for (int i=0; i<map2.size(); i++) {
+ Log.e("test", " " + map1.keyAt(i) + " -> " + map1.valueAt(i));
+ }
+ Log.e("test", "ArrayMap of " + map2.size() + " entries:");
+ for (int i=0; i<map2.size(); i++) {
+ Log.e("test", " " + map2.keyAt(i) + " -> " + map2.valueAt(i));
+ }
+ }
+
+ public static void run() {
+ HashMap<ControlledHash, Integer> hashMap = new HashMap<ControlledHash, Integer>();
+ ArrayMap<ControlledHash, Integer> arrayMap = new ArrayMap<ControlledHash, Integer>();
+ HashSet<ControlledHash> hashSet = new HashSet<ControlledHash>();
+ ArraySet<ControlledHash> arraySet = new ArraySet<ControlledHash>();
+
+ for (int i=0; i<OPS.length; i++) {
+ Integer oldHash;
+ Integer oldArray;
+ boolean hashChanged;
+ boolean arrayChanged;
+ switch (OPS[i]) {
+ case OP_ADD:
+ Log.i("test", "Adding key: " + KEYS[i]);
+ oldHash = hashMap.put(new ControlledHash(KEYS[i]), i);
+ oldArray = arrayMap.put(new ControlledHash(KEYS[i]), i);
+ hashChanged = hashSet.add(new ControlledHash(KEYS[i]));
+ arrayChanged = arraySet.add(new ControlledHash(KEYS[i]));
+ break;
+ case OP_REM:
+ Log.i("test", "Removing key: " + KEYS[i]);
+ oldHash = hashMap.remove(new ControlledHash(KEYS[i]));
+ oldArray = arrayMap.remove(new ControlledHash(KEYS[i]));
+ hashChanged = hashSet.remove(new ControlledHash(KEYS[i]));
+ arrayChanged = arraySet.remove(new ControlledHash(KEYS[i]));
+ break;
+ default:
+ Log.e("test", "Bad operation " + OPS[i] + " @ " + i);
+ return;
+ }
+ if (!compare(oldHash, oldArray)) {
+ Log.e("test", "Bad result: expected " + oldHash + ", got " + oldArray);
+ dump(hashMap, arrayMap);
+ return;
+ }
+ if (hashChanged != arrayChanged) {
+ Log.e("test", "Bad change: expected " + hashChanged + ", got " + arrayChanged);
+ dump(hashSet, arraySet);
+ return;
+ }
+ if (!validateArrayMap(arrayMap)) {
+ dump(hashMap, arrayMap);
+ return;
+ }
+ if (!compareMaps(hashMap, arrayMap)) {
+ dump(hashMap, arrayMap);
+ return;
+ }
+ if (!compareSets(hashSet, arraySet)) {
+ dump(hashSet, arraySet);
+ return;
+ }
+ }
+
+ arrayMap.put(new ControlledHash(50000), 100);
+ ControlledHash lookup = new ControlledHash(50000);
+ Iterator<ControlledHash> it = arrayMap.keySet().iterator();
+ while (it.hasNext()) {
+ if (it.next().equals(lookup)) {
+ it.remove();
+ }
+ }
+ if (arrayMap.containsKey(lookup)) {
+ Log.e("test", "Bad map iterator: didn't remove test key");
+ dump(hashMap, arrayMap);
+ }
+
+ arraySet.add(new ControlledHash(50000));
+ it = arraySet.iterator();
+ while (it.hasNext()) {
+ if (it.next().equals(lookup)) {
+ it.remove();
+ }
+ }
+ if (arraySet.contains(lookup)) {
+ Log.e("test", "Bad set iterator: didn't remove test key");
+ dump(hashSet, arraySet);
+ }
+
+ if (!equalsMapTest()) {
+ return;
+ }
+
+ if (!equalsSetTest()) {
+ return;
+ }
+
+ // map copy constructor test
+ ArrayMap newMap = new ArrayMap<Integer, String>();
+ for (int i = 0; i < 10; ++i) {
+ newMap.put(i, String.valueOf(i));
+ }
+ ArrayMap mapCopy = new ArrayMap(newMap);
+ if (!compare(mapCopy, newMap)) {
+ Log.e("test", "ArrayMap copy constructor failure: expected " +
+ newMap + ", got " + mapCopy);
+ dump(newMap, mapCopy);
+ return;
+ }
+
+ // set copy constructor test
+ ArraySet newSet = new ArraySet<Integer>();
+ for (int i = 0; i < 10; ++i) {
+ newSet.add(i);
+ }
+ ArraySet setCopy = new ArraySet(newSet);
+ if (!compare(setCopy, newSet)) {
+ Log.e("test", "ArraySet copy constructor failure: expected " +
+ newSet + ", got " + setCopy);
+ dump(newSet, setCopy);
+ return;
+ }
+
+ Log.e("test", "Test successful; printing final map.");
+ dump(hashMap, arrayMap);
+
+ Log.e("test", "Test successful; printing final set.");
+ dump(hashSet, arraySet);
+ }
+
+ private static boolean equalsMapTest() {
+ ArrayMap<Integer, String> map1 = new ArrayMap<Integer, String>();
+ ArrayMap<Integer, String> map2 = new ArrayMap<Integer, String>();
+ HashMap<Integer, String> map3 = new HashMap<Integer, String>();
+ if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
+ Log.e("test", "ArrayMap equals failure for empty maps " + map1 + ", " +
+ map2 + ", " + map3);
+ return false;
+ }
+
+ for (int i = 0; i < 10; ++i) {
+ String value = String.valueOf(i);
+ map1.put(i, value);
+ map2.put(i, value);
+ map3.put(i, value);
+ }
+ if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
+ Log.e("test", "ArrayMap equals failure for populated maps " + map1 + ", " +
+ map2 + ", " + map3);
+ return false;
+ }
+
+ map1.remove(0);
+ if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
+ Log.e("test", "ArrayMap equals failure for map size " + map1 + ", " +
+ map2 + ", " + map3);
+ return false;
+ }
+
+ map1.put(0, "-1");
+ if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
+ Log.e("test", "ArrayMap equals failure for map contents " + map1 + ", " +
+ map2 + ", " + map3);
+ return false;
+ }
+
+ return true;
+ }
+
+ private static boolean equalsSetTest() {
+ ArraySet<Integer> set1 = new ArraySet<Integer>();
+ ArraySet<Integer> set2 = new ArraySet<Integer>();
+ HashSet<Integer> set3 = new HashSet<Integer>();
+ if (!compare(set1, set2) || !compare(set1, set3) || !compare(set3, set2)) {
+ Log.e("test", "ArraySet equals failure for empty sets " + set1 + ", " +
+ set2 + ", " + set3);
+ return false;
+ }
+
+ for (int i = 0; i < 10; ++i) {
+ set1.add(i);
+ set2.add(i);
+ set3.add(i);
+ }
+ if (!compare(set1, set2) || !compare(set1, set3) || !compare(set3, set2)) {
+ Log.e("test", "ArraySet equals failure for populated sets " + set1 + ", " +
+ set2 + ", " + set3);
+ return false;
+ }
+
+ set1.remove(0);
+ if (compare(set1, set2) || compare(set1, set3) || compare(set3, set1)) {
+ Log.e("test", "ArraSet equals failure for set size " + set1 + ", " +
+ set2 + ", " + set3);
+ return false;
+ }
+
+ set1.add(-1);
+ if (compare(set1, set2) || compare(set1, set3) || compare(set3, set1)) {
+ Log.e("test", "ArraySet equals failure for set contents " + set1 + ", " +
+ set2 + ", " + set3);
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
index 9939c08..6ad01a0 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
@@ -224,7 +224,7 @@
put("rotate45", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.rotate(5);
+ canvas.rotate(45);
}
});
put("rotate90", new DisplayModifier() {
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
index 400dff0..405ff65 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
@@ -82,6 +82,7 @@
mCompareImageView.setImageBitmap(mCompareBitmap);
break;
}
+ mCompareImageView.getDrawable().setFilterBitmap(false);
mCompareImageView.invalidate();
}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs
index 668f61d..3681784 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs
@@ -49,8 +49,8 @@
float4 diff = idealPixel - givenPixel;
float totalDiff = diff.x + diff.y + diff.z + diff.w;
if (totalDiff < 0) {
- v_out[0] = rsPackColorTo8888(0, 0, clamp(-totalDiff/2, 0, 1));
+ v_out[0] = rsPackColorTo8888(0, 0, clamp(-totalDiff/2.f, 0.f, 1.f));
} else {
- v_out[0] = rsPackColorTo8888(clamp(totalDiff/2, 0, 1), 0, 0);
+ v_out[0] = rsPackColorTo8888(clamp(totalDiff/2.f, 0.f, 1.f), 0, 0);
}
}
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java b/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java
index 5f4f006..309ce34 100644
--- a/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java
@@ -21,7 +21,11 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.lang.String;
+import java.util.HashMap;
+import java.util.Random;
+import android.util.ArrayMap;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -142,6 +146,18 @@
new LoadRecycleLargeBitmapOp(),
new LoadSmallScaledBitmapOp(),
new LoadLargeScaledBitmapOp(),
+ new GrowTinyHashMapOp(),
+ new GrowTinyArrayMapOp(),
+ new GrowSmallHashMapOp(),
+ new GrowSmallArrayMapOp(),
+ new GrowLargeHashMapOp(),
+ new GrowLargeArrayMapOp(),
+ new LookupTinyHashMapOp(),
+ new LookupTinyArrayMapOp(),
+ new LookupSmallHashMapOp(),
+ new LookupSmallArrayMapOp(),
+ new LookupLargeHashMapOp(),
+ new LookupLargeArrayMapOp(),
};
static final int CMD_START_TEST = 1;
@@ -1111,4 +1127,196 @@
mFile.delete();
}
}
+
+ static abstract class GenericMapOp extends Op {
+ final int mSize;
+ String[] mKeys;
+ String[] mValues;
+
+ GenericMapOp(String name, String longName, int size) {
+ super(name, longName);
+ mSize = size;
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mKeys = new String[mSize];
+ mValues = new String[mSize];
+ Random random = new Random(0);
+ for (int i=0; i<mSize; i++) {
+ int chars = random.nextInt(10);
+ StringBuilder builder = new StringBuilder(chars);
+ for (int j=0; j<chars; j++) {
+ builder.append('a' + random.nextInt(100));
+ }
+ mKeys[i] = builder.toString();
+ mValues[i] = Integer.toString(i);
+ }
+ }
+
+ int getOpsPerRun() {
+ return mSize;
+ }
+ }
+
+ static class GrowTinyHashMapOp extends GenericMapOp {
+ GrowTinyHashMapOp() {
+ super("GrowTinyHashMap", "Add 5 items to a HashMap", 5);
+ }
+
+ boolean onRun() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ for (int i=0; i<mSize; i++) {
+ map.put(mKeys[i], mValues[i]);
+ }
+ return true;
+ }
+ }
+
+ static class GrowTinyArrayMapOp extends GenericMapOp {
+ GrowTinyArrayMapOp() {
+ super("GrowTinyArrayMap", "Add 5 items to a ArrayMap", 5);
+ }
+
+ boolean onRun() {
+ ArrayMap<String, String> map = new ArrayMap<String, String>();
+ for (int i=0; i<mSize; i++) {
+ map.put(mKeys[i], mValues[i]);
+ }
+ return true;
+ }
+ }
+
+ static class GrowSmallHashMapOp extends GenericMapOp {
+ GrowSmallHashMapOp() {
+ super("GrowSmallHashMap", "Add 100 items to a HashMap", 100);
+ }
+
+ boolean onRun() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ for (int i=0; i<mSize; i++) {
+ map.put(mKeys[i], mValues[i]);
+ }
+ return true;
+ }
+ }
+
+ static class GrowSmallArrayMapOp extends GenericMapOp {
+ GrowSmallArrayMapOp() {
+ super("GrowSmallArrayMap", "Add 100 items to a ArrayMap", 100);
+ }
+
+ boolean onRun() {
+ ArrayMap<String, String> map = new ArrayMap<String, String>();
+ for (int i=0; i<mSize; i++) {
+ map.put(mKeys[i], mValues[i]);
+ }
+ return true;
+ }
+ }
+
+ static class GrowLargeHashMapOp extends GenericMapOp {
+ GrowLargeHashMapOp() {
+ super("GrowLargeHashMap", "Add 10000 items to a HashMap", 10000);
+ }
+
+ boolean onRun() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ for (int i=0; i<mSize; i++) {
+ map.put(mKeys[i], mValues[i]);
+ }
+ return true;
+ }
+ }
+
+ static class GrowLargeArrayMapOp extends GenericMapOp {
+ GrowLargeArrayMapOp() {
+ super("GrowLargeArrayMap", "Add 10000 items to a ArrayMap", 10000);
+ }
+
+ boolean onRun() {
+ ArrayMap<String, String> map = new ArrayMap<String, String>();
+ for (int i=0; i<mSize; i++) {
+ map.put(mKeys[i], mValues[i]);
+ }
+ return true;
+ }
+ }
+
+ static class LookupTinyHashMapOp extends LookupSmallHashMapOp {
+ LookupTinyHashMapOp() {
+ super("LookupTinyHashMap", "Lookup items in 5 entry HashMap", 5);
+ }
+ }
+
+ static class LookupTinyArrayMapOp extends LookupSmallArrayMapOp {
+ LookupTinyArrayMapOp() {
+ super("LookupTinyArrayMap", "Lookup items in 5 entry ArrayMap", 5);
+ }
+ }
+
+ static class LookupSmallHashMapOp extends GenericMapOp {
+ HashMap<String, String> mHashMap;
+
+ LookupSmallHashMapOp() {
+ super("LookupSmallHashMap", "Lookup items in 100 entry HashMap", 100);
+ }
+
+ LookupSmallHashMapOp(String name, String longname, int size) {
+ super(name, longname, size);
+ }
+
+ void onInit(Context context, boolean foreground) {
+ super.onInit(context, foreground);
+ mHashMap = new HashMap<String, String>();
+ for (int i=0; i<mSize; i++) {
+ mHashMap.put(mKeys[i], mValues[i]);
+ }
+ }
+
+ boolean onRun() {
+ for (int i=0; i<mSize; i++) {
+ mHashMap.get(mKeys[i]);
+ }
+ return true;
+ }
+ }
+
+ static class LookupSmallArrayMapOp extends GenericMapOp {
+ ArrayMap<String, String> mArrayMap;
+
+ LookupSmallArrayMapOp() {
+ super("LookupSmallArrayMap", "Lookup items in 100 entry ArrayMap", 100);
+ }
+
+ LookupSmallArrayMapOp(String name, String longname, int size) {
+ super(name, longname, size);
+ }
+
+ void onInit(Context context, boolean foreground) {
+ super.onInit(context, foreground);
+ mArrayMap = new ArrayMap<String, String>();
+ for (int i=0; i<mSize; i++) {
+ mArrayMap.put(mKeys[i], mValues[i]);
+ }
+ }
+
+ boolean onRun() {
+ for (int i=0; i<mSize; i++) {
+ mArrayMap.get(mKeys[i]);
+ }
+ return true;
+ }
+ }
+
+ static class LookupLargeHashMapOp extends LookupSmallHashMapOp {
+ LookupLargeHashMapOp() {
+ super("LookupLargeHashMap", "Lookup items in 10000 entry HashMap", 10000);
+ }
+ }
+
+ static class LookupLargeArrayMapOp extends LookupSmallArrayMapOp {
+ LookupLargeArrayMapOp() {
+ super("LookupLargeArrayMap", "Lookup items in 10000 entry ArrayMap", 10000);
+ }
+ }
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 46a539e..bdd8aa6 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -41,6 +41,16 @@
</activity>
<activity
+ android:name="AssetsAtlasActivity"
+ android:label="Atlas/Framework"
+ android:theme="@android:style/Theme.Holo.Light">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="ScaledTextActivity"
android:label="Text/Scaled"
android:theme="@android:style/Theme.Holo.Light">
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AssetsAtlasActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/AssetsAtlasActivity.java
new file mode 100644
index 0000000..df7e3bb
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/AssetsAtlasActivity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import com.android.internal.R;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AssetsAtlasActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(new BitmapsView(this));
+ }
+
+ static class BitmapsView extends View {
+ private final Bitmap mBitmap;
+
+ BitmapsView(Context c) {
+ super(c);
+
+ Drawable d = c.getResources().getDrawable(R.drawable.text_select_handle_left);
+ mBitmap = ((BitmapDrawable) d).getBitmap();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ final Matrix matrix = new Matrix();
+ matrix.setScale(0.5f, 0.5f);
+
+ final Rect src = new Rect(0, 0, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);
+ final Rect dst = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+
+ canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
+ canvas.translate(0.0f, mBitmap.getHeight());
+ canvas.drawBitmap(mBitmap, matrix, null);
+ canvas.translate(0.0f, mBitmap.getHeight());
+ canvas.drawBitmap(mBitmap, src, dst, null);
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
index 13b6129..db6421e 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
@@ -20,19 +20,15 @@
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
-import android.os.Environment;
import android.util.DisplayMetrics;
import android.view.ContextMenu;
import android.view.View;
-import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
-import java.io.File;
-
@SuppressWarnings({"UnusedDeclaration"})
public class ListActivity extends Activity {
private static final String[] DATA_LIST = {
@@ -86,7 +82,7 @@
ListAdapter adapter = new SimpleListAdapter(this);
- ListView list = (ListView) findViewById(R.id.list);
+ final ListView list = (ListView) findViewById(R.id.list);
list.setAdapter(adapter);
registerForContextMenu(list);
diff --git a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java
index ba09421..98cec55 100644
--- a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java
+++ b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java
@@ -20,7 +20,7 @@
import java.util.ArrayList;
import java.util.concurrent.Semaphore;
-import android.renderscript.RSTextureView;
+import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScript;
import android.renderscript.RenderScriptGL;
@@ -39,7 +39,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
-public class FountainView extends RSTextureView {
+public class FountainView extends RSSurfaceView {
public FountainView(Context context) {
super(context);
@@ -49,13 +49,12 @@
private RenderScriptGL mRS;
private FountainRS mRender;
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- android.util.Log.e("rs", "onAttachedToWindow");
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
if (mRS == null) {
RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
mRS = createRenderScriptGL(sc);
+ mRS.setSurface(holder, w, h);
mRender = new FountainRS();
mRender.init(mRS, getResources());
}
@@ -63,8 +62,6 @@
@Override
protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- android.util.Log.e("rs", "onDetachedFromWindow");
if (mRS != null) {
mRS = null;
destroyRenderScriptGL();
diff --git a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java
index 6e40da3..8636717 100644
--- a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java
+++ b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java
@@ -17,12 +17,13 @@
package com.example.android.rs.fountainfbo;
-import android.renderscript.RSTextureView;
+import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScriptGL;
import android.content.Context;
+import android.view.SurfaceHolder;
import android.view.MotionEvent;
-public class FountainFboView extends RSTextureView {
+public class FountainFboView extends RSSurfaceView {
public FountainFboView(Context context) {
super(context);
@@ -31,13 +32,12 @@
private RenderScriptGL mRS;
private FountainFboRS mRender;
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- android.util.Log.e("rs", "onAttachedToWindow");
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
if (mRS == null) {
RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
mRS = createRenderScriptGL(sc);
+ mRS.setSurface(holder, w, h);
mRender = new FountainFboRS();
mRender.init(mRS, getResources());
}
diff --git a/tests/TransitionTests/Android.mk b/tests/TransitionTests/Android.mk
new file mode 100644
index 0000000..22fa638
--- /dev/null
+++ b/tests/TransitionTests/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := TransitionTests
+
+LOCAL_STATIC_JAVA_LIBRARIES += android-common
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/TransitionTests/AndroidManifest.xml b/tests/TransitionTests/AndroidManifest.xml
new file mode 100644
index 0000000..9a399d0
--- /dev/null
+++ b/tests/TransitionTests/AndroidManifest.xml
@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.transitiontests"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application
+ android:icon="@drawable/icon"
+ android:label="@string/app_name"
+ android:hardwareAccelerated="true"
+ android:theme="@style/AppTheme">
+ <activity android:label="@string/states_test1"
+ android:name="ScenesTestv21">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/states_test_auto_targets"
+ android:name="ScenesTestAutoTargets">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/states_test_auto_transition"
+ android:name="ScenesTestAutoTransition">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/states_test_auto_transition2"
+ android:name="ScenesTestAutoTransition2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/contacts_expansion"
+ android:name="ContactsExpansion">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo0"
+ android:name="Demo0">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo1"
+ android:name="Demo1">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo2"
+ android:name="Demo2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo3"
+ android:name="Demo3">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo4"
+ android:name="Demo4">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo5"
+ android:name="Demo5">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="LoginActivity"
+ android:name="LoginActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="LoginActivityFromResources"
+ android:name="LoginActivityFromResources">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="OverlayTest"
+ android:name="OverlayTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ResourceLoadingTest"
+ android:name="ResourceLoadingTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="FadingTest"
+ android:name="FadingTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="UniqueIds"
+ android:name="UniqueIds">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="HitRectBug"
+ android:name="HitRectBug">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SequenceTest"
+ android:name="SequenceTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SequenceTestSimple"
+ android:name="SequenceTestSimple">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ChangingText"
+ android:name="ChangingText">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ClippingText"
+ android:name="ClippingText">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ListViewAddRemove"
+ android:name="ListViewAddRemove">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ListViewAddRemoveNoTransition"
+ android:name="ListViewAddRemoveNoTransition">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="CrossFadeDemo"
+ android:name="CrossFadeDemo">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Reparenting"
+ android:name="Reparenting">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SurfaceAndTextureViews"
+ android:name="SurfaceAndTextureViews">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="InstanceTargets"
+ android:name="InstanceTargets">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="InterruptionTest"
+ android:name="InterruptionTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="DelayedTransition"
+ android:name="DelayedTransition">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="FadingHierachy"
+ android:name=".FadingHierarchy">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="HierarchicalMove"
+ android:name=".HierarchicalMove">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="CrossfadeImage"
+ android:name=".CrossfadeImage">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/tests/TransitionTests/res/drawable-hdpi/icon.png b/tests/TransitionTests/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-ldpi/icon.png b/tests/TransitionTests/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-mdpi/icon.png b/tests/TransitionTests/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png b/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png
new file mode 100644
index 0000000..5cae8f2
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg
new file mode 100644
index 0000000..8cce1c1
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg
new file mode 100644
index 0000000..26c0a85
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg
new file mode 100644
index 0000000..f18ae5b
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/layout/activity_login.xml b/tests/TransitionTests/res/layout/activity_login.xml
new file mode 100644
index 0000000..2aaafc0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/activity_login.xml
@@ -0,0 +1,86 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/username"
+ android:layout_below="@+id/username"
+ android:layout_marginTop="30dp"
+ android:textColor="#7f7f7f"
+ android:text="@string/password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/usernameEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/username"
+ android:layout_marginLeft="14dp"
+ android:layout_toRightOf="@+id/username"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="51dp"
+ android:text="@string/username"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/passwordEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/password"
+ android:layout_alignBottom="@+id/password"
+ android:layout_alignLeft="@+id/usernameEdit"
+ android:layout_alignParentRight="true"
+ android:editable="false"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="14dp"
+ android:layout_toLeftOf="@+id/passwordEdit"
+ android:onClick="sendMessage"
+ android:text="@string/cancel" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/cancel"
+ android:layout_alignBottom="@+id/cancel"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_marginLeft="41dp"
+ android:onClick="sendMessage"
+ android:text="@string/submit" />
+
+ <TextView
+ android:id="@+id/newuser"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/passwordEdit"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="16dp"
+ android:textColor="#0000ff"
+ android:text="@string/new_user"
+ android:clickable="true"
+ android:onClick="sendMessage"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="italic" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/button_template.xml b/tests/TransitionTests/res/layout/button_template.xml
new file mode 100644
index 0000000..9b867f2
--- /dev/null
+++ b/tests/TransitionTests/res/layout/button_template.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:onClick="sendMessage"
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+</Button>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/changing_text_1.xml b/tests/TransitionTests/res/layout/changing_text_1.xml
new file mode 100644
index 0000000..88086a3
--- /dev/null
+++ b/tests/TransitionTests/res/layout/changing_text_1.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText1"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttona"
+ android:text="@string/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText1"
+ android:background="#8f8f8f"
+ android:id="@+id/textview2"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttonb"
+ android:text="@string/button"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/changing_text_2.xml b/tests/TransitionTests/res/layout/changing_text_2.xml
new file mode 100644
index 0000000..91fdfef
--- /dev/null
+++ b/tests/TransitionTests/res/layout/changing_text_2.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText2"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttona"
+ android:text="@string/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText2"
+ android:background="#8f8f8f"
+ android:id="@+id/textview2"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttonb"
+ android:text="@string/button"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/clipping_text_1.xml b/tests/TransitionTests/res/layout/clipping_text_1.xml
new file mode 100644
index 0000000..5fe45ab
--- /dev/null
+++ b/tests/TransitionTests/res/layout/clipping_text_1.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText1"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/clipping_text_2.xml b/tests/TransitionTests/res/layout/clipping_text_2.xml
new file mode 100644
index 0000000..3a0e3f4
--- /dev/null
+++ b/tests/TransitionTests/res/layout/clipping_text_2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="500dip"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText2"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/contact_collapsed.xml b/tests/TransitionTests/res/layout/contact_collapsed.xml
new file mode 100644
index 0000000..bfa97df
--- /dev/null
+++ b/tests/TransitionTests/res/layout/contact_collapsed.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:background="#fff"
+ android:layout_width="fill_parent"
+ android:id="@+id/topContainer"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_arrow"
+ android:src="@drawable/arrow_thumbnail"/>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_picture"/>
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="0dip"
+ android:id="@+id/itemContainer"
+ android:background="#ccc"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_name"/>
+ <LinearLayout android:orientation="vertical"
+ android:visibility="gone"
+ android:id="@+id/expanded_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_street"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_city"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_phone"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/contact_email"/>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/contact_expanded.xml b/tests/TransitionTests/res/layout/contact_expanded.xml
new file mode 100644
index 0000000..4007d4a
--- /dev/null
+++ b/tests/TransitionTests/res/layout/contact_expanded.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:background="#fff"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_arrow"
+ android:src="@drawable/arrow_thumbnail"/>
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_name"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_street"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_city"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_phone"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/contact_email"/>
+ </LinearLayout>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_picture"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/contacts_list.xml b/tests/TransitionTests/res/layout/contacts_list.xml
new file mode 100644
index 0000000..d38704c
--- /dev/null
+++ b/tests/TransitionTests/res/layout/contacts_list.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/contactsContainer"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/crossfade.xml b/tests/TransitionTests/res/layout/crossfade.xml
new file mode 100644
index 0000000..d766ce1
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="sendMessage"
+ android:id="@+id/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText1"
+ android:id="@+id/textview"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText1"
+ android:id="@+id/textview1"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/someText"
+ android:id="@+id/textview2"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/crossfade_1.xml b/tests/TransitionTests/res/layout/crossfade_1.xml
new file mode 100644
index 0000000..4cc5bc9
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade_1.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="sendMessage"
+ android:id="@+id/button"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText2"
+ android:id="@+id/textview"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText2"
+ android:id="@+id/textview1"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/someText"
+ android:id="@+id/textview2"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/crossfade_image.xml b/tests/TransitionTests/res/layout/crossfade_image.xml
new file mode 100644
index 0000000..c46327a
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade_image.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/self_portrait_square_100"
+ android:onClick="sendMessage"
+ android:id="@+id/contact_picture"/>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/self_portrait_square_100"
+ android:onClick="sendMessage"
+ android:id="@+id/contact_picture1"/>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/self_portrait_square_100"
+ android:onClick="sendMessage"
+ android:id="@+id/contact_picture2"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_hierarchy.xml b/tests/TransitionTests/res/layout/fading_hierarchy.xml
new file mode 100644
index 0000000..a24a6b6
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_hierarchy.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:id="@+id/removingContainer"
+ android:layout_width="wrap_content"
+ android:layout_height="200dip">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:id="@+id/removingButton"/>
+ </LinearLayout>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test.xml b/tests/TransitionTests/res/layout/fading_test.xml
new file mode 100644
index 0000000..3728b5e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:id="@+id/removingButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/invisibleButton"
+ android:id="@+id/invisibleButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/goneButton"
+ android:id="@+id/goneButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:text="@string/button"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test_scene_2.xml b/tests/TransitionTests/res/layout/fading_test_scene_2.xml
new file mode 100644
index 0000000..baf5b4d
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test_scene_2.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/invisibleButton"
+ android:visibility="invisible"
+ android:id="@+id/invisibleButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/goneButton"
+ android:visibility="gone"
+ android:id="@+id/goneButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:text="@string/button"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test_simple.xml b/tests/TransitionTests/res/layout/fading_test_simple.xml
new file mode 100644
index 0000000..d201eca
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test_simple.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:id="@+id/removingButton"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test_simple2.xml b/tests/TransitionTests/res/layout/fading_test_simple2.xml
new file mode 100644
index 0000000..80f3f94
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test_simple2.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="50dip"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:visibility="invisible"
+ android:id="@+id/removingButton"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/hierarchical_move.xml b/tests/TransitionTests/res/layout/hierarchical_move.xml
new file mode 100644
index 0000000..1e70ba9
--- /dev/null
+++ b/tests/TransitionTests/res/layout/hierarchical_move.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button0"
+ android:id="@+id/button0"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button1"
+ android:id="@+id/button1"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button2"
+ android:id="@+id/button2"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button3"
+ android:id="@+id/button3"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button4"
+ android:id="@+id/button4"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button5"
+ android:id="@+id/button5"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/incorrect_password.xml b/tests/TransitionTests/res/layout/incorrect_password.xml
new file mode 100644
index 0000000..af59618
--- /dev/null
+++ b/tests/TransitionTests/res/layout/incorrect_password.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:id="@+id/okay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="164dp"
+ android:onClick="sendMessage"
+ android:text="@string/okay" />
+
+ <TextView
+ android:id="@+id/incorrectpassword"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/okay"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="93dp"
+ android:text="@string/incorrect_password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/tryagain"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/incorrectpassword"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="35dp"
+ android:text="@string/try_again"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/instance_targets.xml b/tests/TransitionTests/res/layout/instance_targets.xml
new file mode 100644
index 0000000..5677d52
--- /dev/null
+++ b/tests/TransitionTests/res/layout/instance_targets.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:onClick="sendMessage"
+ android:id="@+id/button0"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button0"
+ android:onClick="sendMessage"
+ android:id="@+id/button1"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button1"
+ android:onClick="sendMessage"
+ android:id="@+id/button2"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button2"
+ android:onClick="sendMessage"
+ android:id="@+id/button3"/>
+
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/interruption.xml b/tests/TransitionTests/res/layout/interruption.xml
new file mode 100644
index 0000000..9fdb27a
--- /dev/null
+++ b/tests/TransitionTests/res/layout/interruption.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <RadioGroup android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <RadioButton android:id="@+id/scene1RB"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/state1"
+ android:onClick="onRadioButtonClicked"/>
+ <RadioButton android:id="@+id/scene2RB"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/state2"
+ android:onClick="onRadioButtonClicked"/>
+ <RadioButton android:id="@+id/scene3RB"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/state3"
+ android:onClick="onRadioButtonClicked"/>
+ <RadioButton android:id="@+id/scene4RB"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/state4"
+ android:onClick="onRadioButtonClicked"/>
+ </RadioGroup>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:id="@+id/sceneRoot"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/interruption_inner_1"/>
+
+ </LinearLayout>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/interruption_inner_1.xml b/tests/TransitionTests/res/layout/interruption_inner_1.xml
new file mode 100644
index 0000000..f82dfb0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/interruption_inner_1.xml
@@ -0,0 +1,20 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/buttonContainer">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:text="@string/state1"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button1"
+ android:layout_alignLeft="@+id/button"
+ android:layout_below="@+id/button"
+ android:text="@string/state1"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/interruption_inner_2.xml b/tests/TransitionTests/res/layout/interruption_inner_2.xml
new file mode 100644
index 0000000..e6821d6
--- /dev/null
+++ b/tests/TransitionTests/res/layout/interruption_inner_2.xml
@@ -0,0 +1,20 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/buttonContainer">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:id="@+id/button"
+ android:layout_alignParentRight="true"
+ android:text="@string/state2"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button1"
+ android:layout_alignLeft="@+id/button"
+ android:layout_below="@+id/button"
+ android:text="@string/state2"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/interruption_inner_3.xml b/tests/TransitionTests/res/layout/interruption_inner_3.xml
new file mode 100644
index 0000000..4e40150
--- /dev/null
+++ b/tests/TransitionTests/res/layout/interruption_inner_3.xml
@@ -0,0 +1,20 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/buttonContainer">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:text="@string/state3"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button1"
+ android:layout_alignLeft="@+id/button"
+ android:layout_above="@+id/button"
+ android:text="@string/state3"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/interruption_inner_4.xml b/tests/TransitionTests/res/layout/interruption_inner_4.xml
new file mode 100644
index 0000000..8c3661e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/interruption_inner_4.xml
@@ -0,0 +1,20 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/buttonContainer">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:text="@string/state4"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button1"
+ android:layout_alignLeft="@+id/button"
+ android:layout_above="@+id/button"
+ android:text="@string/state4"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/list_view_add_remove.xml b/tests/TransitionTests/res/layout/list_view_add_remove.xml
new file mode 100644
index 0000000..230511f
--- /dev/null
+++ b/tests/TransitionTests/res/layout/list_view_add_remove.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/listview"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/login_password.xml b/tests/TransitionTests/res/layout/login_password.xml
new file mode 100644
index 0000000..1e75694
--- /dev/null
+++ b/tests/TransitionTests/res/layout/login_password.xml
@@ -0,0 +1,86 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".LoginActivity" >
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/username"
+ android:layout_below="@+id/username"
+ android:layout_marginTop="30dp"
+ android:text="@string/password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/usernameEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/username"
+ android:layout_marginLeft="14dp"
+ android:layout_toRightOf="@+id/username"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="51dp"
+ android:text="@string/username"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/passwordEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/password"
+ android:layout_alignBottom="@+id/password"
+ android:layout_alignLeft="@+id/usernameEdit"
+ android:layout_alignParentRight="true"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="14dp"
+ android:layout_toLeftOf="@+id/passwordEdit"
+ android:onClick="sendMessage"
+ android:text="@string/cancel" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/cancel"
+ android:layout_alignBottom="@+id/cancel"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_marginLeft="41dp"
+ android:onClick="sendMessage"
+ android:text="@string/submit" />
+
+ <TextView
+ android:id="@+id/newuser"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/passwordEdit"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="16dp"
+ android:textColor="#0000ff"
+ android:text="@string/new_user"
+ android:clickable="true"
+ android:onClick="sendMessage"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="italic" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/main.xml b/tests/TransitionTests/res/layout/main.xml
new file mode 100644
index 0000000..b42318e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/main.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/new_user.xml b/tests/TransitionTests/res/layout/new_user.xml
new file mode 100644
index 0000000..f8dfab06
--- /dev/null
+++ b/tests/TransitionTests/res/layout/new_user.xml
@@ -0,0 +1,92 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container"
+ tools:context=".LoginActivity" >
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/username"
+ android:layout_below="@+id/username"
+ android:layout_marginTop="30dp"
+ android:text="@string/password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/usernameEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/username"
+ android:layout_marginLeft="14dp"
+ android:layout_toRightOf="@+id/username"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="51dp"
+ android:text="@string/username"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/passwordEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/password"
+ android:layout_alignBottom="@+id/password"
+ android:layout_alignLeft="@+id/usernameEdit"
+ android:layout_alignParentRight="true"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="14dp"
+ android:layout_toLeftOf="@+id/passwordEdit"
+ android:onClick="sendMessage"
+ android:text="@string/cancel" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/cancel"
+ android:layout_alignBottom="@+id/cancel"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_marginLeft="41dp"
+ android:onClick="sendMessage"
+ android:text="@string/submit" />
+
+ <EditText
+ android:id="@+id/retypeEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/retype"
+ android:layout_alignBottom="@+id/retype"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_alignParentRight="true"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/retype"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/passwordEdit"
+ android:layout_marginTop="26dp"
+ android:layout_toLeftOf="@+id/usernameEdit"
+ android:text="@string/retype"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/overlay_test.xml b/tests/TransitionTests/res/layout/overlay_test.xml
new file mode 100644
index 0000000..edd0393
--- /dev/null
+++ b/tests/TransitionTests/res/layout/overlay_test.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:text="@string/start"
+ android:onClick="onClick"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fadingButton"
+ android:id="@+id/fadingButton"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/reparenting.xml b/tests/TransitionTests/res/layout/reparenting.xml
new file mode 100644
index 0000000..b3bfbb9
--- /dev/null
+++ b/tests/TransitionTests/res/layout/reparenting.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/container1"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/container2"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/resources_test_layout.xml b/tests/TransitionTests/res/layout/resources_test_layout.xml
new file mode 100644
index 0000000..48affa0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/resources_test_layout.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView
+ src="@drawable/icon"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/results_screen.xml b/tests/TransitionTests/res/layout/results_screen.xml
new file mode 100644
index 0000000..8550a5e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/results_screen.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="#7f7f7f"
+ android:id="@+id/container">
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|right"
+ android:id="@+id/searchContainer">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchText"
+ android:id="@+id/searchText"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchButton"
+ android:onClick="sendMessage"
+ android:id="@+id/searchButton"/>
+ </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/resultsTitle"
+ android:id="@+id/resultsText"/>
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:id="@+id/resultsList">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/search_screen.xml b/tests/TransitionTests/res/layout/search_screen.xml
new file mode 100644
index 0000000..947702b
--- /dev/null
+++ b/tests/TransitionTests/res/layout/search_screen.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="#000000"
+ android:id="@+id/container">
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:id="@+id/searchContainer">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchText"
+ android:id="@+id/searchText"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchButton"
+ android:onClick="sendMessage"
+ android:id="@+id/searchButton"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/success.xml b/tests/TransitionTests/res/layout/success.xml
new file mode 100644
index 0000000..9c265ef
--- /dev/null
+++ b/tests/TransitionTests/res/layout/success.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <TextView
+ android:id="@+id/loginsuccessful"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="93dp"
+ android:text="@string/login_successful"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/firstactivityscreen"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/loginsuccessful"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="35dp"
+ android:text="@string/first_activity_screen"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <Button
+ android:id="@+id/reset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="164dp"
+ android:onClick="sendMessage"
+ android:text="@string/reset" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/surface_texture_views.xml b/tests/TransitionTests/res/layout/surface_texture_views.xml
new file mode 100644
index 0000000..9260bc0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/surface_texture_views.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:text="@string/toggle"
+ android:id="@+id/toggleButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/two_buttons.xml b/tests/TransitionTests/res/layout/two_buttons.xml
new file mode 100644
index 0000000..23d59f8
--- /dev/null
+++ b/tests/TransitionTests/res/layout/two_buttons.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button1"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button2"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/unique_id_test.xml b/tests/TransitionTests/res/layout/unique_id_test.xml
new file mode 100644
index 0000000..9b7eb10
--- /dev/null
+++ b/tests/TransitionTests/res/layout/unique_id_test.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/username_taken.xml b/tests/TransitionTests/res/layout/username_taken.xml
new file mode 100644
index 0000000..9484e69
--- /dev/null
+++ b/tests/TransitionTests/res/layout/username_taken.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:id="@+id/okay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="164dp"
+ android:onClick="sendMessage"
+ android:text="@string/okay" />
+
+ <TextView
+ android:id="@+id/usernametaken"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/okay"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="93dp"
+ android:text="@string/username_taken"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/textView2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/usernametaken"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="35dp"
+ android:text="@string/try_again"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/incorrect_password_scene.xml b/tests/TransitionTests/res/scene/incorrect_password_scene.xml
new file mode 100644
index 0000000..a31ad22
--- /dev/null
+++ b/tests/TransitionTests/res/scene/incorrect_password_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/incorrect_password"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/login_scene.xml b/tests/TransitionTests/res/scene/login_scene.xml
new file mode 100644
index 0000000..b258303
--- /dev/null
+++ b/tests/TransitionTests/res/scene/login_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/activity_login"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/my_scene.xml b/tests/TransitionTests/res/scene/my_scene.xml
new file mode 100644
index 0000000..b8278c9
--- /dev/null
+++ b/tests/TransitionTests/res/scene/my_scene.xml
@@ -0,0 +1,3 @@
+<scene >
+
+</scene>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/new_user_scene.xml b/tests/TransitionTests/res/scene/new_user_scene.xml
new file mode 100644
index 0000000..d6e5f0f
--- /dev/null
+++ b/tests/TransitionTests/res/scene/new_user_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/new_user"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/password_scene.xml b/tests/TransitionTests/res/scene/password_scene.xml
new file mode 100644
index 0000000..99a0038
--- /dev/null
+++ b/tests/TransitionTests/res/scene/password_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/login_password"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/results_scene.xml b/tests/TransitionTests/res/scene/results_scene.xml
new file mode 100644
index 0000000..9c9fc69
--- /dev/null
+++ b/tests/TransitionTests/res/scene/results_scene.xml
@@ -0,0 +1,3 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/results_screen">
+</scene>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/search_scene.xml b/tests/TransitionTests/res/scene/search_scene.xml
new file mode 100644
index 0000000..742cd57
--- /dev/null
+++ b/tests/TransitionTests/res/scene/search_scene.xml
@@ -0,0 +1,3 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/search_screen">
+</scene>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/success_scene.xml b/tests/TransitionTests/res/scene/success_scene.xml
new file mode 100644
index 0000000..3d76d89
--- /dev/null
+++ b/tests/TransitionTests/res/scene/success_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/success"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/username_taken_scene.xml b/tests/TransitionTests/res/scene/username_taken_scene.xml
new file mode 100644
index 0000000..b2f050e
--- /dev/null
+++ b/tests/TransitionTests/res/scene/username_taken_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/username_taken"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/colorizer_transition.xml b/tests/TransitionTests/res/transition/colorizer_transition.xml
new file mode 100644
index 0000000..731d7ee
--- /dev/null
+++ b/tests/TransitionTests/res/transition/colorizer_transition.xml
@@ -0,0 +1,6 @@
+<recolor xmlns:android="http://schemas.android.com/apk/res/android">>
+ <targets>
+ <target android:targetID="@id/password"/>
+ <target android:targetID="@id/passwordEdit"/>
+ </targets>
+</recolor>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/fader.xml b/tests/TransitionTests/res/transition/fader.xml
new file mode 100644
index 0000000..c71fd94
--- /dev/null
+++ b/tests/TransitionTests/res/transition/fader.xml
@@ -0,0 +1 @@
+<fade/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/login_slider_transition.xml b/tests/TransitionTests/res/transition/login_slider_transition.xml
new file mode 100644
index 0000000..dbdd6e9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/login_slider_transition.xml
@@ -0,0 +1,22 @@
+<transitionGroup xmlns:android="http://schemas.android.com/apk/res/android">
+ <slide>
+ <targets>
+ <target android:targetID="@id/retype"/>
+ <target android:targetID="@id/retypeEdit"/>
+ </targets>
+ </slide>
+ <recolor>
+ <targets>
+ <target android:targetID="@id/password"/>
+ <target android:targetID="@id/passwordEdit"/>
+ </targets>
+ </recolor>
+ <fade/>
+</transitionGroup>
+
+<!--
+ TransitionGroup slider = new TransitionGroup();
+ slider.addTransition(new Slide(R.id.retype, R.id.retypeEdit));
+ slider.addTransition(new Recolor(R.id.password, R.id.passwordEdit));
+ slider.addTransition(new Fade());
+-->
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/login_transition_mgr.xml b/tests/TransitionTests/res/transition/login_transition_mgr.xml
new file mode 100644
index 0000000..8a8b9e9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/login_transition_mgr.xml
@@ -0,0 +1,39 @@
+<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
+ <transition fromScene="@scene/login_scene" toScene="@scene/new_user_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/password_scene" toScene="@scene/new_user_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/new_user_scene" toScene="@scene/login_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/new_user_scene" toScene="@scene/password_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/login_scene" toScene="@scene/password_scene"
+ transition="@transition/colorizer_transition"/>
+ <transition fromScene="@scene/password_scene" toScene="@scene/login_scene"
+ transition="@transition/colorizer_transition"/>
+</transitionManager>
+
+ <!--
+ mLoginScene = new Scene(this, mSceneRoot, R.layout.activity_login);
+ mPasswordScene = new Scene(this, mSceneRoot, R.layout.login_password);
+ mIncorrectPasswordScene = new Scene(this, mSceneRoot, R.layout.incorrect_password);
+ mUsernameTakenScene = new Scene(this, mSceneRoot, R.layout.username_taken);
+ mSuccessScene = new Scene(this, mSceneRoot, R.layout.success);
+ mNewUserScene = new Scene(this, mSceneRoot, R.layout.new_user);
+
+ mTransitionManager = new TransitionManager();
+ // Custom transitions in/out of NewUser screen - slide in the 2nd password UI
+ TransitionGroup slider = new TransitionGroup();
+ slider.addTransition(new Slide(R.id.retype, R.id.retypeEdit));
+ slider.addTransition(new Recolor(R.id.password, R.id.passwordEdit));
+ slider.addTransition(new Fade());
+ mTransitionManager.setTransition(mLoginScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mPasswordScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mLoginScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mPasswordScene, slider);
+
+ // Custom transitions with recoloring password field
+ Transition colorizer = new Recolor(R.id.password, R.id.passwordEdit);
+ mTransitionManager.setTransition(mLoginScene, mPasswordScene, colorizer);
+ mTransitionManager.setTransition(mPasswordScene, mLoginScene, colorizer);
+-->
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/mover.xml b/tests/TransitionTests/res/transition/mover.xml
new file mode 100644
index 0000000..3c47606
--- /dev/null
+++ b/tests/TransitionTests/res/transition/mover.xml
@@ -0,0 +1 @@
+<move/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/mover_fader.xml b/tests/TransitionTests/res/transition/mover_fader.xml
new file mode 100644
index 0000000..8b805e9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/mover_fader.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<transitionGroup xmlns:android="http://schemas.android.com/apk/res/android">
+ <fade/>
+ <move/>
+</transitionGroup>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/my_transition.xml b/tests/TransitionTests/res/transition/my_transition.xml
new file mode 100644
index 0000000..14c91b9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/my_transition.xml
@@ -0,0 +1,20 @@
+<transitionGroup xmlns:android="http://schemas.android.com/apk/res/android">
+ <fade></fade>
+ <transitionGroup>
+ <move android:duration="500">
+ <targets>
+ <target android:targetID="@id/container"/>
+ <target android:targetID="@id/resultsList"/>
+ </targets>
+ </move>
+ <transitionGroup>
+ <targets>
+ <target android:targetID="@id/container"/>
+ <target android:targetID="@id/resultsList"/>
+ </targets>
+ <fade android:startOffset="25"/>
+ </transitionGroup>
+ <recolor/>
+ </transitionGroup>
+ <move/>
+</transitionGroup>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/my_transition_mgr.xml b/tests/TransitionTests/res/transition/my_transition_mgr.xml
new file mode 100644
index 0000000..ca9705a
--- /dev/null
+++ b/tests/TransitionTests/res/transition/my_transition_mgr.xml
@@ -0,0 +1,6 @@
+<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
+ <transition fromScene="@scene/search_scene" toScene="@scene/results_scene"
+ transition="@transition/mover_fader"/>
+ <transition fromScene="@scene/results_scene" toScene="@scene/search_scene"
+ transition="@transition/mover_fader"/>
+</transitionManager>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/values-v11/styles.xml b/tests/TransitionTests/res/values-v11/styles.xml
new file mode 100644
index 0000000..541752f
--- /dev/null
+++ b/tests/TransitionTests/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/values-v14/styles.xml b/tests/TransitionTests/res/values-v14/styles.xml
new file mode 100644
index 0000000..f20e015
--- /dev/null
+++ b/tests/TransitionTests/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/values/strings.xml b/tests/TransitionTests/res/values/strings.xml
new file mode 100644
index 0000000..9b80a26
--- /dev/null
+++ b/tests/TransitionTests/res/values/strings.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">StatesTest</string>
+ <string name="states_test1">StatesTestv21</string>
+ <string name="states_test_auto_targets">StatesTestAutoTargets</string>
+ <string name="states_test_auto_transition">StatesTestAutoTransition</string>
+ <string name="states_test_auto_transition2">StatesTestAutoTransition2</string>
+ <string name="contacts_expansion">ContactsExpansion</string>
+ <string name="states_test3">StatesTest3</string>
+ <string name="states_test4">StatesTest4</string>
+ <string name="states_test5">StatesTest5</string>
+ <string name="states_test6">StatesTest6</string>
+ <string name="button">Button</string>
+ <string name="searchButton">Search</string>
+ <string name="searchText">This is some text</string>
+ <string name="resultsTitle">Search Results</string>
+ <string name="placeholder">Blah Blah Blah</string>
+ <string name="username">Username:</string>
+ <string name="password">Password:</string>
+ <string name="retype">Retype:</string>
+ <string name="new_user">New User?</string>
+ <string name="incorrect_password">Incorrect password:</string>
+ <string name="try_again">Please try again.</string>
+ <string name="login_successful">Success!</string>
+ <string name="first_activity_screen">First activity screen</string>
+ <string name="username_taken">Username taken:</string>
+ <string name="cancel">Cancel</string>
+ <string name="submit">Submit</string>
+ <string name="okay">Okay</string>
+ <string name="reset">Reset</string>
+ <string name="fadingButton">Fading Button</string>
+ <string name="removingButton">Removing Button</string>
+ <string name="invisibleButton">invisible Button</string>
+ <string name="goneButton">Gone Button</string>
+ <string name="start">Start</string>
+ <string name="toggle">Toggle State</string>
+ <string name="someText">This is some text</string>
+ <string name="shortText1">This is some short text</string>
+ <string name="shortText2">Not much text here</string>
+ <string name="longText1">This is the beginning of the Spring of my discontent. In the event of a real emergency, you would be notified by email. Fear not, for death comes swiftly.</string>
+ <string name="longText2">When do we get to eat? I like all things, especially following strong leaders, and mangy cats. Break glass in emergency. The purpose of a framework is to provide the facilities and functionality of a powerful toolkit with the simplicity of a refrigerator.</string>
+ <string name="state1">State 1</string>
+ <string name="state2">State 2</string>
+ <string name="state3">State 3</string>
+ <string name="state4">State 4</string>
+ <string name="button0">Button 0</string>
+ <string name="button1">Button 1</string>
+ <string name="button2">Button 2</string>
+ <string name="button3">Button 3</string>
+ <string name="button4">Button 4</string>
+ <string name="button5">Button 5</string>
+</resources>
diff --git a/tests/TransitionTests/res/values/styles.xml b/tests/TransitionTests/res/values/styles.xml
new file mode 100644
index 0000000..4a10ca4
--- /dev/null
+++ b/tests/TransitionTests/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java b/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java
new file mode 100644
index 0000000..05bed5f
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.TextChange;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+public class ChangingText extends Activity {
+
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ TransitionGroup mChanger;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.changing_text_1);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mScene1 = new Scene(mSceneRoot, R.layout.changing_text_1, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.changing_text_2, this);
+
+ mChanger = new TransitionGroup(TransitionGroup.TOGETHER);
+ mChanger.addTransitions(new Move(), new TextChange());
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, mChanger);
+ } else {
+ TransitionManager.go(mScene1, mChanger);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java b/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java
new file mode 100644
index 0000000..c3201f2
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.TextChange;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+public class ClippingText extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ // static Fade sFade = new Fade(R.id.removingButton, R.id.invisibleButton, R.id.goneButton);
+ Fade fader;
+ TransitionGroup mChanger;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.clipping_text_1);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mScene1 = new Scene(mSceneRoot, R.layout.clipping_text_1, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.clipping_text_2, this);
+
+ mChanger = new TransitionGroup(TransitionGroup.TOGETHER);
+ Move move = new Move();
+ move.setResizeClip(true);
+ mChanger.addTransitions(move, new TextChange());
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, mChanger);
+ } else {
+ TransitionManager.go(mScene1, mChanger);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java b/tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java
new file mode 100644
index 0000000..c7da6a3
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Rotate;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+public class ContactsExpansion extends Activity {
+
+ String contactsData[] = {
+ "Alan Green", "56 Bob Street", "Boston, MA 02134", "617-555-5555", "blatt@blatt.com",
+ "Bob Foonman", "92 The Avenue", "Chico, CA 78456", "510-555-5556", "bob@jerk.com",
+ "Tracey Sue", "95 Houses Street", "San Jose, CA 96504", "415-555-5557", "ts@thing.com",
+ };
+
+ View currentItem = null;
+
+ TransitionGroup mMyAutoTransition = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.contacts_list);
+ ViewGroup contactsContainer = (ViewGroup) findViewById(R.id.contactsContainer);
+
+ int contactsIndex = 0;
+ addContact(contactsContainer, contactsIndex, R.drawable.self_portrait_square_100);
+ contactsIndex += 5;
+ addContact(contactsContainer, contactsIndex, R.drawable.self_portrait_square_100);
+ contactsIndex += 5;
+ addContact(contactsContainer, contactsIndex, R.drawable.self_portrait_square_100);
+
+ }
+
+ private void addContact(ViewGroup container, int dataIndex, int thumbnailID) {
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View contactItem = inflater.inflate(R.layout.contact_collapsed, container, false);
+ ImageView thumbnailView = (ImageView) contactItem.findViewById(R.id.contact_picture);
+ thumbnailView.setImageResource(thumbnailID);
+ ((TextView)contactItem.findViewById(R.id.contact_name)).setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_street)).
+ setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_city)).setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_phone)).setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_email)).setText(contactsData[dataIndex++]);
+ container.addView(contactItem);
+
+ final TransitionGroup myTransition = new TransitionGroup();
+ myTransition.addTransitions(new Fade(Fade.IN),
+ new Rotate().setTargetIds(R.id.contact_arrow),
+ new Move(), new Fade(Fade.OUT),
+ new Crossfade().setTargetIds(R.id.contact_picture));
+ final ToggleScene toggleScene = new ToggleScene(container, myTransition);
+ contactItem.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ currentItem = v;
+ toggleScene.changeToScene();
+ }
+ });
+ }
+
+ class ToggleScene {
+ boolean expanded = false;
+ Scene mScene;
+ Transition mTransition;
+
+ ToggleScene(ViewGroup rootView, Transition transition) {
+ mScene = new Scene(rootView);
+ mTransition = transition;
+ mScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ if (currentItem != null) {
+ System.out.println("onsceneChanged: currentItem = " + currentItem);
+ View expandedContainer = currentItem.findViewById(R.id.expanded_info);
+ expandedContainer.setVisibility(expanded ? View.GONE : View.VISIBLE);
+ ImageView thumbnailView =
+ (ImageView) currentItem.findViewById(R.id.contact_picture);
+ thumbnailView.setImageResource(expanded ? R.drawable.self_portrait_square_100 :
+ R.drawable.self_portrait_square_200);
+ ImageView arrow = (ImageView) currentItem.findViewById(R.id.contact_arrow);
+ arrow.setRotation(expanded ? 0 : 90);
+ expanded = !expanded;
+ }
+ }
+ });
+ }
+
+ void changeToScene() {
+ TransitionManager.go(mScene, mTransition);
+ }
+ };
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java b/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java
new file mode 100644
index 0000000..d5f6a28
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+
+public class CrossFadeDemo extends Activity {
+
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+ Scene mScene1, mScene2;
+ TransitionManager mTransitionManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.crossfade);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mScene1 = new Scene(mSceneRoot, R.layout.crossfade, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.crossfade_1, this);
+
+ Crossfade crossfade = new Crossfade();
+ crossfade.setFadeBehavior(Crossfade.FADE_BEHAVIOR_CROSSFADE);
+ crossfade.setResizeBehavior(Crossfade.RESIZE_BEHAVIOR_NONE);
+ crossfade.setTargetIds(R.id.textview, R.id.textview1, R.id.textview2);
+ mTransitionManager = new TransitionManager();
+ TransitionGroup moveCrossFade = new TransitionGroup();
+ moveCrossFade.addTransitions(crossfade, new Move());
+ mTransitionManager.setTransition(mScene1, moveCrossFade);
+ mTransitionManager.setTransition(mScene2, moveCrossFade);
+ mCurrentScene = 1;
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == 1) {
+ mTransitionManager.transitionTo(mScene2);
+ mCurrentScene = 2;
+ } else {
+ mTransitionManager.transitionTo(mScene1);
+ mCurrentScene = 1;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java
new file mode 100644
index 0000000..28e055f
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.ImageView;
+
+public class CrossfadeImage extends Activity {
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+ Scene mScene1, mScene2;
+ TransitionManager mTransitionManager;
+ boolean mExpanded = false;
+ Transition mTransition;
+ ImageView mImageView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.crossfade_image);
+
+ ViewGroup container = (ViewGroup) findViewById(R.id.container);
+ mSceneRoot = container;
+
+ mImageView = (ImageView) findViewById(R.id.contact_picture);
+ mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+
+ Crossfade mCrossfade = new Crossfade();
+ mCrossfade.setTargetIds(R.id.contact_picture);
+
+ TransitionGroup group = new TransitionGroup();
+ group.setDuration(1500);
+ group.addTransitions(mCrossfade, new Move());
+ mTransition = group;
+ }
+
+ public void sendMessage(View view) {
+ TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+ if (mExpanded) {
+ mImageView.setImageResource(R.drawable.self_portrait_square_100);
+ } else {
+ mImageView.setImageResource(R.drawable.self_portrait_square_200);
+ }
+ mExpanded = !mExpanded;
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/DelayedTransition.java b/tests/TransitionTests/src/com/android/transitiontests/DelayedTransition.java
new file mode 100644
index 0000000..f05ea78
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/DelayedTransition.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import static android.widget.LinearLayout.LayoutParams;
+
+public class DelayedTransition extends Activity {
+
+ private static final int SEARCH_SCREEN = 0;
+ private static final int RESULTS_SCREEN = 1;
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.two_buttons);
+
+ final Button button1 = (Button) findViewById(R.id.button1);
+ final Button button2 = (Button) findViewById(R.id.button2);
+ final LinearLayout container = (LinearLayout) findViewById(R.id.container);
+ button1.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int buttonWidth = button1.getWidth();
+ int containerWidth = container.getWidth();
+ if (buttonWidth < containerWidth) {
+ TransitionManager.beginDelayedTransition(container, null);
+ button1.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT));
+ TransitionManager.beginDelayedTransition(container, null);
+ button2.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT));
+ } else {
+ TransitionManager.beginDelayedTransition(container, null);
+ button1.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT));
+ TransitionManager.beginDelayedTransition(container, null);
+ button2.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT));
+ }
+ }
+ });
+ }
+
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo0.java b/tests/TransitionTests/src/com/android/transitiontests/Demo0.java
new file mode 100644
index 0000000..d52ab1d
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo0.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+public class Demo0 extends Activity {
+
+ private static final int SEARCH_SCREEN = 0;
+ private static final int RESULTS_SCREEN = 1;
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mCurrentScene = SEARCH_SCREEN;
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == RESULTS_SCREEN) {
+ mSceneRoot.removeAllViews();
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.search_screen, mSceneRoot);
+ mCurrentScene = SEARCH_SCREEN;
+ } else {
+ mSceneRoot.removeAllViews();
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.results_screen, mSceneRoot);
+ mCurrentScene = RESULTS_SCREEN;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo1.java b/tests/TransitionTests/src/com/android/transitiontests/Demo1.java
new file mode 100644
index 0000000..ce7f439
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo1.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+
+public class Demo1 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ boolean mFirstTime = true;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+// mResultsScreen = new MyScene(mSceneRoot, R.layout.results_screen);
+// mSearchScreen = new MyScene(mSceneRoot, R.layout.search_screen);
+ mResultsScreen = new Scene(mSceneRoot);
+ mResultsScreen.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.results_screen, mSceneRoot);
+ }
+ });
+ mSearchScreen = new Scene(mSceneRoot);
+ mSearchScreen.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.search_screen, mSceneRoot);
+ }
+ });
+
+ }
+
+ public void sendMessage(View view) {
+ if (mFirstTime) {
+ mFirstTime = false;
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade().setTargetIds(R.id.resultsText, R.id.resultsList),
+ new Move().setTargetIds(R.id.searchContainer));
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo2.java b/tests/TransitionTests/src/com/android/transitiontests/Demo2.java
new file mode 100644
index 0000000..7e1afab
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo2.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionInflater;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+public class Demo2 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ boolean mFirstTime = true;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ }
+
+ public void sendMessage(View view) {
+ if (mFirstTime) {
+ mFirstTime = false;
+ // Non-resource approach of creating scenes
+// mSearchScreen = new Scene(this, mSceneRoot, R.layout.search_screen);
+// mResultsScreen = new Scene(this, mSceneRoot, R.layout.results_screen);
+ try {
+ mSearchScreen = TransitionInflater.from(this).
+ inflateScene(R.scene.search_scene, mSceneRoot);
+ mResultsScreen = TransitionInflater.from(this).
+ inflateScene(R.scene.results_scene, mSceneRoot);
+ } catch (Exception e) {
+ System.out.println("Problem loading scene resource: " + e);
+ }
+
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade().setTargetIds(R.id.resultsText, R.id.resultsList),
+ new Move().setTargetIds(R.id.searchContainer),
+ new Recolor().setTargetIds(R.id.container));
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo3.java b/tests/TransitionTests/src/com/android/transitiontests/Demo3.java
new file mode 100644
index 0000000..b8daff5
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo3.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+
+public class Demo3 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade(), new Move(), new Recolor());
+
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo4.java b/tests/TransitionTests/src/com/android/transitiontests/Demo4.java
new file mode 100644
index 0000000..8be54f5
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo4.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+
+public class Demo4 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transitionToResults = new TransitionGroup();
+ Fade fade = new Fade();
+ fade.setTargetIds(R.id.resultsText, R.id.resultsList);
+ fade.setStartDelay(300);
+ fade.setDuration(1000);
+ transitionToResults.addTransitions(fade, new Move().setTargetIds(R.id.searchContainer),
+ new Recolor().setTargetIds(R.id.container));
+
+ TransitionGroup transitionToSearch = new TransitionGroup();
+ transitionToSearch.addTransitions(fade, new Move().setTargetIds(R.id.searchContainer),
+ new Recolor().setTargetIds(R.id.container));
+
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transitionToSearch);
+ mTransitionManager.setTransition(mResultsScreen, transitionToResults);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo5.java b/tests/TransitionTests/src/com/android/transitiontests/Demo5.java
new file mode 100644
index 0000000..e64511e
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo5.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+
+
+public class Demo5 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Scene mSearchScreen, mResultsScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ TransitionManager.go(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ TransitionManager.go(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/FadingHierarchy.java b/tests/TransitionTests/src/com/android/transitiontests/FadingHierarchy.java
new file mode 100644
index 0000000..e0fe379
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/FadingHierarchy.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+
+public class FadingHierarchy extends Activity {
+
+ ViewGroup mRemovingContainer, mContainer;
+ Button mRemovingButton;
+ boolean mVisible = true;
+ ViewGroup mInnerContainerParent;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_hierarchy);
+
+ mContainer = (ViewGroup) findViewById(R.id.container);
+ mRemovingContainer = (ViewGroup) findViewById(R.id.removingContainer);
+ mInnerContainerParent = (ViewGroup) mRemovingContainer.getParent();
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+ }
+
+ public void sendMessage(View view) {
+ TransitionManager.beginDelayedTransition(mContainer, null);
+ if (mVisible) {
+ mInnerContainerParent.removeView(mRemovingContainer);
+ } else {
+ mInnerContainerParent.addView(mRemovingContainer);
+ }
+ mVisible = !mVisible;
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/FadingTest.java b/tests/TransitionTests/src/com/android/transitiontests/FadingTest.java
new file mode 100644
index 0000000..400e994
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/FadingTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.TransitionManager;
+
+
+public class FadingTest extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ static Fade sFade = new Fade();
+
+ static {
+ sFade.setTargetIds(R.id.removingButton, R.id.invisibleButton, R.id.goneButton);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_test);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+ mInvisibleButton = (Button) findViewById(R.id.invisibleButton);
+ mGoneButton = (Button) findViewById(R.id.goneButton);
+
+ mGoneButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mGoneButton.setAlpha(mGoneButton.getAlpha() < 1 ? 1 : .6f);
+ }
+ });
+
+ mScene1 = new Scene(mSceneRoot, R.layout.fading_test, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.fading_test_scene_2, this);
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2);
+ } else {
+ TransitionManager.go(mScene1);
+ }
+ }
+
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/HierarchicalMove.java b/tests/TransitionTests/src/com/android/transitiontests/HierarchicalMove.java
new file mode 100644
index 0000000..093d7c1
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/HierarchicalMove.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Move;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+
+import static android.widget.LinearLayout.LayoutParams;
+
+public class HierarchicalMove extends Activity {
+
+ Button[] buttons = new Button[6];
+ ViewGroup mSceneRoot;
+ boolean wide = false;
+ Transition mTransition;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.hierarchical_move);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ buttons[0] = (Button) findViewById(R.id.button0);
+ buttons[1] = (Button) findViewById(R.id.button1);
+ buttons[2] = (Button) findViewById(R.id.button2);
+ buttons[3] = (Button) findViewById(R.id.button3);
+ buttons[4] = (Button) findViewById(R.id.button4);
+ buttons[5] = (Button) findViewById(R.id.button5);
+
+ // Move button0, then buttons 1/2 together, then buttons 3/4/5 sequentially:
+ // group (seq)
+ // Move 0
+ // group (seq)
+ // group (together)
+ // Move 1
+ // Move 2
+ // group (sequentially)
+ // Move 3
+ // Move 4/5
+ TransitionGroup rootTransition = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+
+ // button0
+ Transition move0 = new Move();
+ move0.setTargets(buttons[0]);
+
+ // buttons 1/2/3/4/5
+ TransitionGroup group12345 = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+
+ // buttons 1/2
+ TransitionGroup group12 = new TransitionGroup(TransitionGroup.TOGETHER);
+ Move move1 = new Move();
+ move1.setTargets(buttons[1]);
+ Move move2 = new Move();
+ move2.setTargets(buttons[2]);
+ group12.addTransitions(move1, move2);
+
+ TransitionGroup group345 = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ Move move3 = new Move();
+ move3.setTargets(buttons[3]);
+ Move move45 = new Move();
+ move45.setTargets(buttons[4], buttons[5]);
+ group345.addTransitions(move3, move45);
+
+ group12345.addTransitions(move0, group12, group345);
+
+ rootTransition.addTransitions(group12345);
+ rootTransition.setDuration(1000);
+ mTransition = rootTransition;
+
+ }
+
+ public void sendMessage(View view) {
+ TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+ int widthSpec = wide ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
+ LayoutParams params = new LayoutParams(widthSpec, LayoutParams.WRAP_CONTENT);
+ for (int i = 0; i < buttons.length; ++i) {
+ buttons[i].setLayoutParams(params);
+ }
+ wide = !wide;
+ }
+
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java b/tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java
new file mode 100644
index 0000000..9f19405
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+
+public class HitRectBug extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(new TestDrawingView(this));
+ }
+
+ public static class TestDrawingView extends RelativeLayout
+ {
+ private Rect mRect = new Rect();
+ private Paint mPaint;
+ private ImageView mImageView;
+
+ public TestDrawingView(Context context)
+ {
+ super(context);
+ setWillNotDraw(false);
+
+ mPaint = new Paint();
+ mPaint.setColor(Color.RED);
+ mPaint.setStyle(Paint.Style.STROKE);
+
+ mImageView = new ImageView(context);
+ mImageView.setLeft(100);
+ mImageView.setRight(200);
+ mImageView.setImageResource(R.drawable.self_portrait_square);
+ mImageView.setScaleX(3);
+ mImageView.setScaleY(3);
+// mImageView.setRotation(145);
+
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mImageView, View.ROTATION, 0, 360);
+ anim.setRepeatCount(ValueAnimator.INFINITE);
+ anim.setDuration(5000);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ invalidate();
+ }
+ });
+ anim.start();
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(128, 128);
+ params.addRule(RelativeLayout.CENTER_IN_PARENT);
+ addView(mImageView, params);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas)
+ {
+ super.onDraw(canvas);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ mImageView.getHitRect(mRect);
+ canvas.drawRect(mRect, mPaint);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java b/tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java
new file mode 100644
index 0000000..cf4ea9a
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+
+import static android.widget.RelativeLayout.ALIGN_PARENT_LEFT;
+import static android.widget.RelativeLayout.ALIGN_PARENT_RIGHT;
+import static android.widget.RelativeLayout.LayoutParams;
+
+public class InstanceTargets extends Activity {
+
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.instance_targets);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container;
+ }
+
+ public void sendMessage(final View view) {
+ TransitionManager.go(mSceneRoot, new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < mSceneRoot.getChildCount(); ++i) {
+ Button button = (Button) mSceneRoot.getChildAt(i);
+ LayoutParams params = (LayoutParams) button.getLayoutParams();
+ int rules[] = params.getRules();
+ if (rules[ALIGN_PARENT_RIGHT] != 0) {
+ params.removeRule(ALIGN_PARENT_RIGHT);
+ params.addRule(ALIGN_PARENT_LEFT);
+ } else {
+ params.removeRule(ALIGN_PARENT_LEFT);
+ params.addRule(ALIGN_PARENT_RIGHT);
+ }
+ button.setLayoutParams(params);
+ }
+ }
+ }, new Move().setTargets(view));
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/InterruptionTest.java b/tests/TransitionTests/src/com/android/transitiontests/InterruptionTest.java
new file mode 100644
index 0000000..b380225
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/InterruptionTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.AutoTransition;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TextChange;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.RadioButton;
+
+public class InterruptionTest extends Activity {
+
+ RadioButton mScene1RB, mScene2RB, mScene3RB, mScene4RB;
+ private Scene mScene1;
+ private Scene mScene2;
+ private Scene mScene3;
+ private Scene mScene4;
+ TransitionGroup mSequencedMove = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.interruption);
+
+ ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.sceneRoot);
+
+ mScene1 = new Scene(sceneRoot, R.layout.interruption_inner_1, this);
+ mScene2 = new Scene(sceneRoot, R.layout.interruption_inner_2, this);
+ mScene3 = new Scene(sceneRoot, R.layout.interruption_inner_3, this);
+ mScene4 = new Scene(sceneRoot, R.layout.interruption_inner_4, this);
+
+ mScene1RB = (RadioButton) findViewById(R.id.scene1RB);
+ mScene2RB = (RadioButton) findViewById(R.id.scene2RB);
+ mScene3RB = (RadioButton) findViewById(R.id.scene3RB);
+ mScene4RB = (RadioButton) findViewById(R.id.scene4RB);
+
+ sceneRoot.setCurrentScene(mScene1);
+
+ Move move1 = new Move();
+ move1.setTargetIds(R.id.button);
+ Move move2 = new Move();
+ move2.setTargetIds(R.id.button1);
+
+ mSequencedMove.addTransitions(move1, move2);
+ mSequencedMove.setDuration(1000);
+ }
+
+ public void onRadioButtonClicked(View clickedButton) {
+ if (clickedButton == mScene1RB) {
+ TransitionManager.go(mScene1, mSequencedMove);
+ } else if (clickedButton == mScene2RB) {
+ TransitionManager.go(mScene2, mSequencedMove);
+ } else if (clickedButton == mScene3RB) {
+ TransitionManager.go(mScene3, mSequencedMove);
+ } else {
+ TransitionManager.go(mScene4, mSequencedMove);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java
new file mode 100644
index 0000000..87ee874
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.transition.Fade;
+import android.view.transition.Scene;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.view.transition.AutoTransition;
+import android.view.transition.Move;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ListViewAddRemove extends Activity {
+
+ final ArrayList<String> numList = new ArrayList<String>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.list_view_add_remove);
+
+ final LinearLayout container = (LinearLayout) findViewById(R.id.container);
+
+ final ListView listview = (ListView) findViewById(R.id.listview);
+ for (int i = 0; i < 200; ++i) {
+ numList.add(Integer.toString(i));
+ }
+ final StableArrayAdapter adapter = new StableArrayAdapter(this,
+ android.R.layout.simple_list_item_1, numList);
+ listview.setAdapter(adapter);
+
+ final ViewTreeObserver observer = container.getViewTreeObserver();
+ observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ public void onGlobalLayout() {
+ System.out.println("-------------------------------------");
+ System.out.println("onLayoutListener: listview view tops: ");
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView view = (TextView) listview.getChildAt(i);
+ System.out.println(" " + view.getText() + ": " + view.getTop());
+ }
+ }
+ });
+
+ final Scene mySceneChanger = new Scene(listview);
+
+ mySceneChanger.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ numList.remove(mItemToDelete);
+ adapter.notifyDataSetChanged();
+ }
+ });
+ final Transition myTransition = new AutoTransition();
+ final TransitionGroup noFadeIn = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ Fade fadeIn = new Fade(Fade.IN);
+ fadeIn.setDuration(50);
+ noFadeIn.addTransitions(new Fade(Fade.OUT), new Move(), fadeIn);
+
+ myTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ System.out.println("---------ListView Tops: Before--------");
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView view = (TextView) listview.getChildAt(i);
+ int position = listview.getPositionForView(view);
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ System.out.println("---------ListView Tops: After--------");
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView view = (TextView) listview.getChildAt(i);
+ int position = listview.getPositionForView(view);
+ if (view.hasTransientState()) {
+// view.setHasTransientState(false);
+ }
+ }
+ myTransition.removeListener(this);
+ }
+ });
+
+ listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
+ System.out.println("---------ListView Tops: OnClick--------");
+ String item = (String) parent.getItemAtPosition(position);
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView v = (TextView) listview.getChildAt(i);
+ if (!item.equals(v.getText())) {
+// v.setHasTransientState(true);
+ }
+ }
+// listview.setHasTransientState(true);
+ mItemToDelete = item;
+// numList.remove(item);
+ TransitionManager.go(mySceneChanger, noFadeIn);
+// view.postDelayed(new Runnable() {
+// @Override
+// public void run() {
+// for (int i = 0; i < listview.getChildCount(); ++i) {
+// TextView v = (TextView) listview.getChildAt(i);
+// v.setHasTransientState(false);
+// }
+// }
+// }, 200);
+ }
+
+ });
+ }
+
+ String mItemToDelete = null;
+
+ private class StableArrayAdapter extends ArrayAdapter<String> {
+
+ HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
+
+ public StableArrayAdapter(Context context, int textViewResourceId,
+ List<String> objects) {
+ super(context, textViewResourceId, objects);
+ for (int i = 0; i < objects.size(); ++i) {
+ mIdMap.put(objects.get(i), i);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ String item = getItem(position);
+ return mIdMap.get(item);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java
new file mode 100644
index 0000000..4eeba4d
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ListViewAddRemoveNoTransition extends Activity {
+
+ final ArrayList<String> numList = new ArrayList<String>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.list_view_add_remove);
+
+ final LinearLayout container = (LinearLayout) findViewById(R.id.container);
+
+ final ListView listview = (ListView) findViewById(R.id.listview);
+ for (int i = 0; i < 200; ++i) {
+ numList.add(Integer.toString(i));
+ }
+ final StableArrayAdapter adapter = new StableArrayAdapter(this,
+ android.R.layout.simple_list_item_1, numList);
+ listview.setAdapter(adapter);
+
+ listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
+ String item = (String) parent.getItemAtPosition(position);
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView v = (TextView) listview.getChildAt(i);
+ if (!item.equals(v.getText())) {
+ v.setHasTransientState(true);
+ }
+ }
+ numList.remove(item);
+ adapter.notifyDataSetChanged();
+ view.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView v = (TextView) listview.getChildAt(i);
+ v.setHasTransientState(false);
+ }
+ }
+ }, 200);
+ }
+
+ });
+ }
+
+ private class StableArrayAdapter extends ArrayAdapter<String> {
+
+ HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
+
+ public StableArrayAdapter(Context context, int textViewResourceId,
+ List<String> objects) {
+ super(context, textViewResourceId, objects);
+ for (int i = 0; i < objects.size(); ++i) {
+ mIdMap.put(objects.get(i), i);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ String item = getItem(position);
+ return mIdMap.get(item);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java b/tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java
new file mode 100644
index 0000000..06fa9f4
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.TextView;
+import android.view.transition.Fade;
+import android.view.transition.Recolor;
+import android.view.transition.Slide;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+
+public class LoginActivity extends Activity {
+ ViewGroup mSceneRoot;
+ Scene mCurrentScene;
+ TransitionManager mTransitionManager;
+ Scene mLoginScene, mPasswordScene, mIncorrectPasswordScene, mSuccessScene, mUsernameTakenScene,
+ mNewUserScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mLoginScene = new Scene(mSceneRoot, R.layout.activity_login, this);
+ mPasswordScene = new Scene(mSceneRoot, R.layout.login_password, this);
+ mIncorrectPasswordScene = new Scene(mSceneRoot, R.layout.incorrect_password, this);
+ mUsernameTakenScene = new Scene(mSceneRoot, R.layout.username_taken, this);
+ mSuccessScene = new Scene(mSceneRoot, R.layout.success, this);
+ mNewUserScene = new Scene(mSceneRoot, R.layout.new_user, this);
+
+ mTransitionManager = new TransitionManager();
+
+ // Custom transitions in/out of NewUser screen - slide in the 2nd password UI
+ TransitionGroup slider = new TransitionGroup();
+ slider.addTransitions(new Slide().setTargetIds(R.id.retype, R.id.retypeEdit));
+ slider.addTransitions(new Recolor().setTargetIds(R.id.password, R.id.passwordEdit));
+ slider.addTransitions(new Fade());
+ mTransitionManager.setTransition(mLoginScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mPasswordScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mLoginScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mPasswordScene, slider);
+
+ // Custom transitions with recoloring password field
+ Transition colorizer = new Recolor().setTargetIds(R.id.password, R.id.passwordEdit);
+ mTransitionManager.setTransition(mLoginScene, mPasswordScene, colorizer);
+ mTransitionManager.setTransition(mPasswordScene, mLoginScene, colorizer);
+
+ mCurrentScene = mLoginScene;
+ mSceneRoot.setCurrentScene(mLoginScene);
+ }
+
+ public void applyScene(Scene scene) {
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = scene;
+ }
+
+ public void sendMessage(View view) {
+ TextView textView = (TextView) view;
+ CharSequence text = textView.getText();
+ if (text.equals("Cancel")) {
+ applyScene(mLoginScene);
+ } else if (text.equals("Submit")) {
+ if (mCurrentScene == mLoginScene) {
+ applyScene(mPasswordScene);
+ } else if (mCurrentScene == mPasswordScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mIncorrectPasswordScene);
+ } else if (mCurrentScene == mNewUserScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mUsernameTakenScene);
+ }
+ } else if (text.equals("New User?")) {
+ applyScene(mNewUserScene);
+ } else if (text.equals("Okay")) {
+ if (mCurrentScene == mIncorrectPasswordScene) {
+ applyScene(mPasswordScene);
+ } else { // username taken scene
+ applyScene(mNewUserScene);
+ }
+ } else if (text.equals("Reset")) {
+ applyScene(mLoginScene);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java b/tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java
new file mode 100644
index 0000000..ff617aa
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.TransitionInflater;
+import android.widget.TextView;
+import android.view.transition.TransitionManager;
+
+
+public class LoginActivityFromResources extends Activity {
+ ViewGroup mSceneRoot;
+ Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ Scene mLoginScene, mPasswordScene, mIncorrectPasswordScene, mSuccessScene, mUsernameTakenScene,
+ mNewUserScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ }
+
+ public void applyScene(Scene scene) {
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = scene;
+ }
+
+ public void sendMessage(View view) {
+ if (mTransitionManager == null) {
+ TransitionInflater inflater = TransitionInflater.from(this);
+
+ mLoginScene = inflater.inflateScene(R.scene.login_scene, mSceneRoot);
+ mPasswordScene = inflater.inflateScene(R.scene.password_scene, mSceneRoot);
+ mIncorrectPasswordScene =
+ inflater.inflateScene(R.scene.incorrect_password_scene,mSceneRoot);
+ mUsernameTakenScene =
+ inflater.inflateScene(R.scene.username_taken_scene, mSceneRoot);
+ mSuccessScene = inflater.inflateScene(R.scene.success_scene, mSceneRoot);
+ mNewUserScene = inflater.inflateScene(R.scene.new_user_scene, mSceneRoot);
+
+ mTransitionManager =
+ inflater.inflateTransitionManager(R.transition.login_transition_mgr,
+ mSceneRoot);
+
+ mCurrentScene = mLoginScene;
+ mSceneRoot.setCurrentScene(mLoginScene);
+ }
+ TextView textView = (TextView) view;
+ CharSequence text = textView.getText();
+ if (text.equals("Cancel")) {
+ applyScene(mLoginScene);
+ } else if (text.equals("Submit")) {
+ if (mCurrentScene == mLoginScene) {
+ applyScene(mPasswordScene);
+ } else if (mCurrentScene == mPasswordScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mIncorrectPasswordScene);
+ } else if (mCurrentScene == mNewUserScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mUsernameTakenScene);
+ }
+ } else if (text.equals("New User?")) {
+ applyScene(mNewUserScene);
+ } else if (text.equals("Okay")) {
+ if (mCurrentScene == mIncorrectPasswordScene) {
+ applyScene(mPasswordScene);
+ } else { // username taken scene
+ applyScene(mNewUserScene);
+ }
+ } else if (text.equals("Reset")) {
+ applyScene(mLoginScene);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java b/tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java
new file mode 100644
index 0000000..ef8cd37
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+
+public class OverlayTest extends Activity {
+
+ ViewGroup mContainer;
+ ViewGroup mRoot;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.overlay_test);
+
+ mContainer = (ViewGroup) findViewById(R.id.container);
+ mRoot = (ViewGroup) mContainer.getParent();
+ }
+
+ public void onClick(View view) {
+ final Button fadingButton = (Button) findViewById(R.id.fadingButton);
+ if (fadingButton != null) {
+ mContainer.removeView(fadingButton);
+ mRoot.getOverlay().add(fadingButton);
+ fadingButton.animate().alpha(0).setDuration(1000).withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ fadingButton.setAlpha(1);
+ mRoot.getOverlay().remove(fadingButton);
+ mContainer.addView(fadingButton);
+ }
+ });
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Reparenting.java b/tests/TransitionTests/src/com/android/transitiontests/Reparenting.java
new file mode 100644
index 0000000..e559c72
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Reparenting.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+
+public class Reparenting extends Activity {
+
+ ViewGroup mSceneRoot;
+ ViewGroup mContainer1, mContainer2;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.reparenting);
+
+ ViewGroup container = (ViewGroup) findViewById(R.id.container);
+ mContainer1 = (ViewGroup) findViewById(R.id.container1);
+ mContainer2 = (ViewGroup) findViewById(R.id.container2);
+ System.out.println("container 1 and 2 " + mContainer1 + ", " + mContainer2);
+
+ setupButtons(0, mContainer1);
+ setupButtons(3, mContainer2);
+
+ mSceneRoot = container;
+ }
+
+ private void setupButtons(int startIndex, ViewGroup parent) {
+ for (int i = startIndex; i < (startIndex + 3); ++i) {
+ Button button = new Button(this);
+ button.setText(Integer.toString(i));
+ button.setOnClickListener(mButtonListener);
+ parent.addView(button);
+ }
+ }
+
+ private View.OnClickListener mButtonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ Scene newScene = new Scene(mSceneRoot);
+ newScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ ViewGroup oldParent = (ViewGroup) v.getParent();
+ ViewGroup newParent = oldParent == mContainer1 ? mContainer2 : mContainer1;
+ oldParent.removeView(v);
+ newParent.addView(v);
+ }
+ });
+ Move reparent = new Move();
+ reparent.setReparent(true);
+ TransitionManager.go(newScene, reparent);
+ }
+ };
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java b/tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java
new file mode 100644
index 0000000..3d7bd9b
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.TransitionInflater;
+import android.view.transition.Transition;
+import android.view.transition.TransitionManager;
+
+
+public class ResourceLoadingTest extends Activity {
+
+ private static final int SEARCH_SCREEN = 0;
+ private static final int RESULTS_SCREEN = 1;
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ TransitionInflater mInflater;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mCurrentScene = SEARCH_SCREEN;
+
+ mInflater = TransitionInflater.from(this);
+ }
+
+ public void sendMessage(View view) {
+ if (mTransitionManager == null) {
+ try {
+ TransitionInflater inflater = TransitionInflater.from(this);
+ mTransitionManager =
+ inflater.inflateTransitionManager(R.transition.my_transition_mgr,
+ mSceneRoot);
+ Scene loadedScene = inflater.inflateScene(R.scene.my_scene, mSceneRoot);
+ System.out.println("loadedScene = " + loadedScene);
+ Transition loadedTransition = inflater.inflateTransition(R.transition.my_transition);
+ System.out.println("loadedTransition = " + loadedTransition);
+ } catch (Exception e) {
+ System.out.println("Problem loading scene resource: " + e);
+ }
+ }
+ if (mCurrentScene == RESULTS_SCREEN) {
+ Scene scene = mInflater.inflateScene(R.scene.search_scene, mSceneRoot);
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = SEARCH_SCREEN;
+ } else {
+ Scene scene = mInflater.inflateScene(R.scene.results_scene, mSceneRoot);
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = RESULTS_SCREEN;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java
new file mode 100644
index 0000000..374a9ab
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+
+public class ScenesTestAutoTargets extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade(), new Move(), new Recolor());
+
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java
new file mode 100644
index 0000000..fb1ba07
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.AutoTransition;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.view.transition.TransitionManager;
+
+
+public class ScenesTestAutoTransition extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Transition mTransition = new AutoTransition();
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ TransitionManager.go(mSearchScreen, mTransition);
+ mCurrentScene = mSearchScreen;
+ } else {
+ TransitionManager.go(mResultsScreen, mTransition);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java
new file mode 100644
index 0000000..c75d419
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+
+
+public class ScenesTestAutoTransition2 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ TransitionManager.go(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ TransitionManager.go(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java
new file mode 100644
index 0000000..399c2be
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+
+public class ScenesTestv21 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transitionToResults = new TransitionGroup();
+ Fade fade = new Fade();
+ fade.setTargetIds(R.id.resultsText, R.id.resultsList);
+ fade.setStartDelay(300);
+ transitionToResults.addTransitions(fade);
+ transitionToResults.addTransitions(new Move().setTargetIds(R.id.searchContainer));
+ transitionToResults.addTransitions(new Recolor().setTargetIds(R.id.container));
+
+ TransitionGroup transitionToSearch = new TransitionGroup();
+ transitionToSearch.addTransitions(new Fade().setTargetIds(R.id.resultsText, R.id.resultsList));
+ transitionToSearch.addTransitions(new Move().setTargetIds(R.id.searchContainer));
+ transitionToSearch.addTransitions(new Recolor().setTargetIds(R.id.container));
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transitionToSearch);
+ mTransitionManager.setTransition(mResultsScreen, transitionToResults);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java b/tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java
new file mode 100644
index 0000000..6b34420
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+
+public class SequenceTest extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ TransitionGroup sequencedFade, reverseSequencedFade;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_test);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+ mInvisibleButton = (Button) findViewById(R.id.invisibleButton);
+ mGoneButton = (Button) findViewById(R.id.goneButton);
+
+ mScene1 = new Scene(mSceneRoot, R.layout.fading_test, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.fading_test_scene_2, this);
+
+ Transition fade1 = new Fade().setTargetIds(R.id.removingButton);
+ Transition fade2 = new Fade().setTargetIds(R.id.invisibleButton);
+ Transition fade3 = new Fade().setTargetIds(R.id.goneButton);
+ TransitionGroup fader = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ fader.addTransitions(fade1, fade2, fade3, new Move());
+ sequencedFade = fader;
+
+ reverseSequencedFade = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ reverseSequencedFade.addTransitions(new Move(), fade3, fade2, fade1);
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, sequencedFade);
+ } else {
+ TransitionManager.go(mScene1, reverseSequencedFade);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java b/tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java
new file mode 100644
index 0000000..972d30e
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+
+
+public class SequenceTestSimple extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ Transition sequencedFade;
+ TransitionGroup sequencedFadeReverse;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_test_simple);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+
+ mScene1 = new Scene(mSceneRoot, R.layout.fading_test_simple, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.fading_test_simple2, this);
+
+ TransitionGroup fader = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ fader.addTransitions(new Fade().setTargetIds(R.id.removingButton));
+ fader.addTransitions(new Move().setTargetIds(R.id.sceneSwitchButton));
+ sequencedFade = fader;
+
+ sequencedFadeReverse = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ sequencedFadeReverse.addTransitions(new Move().setTargetIds(R.id.sceneSwitchButton));
+ sequencedFadeReverse.addTransitions(new Fade().setTargetIds(R.id.removingButton));
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, sequencedFade);
+ } else {
+ TransitionManager.go(mScene1, sequencedFadeReverse);
+ }
+ }}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java b/tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java
new file mode 100644
index 0000000..aa76923
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+
+import static android.widget.LinearLayout.LayoutParams;
+
+public class SurfaceAndTextureViews extends Activity {
+
+ SimpleView mView;
+ SimpleSurfaceView mSurfaceView;
+ SimpleTextureView mTextureView;
+ private static final int SMALL_SIZE = 200;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.surface_texture_views);
+
+ final ViewGroup container = (ViewGroup) findViewById(R.id.container);
+ Button toggleButton = (Button) findViewById(R.id.toggleButton);
+
+ mView = new SimpleView(this);
+ mView.setId(0);
+ mView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ container.addView(mView);
+
+ mSurfaceView = new SimpleSurfaceView(this);
+ mSurfaceView.setId(1);
+ mSurfaceView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ container.addView(mSurfaceView);
+
+ mTextureView = new SimpleTextureView(this);
+ mTextureView.setId(2);
+ mTextureView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ container.addView(mTextureView);
+
+ final TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Move(), new Crossfade().setTargetIds(0, 1, 2));
+
+ toggleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Scene newScene = new Scene(container);
+ newScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ if (mView.getWidth() <= SMALL_SIZE) {
+ mView.setLayoutParams(new LayoutParams(SMALL_SIZE * 2, SMALL_SIZE));
+ mSurfaceView.setLayoutParams(new LayoutParams(SMALL_SIZE * 2, SMALL_SIZE));
+ mTextureView.setLayoutParams(new LayoutParams(SMALL_SIZE * 2, SMALL_SIZE));
+ mView.mColor = SimpleView.LARGE_COLOR;
+ mSurfaceView.mColor = SimpleSurfaceView.LARGE_COLOR;
+ mTextureView.mColor = SimpleTextureView.LARGE_COLOR;
+ } else {
+ mView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ mSurfaceView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ mTextureView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ mView.mColor = SimpleView.SMALL_COLOR;
+ mSurfaceView.mColor = SimpleSurfaceView.SMALL_COLOR;
+ mTextureView.mColor = SimpleTextureView.SMALL_COLOR;
+ }
+ }
+ });
+ TransitionManager.go(newScene, transition);
+ }
+ });
+
+ }
+
+ static private class SimpleView extends View {
+ static final int SMALL_COLOR = Color.BLUE;
+ static final int LARGE_COLOR = Color.YELLOW;
+ int mColor = SMALL_COLOR;
+
+ private SimpleView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawColor(mColor);
+ }
+ }
+
+ static private class SimpleSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+
+ static final int SMALL_COLOR = Color.GREEN;
+ static final int LARGE_COLOR = Color.GRAY;
+ int mColor = SMALL_COLOR;
+ SurfaceHolder mHolder = null;
+
+ private SimpleSurfaceView(Context context) {
+ super(context);
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ }
+
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ System.out.println("surfaceCreated");
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ System.out.println("surfaceChanged: w h = " + width + ", " + height);
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(mColor);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ System.out.println("surfaceDestroyed");
+ }
+ }
+
+ static private class SimpleTextureView extends TextureView implements TextureView.SurfaceTextureListener {
+
+ static final int SMALL_COLOR = Color.RED;
+ static final int LARGE_COLOR = Color.CYAN;
+ int mColor = SMALL_COLOR;
+
+ private SimpleTextureView(Context context) {
+ super(context);
+ setSurfaceTextureListener(this);
+ }
+
+ private SimpleTextureView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setSurfaceTextureListener(this);
+ }
+
+ private SimpleTextureView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setSurfaceTextureListener(this);
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ System.out.println("SurfaceTexture available");
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ System.out.println("SurfaceTexture size changed to " + width + ", " + height);
+ Canvas canvas = lockCanvas();
+ canvas.drawColor(mColor);
+ unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ return false;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ System.out.println("SurfaceTexture updated");
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java b/tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java
new file mode 100644
index 0000000..433a91c4
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.view.transition.TransitionManager;
+
+
+import java.util.HashMap;
+
+public class UniqueIds extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ HashMap<Button, ToggleScene> mSceneMap = new HashMap<Button, ToggleScene>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.unique_id_test);
+
+ LinearLayout container = (LinearLayout) findViewById(R.id.container);
+ LayoutInflater inflater = getLayoutInflater();
+ Button button = (Button) inflater.inflate(R.layout.button_template, null);
+ container.addView(button);
+ ToggleScene scene = new ToggleScene(container, button);
+ mSceneMap.put(button, scene);
+ button = (Button) inflater.inflate(R.layout.button_template, null);
+ container.addView(button);
+ scene = new ToggleScene(container, button);
+ mSceneMap.put(button, scene);
+ }
+
+ public void sendMessage(View view) {
+ mSceneMap.get(view).changeToScene();
+ }
+
+ class ToggleScene {
+ Scene mScene;
+ Transition mTransition;
+ Button mButton;
+
+ ToggleScene(ViewGroup rootView, Button button) {
+ mScene = new Scene(rootView);
+ mButton = button;
+ mScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ if (mButton.getLeft() == 0) {
+ mButton.offsetLeftAndRight(500);
+ } else {
+ int width = mButton.getWidth();
+ mButton.setLeft(0);
+ mButton.setRight(width);
+ }
+ }
+ });
+ }
+
+ void changeToScene() {
+ TransitionManager.go(mScene);
+ }
+ }
+}
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 746ac06..53318a4 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
}
try {
- mWm.addAppToken(0, null, 0, 0, false, false);
+ mWm.addAppToken(0, null, 0, 0, 0, false, false);
fail("IWindowManager.addAppToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -164,7 +164,7 @@
}
try {
- mWm.setAppStartingWindow(null, "foo", 0, null, null, 0, 0, 0, null, false);
+ mWm.setAppStartingWindow(null, "foo", 0, null, null, 0, 0, 0, 0, null, false);
fail("IWindowManager.setAppStartingWindow did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -222,37 +222,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
- try {
- mWm.moveAppToken(0, null);
- fail("IWindowManager.moveAppToken did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
- mWm.moveAppTokensToTop(null);
- fail("IWindowManager.moveAppTokensToTop did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
- mWm.moveAppTokensToBottom(null);
- fail("IWindowManager.moveAppTokensToBottom did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
- }
+ }
@SmallTest
public void testDISABLE_KEYGUARD() {
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 9b1658a..452c60a 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -7,10 +7,8 @@
# This tool is prebuilt if we're doing an app-only build.
ifeq ($(TARGET_BUILD_APPS),)
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
+aapt_src_files := \
AaptAssets.cpp \
Command.cpp \
CrunchCache.cpp \
@@ -24,23 +22,29 @@
ResourceTable.cpp \
Images.cpp \
Resource.cpp \
+ pseudolocalize.cpp \
SourcePos.cpp \
+ WorkQueue.cpp \
ZipEntry.cpp \
- ZipFile.cpp
+ ZipFile.cpp \
+ qsort_r_compat.c
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(aapt_src_files)
LOCAL_CFLAGS += -Wno-format-y2k
ifeq (darwin,$(HOST_OS))
LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
endif
+LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
LOCAL_C_INCLUDES += external/libpng
LOCAL_C_INCLUDES += external/zlib
-LOCAL_C_INCLUDES += build/libs/host/include
LOCAL_STATIC_LIBRARIES := \
- libhost \
libandroidfw \
libutils \
libcutils \
@@ -64,4 +68,36 @@
include $(BUILD_HOST_EXECUTABLE)
+# aapt for running on the device
+# =========================================================
+ifneq ($(SDK_ONLY),true)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(aapt_src_files)
+
+LOCAL_MODULE := aapt
+
+LOCAL_C_INCLUDES += bionic
+LOCAL_C_INCLUDES += bionic/libstdc++/include
+LOCAL_C_INCLUDES += external/stlport/stlport
+LOCAL_C_INCLUDES += external/libpng
+LOCAL_C_INCLUDES += external/zlib
+
+LOCAL_CFLAGS += -Wno-non-virtual-dtor
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroidfw \
+ libutils \
+ libcutils \
+ libpng \
+ liblog \
+ libz
+
+LOCAL_STATIC_LIBRARIES := \
+ libstlport_static \
+ libexpat_static
+
+include $(BUILD_EXECUTABLE)
+endif
+
endif # TARGET_BUILD_APPS
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 3930117..872d95c 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -8,10 +8,13 @@
#include "ResourceTable.h"
#include "ResourceFilter.h"
+#include <androidfw/misc.h>
+
#include <utils/Log.h>
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Errors.h>
+#include <utils/misc.h>
#include <sys/types.h>
#include <dirent.h>
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 6168bbd..d617928 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -14,7 +14,7 @@
#include "FileFinder.h"
#include "CacheUpdater.h"
-#include <utils/WorkQueue.h>
+#include "WorkQueue.h"
#if HAVE_PRINTF_ZD
# define ZD "%zd"
@@ -172,6 +172,7 @@
bool isValidResourceType(const String8& type)
{
return type == "anim" || type == "animator" || type == "interpolator"
+ || type == "transition" || type == "scene"
|| type == "drawable" || type == "layout"
|| type == "values" || type == "xml" || type == "raw"
|| type == "color" || type == "menu" || type == "mipmap";
@@ -932,6 +933,8 @@
sp<ResourceTypeSet> anims;
sp<ResourceTypeSet> animators;
sp<ResourceTypeSet> interpolators;
+ sp<ResourceTypeSet> transitions;
+ sp<ResourceTypeSet> scenes;
sp<ResourceTypeSet> xmls;
sp<ResourceTypeSet> raws;
sp<ResourceTypeSet> colors;
@@ -943,6 +946,8 @@
ASSIGN_IT(anim);
ASSIGN_IT(animator);
ASSIGN_IT(interpolator);
+ ASSIGN_IT(transition);
+ ASSIGN_IT(scene);
ASSIGN_IT(xml);
ASSIGN_IT(raw);
ASSIGN_IT(color);
@@ -965,6 +970,8 @@
!applyFileOverlay(bundle, assets, &anims, "anim") ||
!applyFileOverlay(bundle, assets, &animators, "animator") ||
!applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
+ !applyFileOverlay(bundle, assets, &transitions, "transition") ||
+ !applyFileOverlay(bundle, assets, &scenes, "scene") ||
!applyFileOverlay(bundle, assets, &xmls, "xml") ||
!applyFileOverlay(bundle, assets, &raws, "raw") ||
!applyFileOverlay(bundle, assets, &colors, "color") ||
@@ -1024,6 +1031,20 @@
}
}
+ if (transitions != NULL) {
+ err = makeFileResources(bundle, assets, &table, transitions, "transition");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (scenes != NULL) {
+ err = makeFileResources(bundle, assets, &table, scenes, "scene");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
if (interpolators != NULL) {
err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
if (err != NO_ERROR) {
@@ -1168,6 +1189,36 @@
err = NO_ERROR;
}
+ if (transitions != NULL) {
+ ResourceDirIterator it(transitions, String8("transition"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
+ hasErrors = true;
+ }
+ err = NO_ERROR;
+ }
+
+ if (scenes != NULL) {
+ ResourceDirIterator it(scenes, String8("scene"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
+ hasErrors = true;
+ }
+ err = NO_ERROR;
+ }
+
if (xmls != NULL) {
ResourceDirIterator it(xmls, String8("xml"));
while ((err=it.next()) == NO_ERROR) {
diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp
index 839eda5..158b391 100644
--- a/tools/aapt/StringPool.cpp
+++ b/tools/aapt/StringPool.cpp
@@ -9,7 +9,7 @@
#include <utils/ByteOrder.h>
#include <utils/SortedVector.h>
-#include <cutils/qsort_r_compat.h>
+#include "qsort_r_compat.h"
#if HAVE_PRINTF_ZD
# define ZD "%zd"
diff --git a/tools/aapt/StringPool.h b/tools/aapt/StringPool.h
index 16050b2..1b3abfd 100644
--- a/tools/aapt/StringPool.h
+++ b/tools/aapt/StringPool.h
@@ -12,7 +12,6 @@
#include <androidfw/ResourceTypes.h>
#include <utils/String16.h>
-#include <utils/TextOutput.h>
#include <utils/TypeHelpers.h>
#include <sys/types.h>
diff --git a/tools/aapt/WorkQueue.cpp b/tools/aapt/WorkQueue.cpp
new file mode 100644
index 0000000..24a962f
--- /dev/null
+++ b/tools/aapt/WorkQueue.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "WorkQueue"
+
+#include <utils/Log.h>
+#include "WorkQueue.h"
+
+namespace android {
+
+// --- WorkQueue ---
+
+WorkQueue::WorkQueue(size_t maxThreads, bool canCallJava) :
+ mMaxThreads(maxThreads), mCanCallJava(canCallJava),
+ mCanceled(false), mFinished(false), mIdleThreads(0) {
+}
+
+WorkQueue::~WorkQueue() {
+ if (!cancel()) {
+ finish();
+ }
+}
+
+status_t WorkQueue::schedule(WorkUnit* workUnit, size_t backlog) {
+ AutoMutex _l(mLock);
+
+ if (mFinished || mCanceled) {
+ return INVALID_OPERATION;
+ }
+
+ if (mWorkThreads.size() < mMaxThreads
+ && mIdleThreads < mWorkUnits.size() + 1) {
+ sp<WorkThread> workThread = new WorkThread(this, mCanCallJava);
+ status_t status = workThread->run("WorkQueue::WorkThread");
+ if (status) {
+ return status;
+ }
+ mWorkThreads.add(workThread);
+ mIdleThreads += 1;
+ } else if (backlog) {
+ while (mWorkUnits.size() >= mMaxThreads * backlog) {
+ mWorkDequeuedCondition.wait(mLock);
+ if (mFinished || mCanceled) {
+ return INVALID_OPERATION;
+ }
+ }
+ }
+
+ mWorkUnits.add(workUnit);
+ mWorkChangedCondition.broadcast();
+ return OK;
+}
+
+status_t WorkQueue::cancel() {
+ AutoMutex _l(mLock);
+
+ return cancelLocked();
+}
+
+status_t WorkQueue::cancelLocked() {
+ if (mFinished) {
+ return INVALID_OPERATION;
+ }
+
+ if (!mCanceled) {
+ mCanceled = true;
+
+ size_t count = mWorkUnits.size();
+ for (size_t i = 0; i < count; i++) {
+ delete mWorkUnits.itemAt(i);
+ }
+ mWorkUnits.clear();
+ mWorkChangedCondition.broadcast();
+ mWorkDequeuedCondition.broadcast();
+ }
+ return OK;
+}
+
+status_t WorkQueue::finish() {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mFinished) {
+ return INVALID_OPERATION;
+ }
+
+ mFinished = true;
+ mWorkChangedCondition.broadcast();
+ } // release lock
+
+ // It is not possible for the list of work threads to change once the mFinished
+ // flag has been set, so we can access mWorkThreads outside of the lock here.
+ size_t count = mWorkThreads.size();
+ for (size_t i = 0; i < count; i++) {
+ mWorkThreads.itemAt(i)->join();
+ }
+ mWorkThreads.clear();
+ return OK;
+}
+
+bool WorkQueue::threadLoop() {
+ WorkUnit* workUnit;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (;;) {
+ if (mCanceled) {
+ return false;
+ }
+
+ if (!mWorkUnits.isEmpty()) {
+ workUnit = mWorkUnits.itemAt(0);
+ mWorkUnits.removeAt(0);
+ mIdleThreads -= 1;
+ mWorkDequeuedCondition.broadcast();
+ break;
+ }
+
+ if (mFinished) {
+ return false;
+ }
+
+ mWorkChangedCondition.wait(mLock);
+ }
+ } // release lock
+
+ bool shouldContinue = workUnit->run();
+ delete workUnit;
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ mIdleThreads += 1;
+
+ if (!shouldContinue) {
+ cancelLocked();
+ return false;
+ }
+ } // release lock
+
+ return true;
+}
+
+// --- WorkQueue::WorkThread ---
+
+WorkQueue::WorkThread::WorkThread(WorkQueue* workQueue, bool canCallJava) :
+ Thread(canCallJava), mWorkQueue(workQueue) {
+}
+
+WorkQueue::WorkThread::~WorkThread() {
+}
+
+bool WorkQueue::WorkThread::threadLoop() {
+ return mWorkQueue->threadLoop();
+}
+
+}; // namespace android
diff --git a/tools/aapt/WorkQueue.h b/tools/aapt/WorkQueue.h
new file mode 100644
index 0000000..d38f05d
--- /dev/null
+++ b/tools/aapt/WorkQueue.h
@@ -0,0 +1,119 @@
+/*]
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_WORK_QUEUE_H
+#define AAPT_WORK_QUEUE_H
+
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+/*
+ * A threaded work queue.
+ *
+ * This class is designed to make it easy to run a bunch of isolated work
+ * units in parallel, using up to the specified number of threads.
+ * To use it, write a loop to post work units to the work queue, then synchronize
+ * on the queue at the end.
+ */
+class WorkQueue {
+public:
+ class WorkUnit {
+ public:
+ WorkUnit() { }
+ virtual ~WorkUnit() { }
+
+ /*
+ * Runs the work unit.
+ * If the result is 'true' then the work queue continues scheduling work as usual.
+ * If the result is 'false' then the work queue is canceled.
+ */
+ virtual bool run() = 0;
+ };
+
+ /* Creates a work queue with the specified maximum number of work threads. */
+ WorkQueue(size_t maxThreads, bool canCallJava = true);
+
+ /* Destroys the work queue.
+ * Cancels pending work and waits for all remaining threads to complete.
+ */
+ ~WorkQueue();
+
+ /* Posts a work unit to run later.
+ * If the work queue has been canceled or is already finished, returns INVALID_OPERATION
+ * and does not take ownership of the work unit (caller must destroy it itself).
+ * Otherwise, returns OK and takes ownership of the work unit (the work queue will
+ * destroy it automatically).
+ *
+ * For flow control, this method blocks when the size of the pending work queue is more
+ * 'backlog' times the number of threads. This condition reduces the rate of entry into
+ * the pending work queue and prevents it from growing much more rapidly than the
+ * work threads can actually handle.
+ *
+ * If 'backlog' is 0, then no throttle is applied.
+ */
+ status_t schedule(WorkUnit* workUnit, size_t backlog = 2);
+
+ /* Cancels all pending work.
+ * If the work queue is already finished, returns INVALID_OPERATION.
+ * If the work queue is already canceled, returns OK and does nothing else.
+ * Otherwise, returns OK, discards all pending work units and prevents additional
+ * work units from being scheduled.
+ *
+ * Call finish() after cancel() to wait for all remaining work to complete.
+ */
+ status_t cancel();
+
+ /* Waits for all work to complete.
+ * If the work queue is already finished, returns INVALID_OPERATION.
+ * Otherwise, waits for all work to complete and returns OK.
+ */
+ status_t finish();
+
+private:
+ class WorkThread : public Thread {
+ public:
+ WorkThread(WorkQueue* workQueue, bool canCallJava);
+ virtual ~WorkThread();
+
+ private:
+ virtual bool threadLoop();
+
+ WorkQueue* const mWorkQueue;
+ };
+
+ status_t cancelLocked();
+ bool threadLoop(); // called from each work thread
+
+ const size_t mMaxThreads;
+ const bool mCanCallJava;
+
+ Mutex mLock;
+ Condition mWorkChangedCondition;
+ Condition mWorkDequeuedCondition;
+
+ bool mCanceled;
+ bool mFinished;
+ size_t mIdleThreads;
+ Vector<sp<WorkThread> > mWorkThreads;
+ Vector<WorkUnit*> mWorkUnits;
+};
+
+}; // namespace android
+
+#endif // AAPT_WORK_QUEUE_H
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index dcbe7db..a663ad5 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -6,8 +6,8 @@
#include "XMLNode.h"
#include "ResourceTable.h"
+#include "pseudolocalize.h"
-#include <host/pseudolocalize.h>
#include <utils/ByteOrder.h>
#include <errno.h>
#include <string.h>
diff --git a/tools/aapt/ZipFile.cpp b/tools/aapt/ZipFile.cpp
index 3994c31..8057068 100644
--- a/tools/aapt/ZipFile.cpp
+++ b/tools/aapt/ZipFile.cpp
@@ -20,8 +20,8 @@
#define LOG_TAG "zip"
+#include <androidfw/ZipUtils.h>
#include <utils/Log.h>
-#include <utils/ZipUtils.h>
#include "ZipFile.h"
diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp
new file mode 100644
index 0000000..9e50c5a
--- /dev/null
+++ b/tools/aapt/pseudolocalize.cpp
@@ -0,0 +1,119 @@
+#include "pseudolocalize.h"
+
+using namespace std;
+
+static const char*
+pseudolocalize_char(char c)
+{
+ switch (c) {
+ case 'a': return "\xc4\x83";
+ case 'b': return "\xcf\x84";
+ case 'c': return "\xc4\x8b";
+ case 'd': return "\xc4\x8f";
+ case 'e': return "\xc4\x99";
+ case 'f': return "\xc6\x92";
+ case 'g': return "\xc4\x9d";
+ case 'h': return "\xd1\x9b";
+ case 'i': return "\xcf\x8a";
+ case 'j': return "\xc4\xb5";
+ case 'k': return "\xc4\xb8";
+ case 'l': return "\xc4\xba";
+ case 'm': return "\xe1\xb8\xbf";
+ case 'n': return "\xd0\xb8";
+ case 'o': return "\xcf\x8c";
+ case 'p': return "\xcf\x81";
+ case 'q': return "\x51";
+ case 'r': return "\xd2\x91";
+ case 's': return "\xc5\xa1";
+ case 't': return "\xd1\x82";
+ case 'u': return "\xce\xb0";
+ case 'v': return "\x56";
+ case 'w': return "\xe1\xba\x85";
+ case 'x': return "\xd1\x85";
+ case 'y': return "\xe1\xbb\xb3";
+ case 'z': return "\xc5\xba";
+ case 'A': return "\xc3\x85";
+ case 'B': return "\xce\xb2";
+ case 'C': return "\xc4\x88";
+ case 'D': return "\xc4\x90";
+ case 'E': return "\xd0\x84";
+ case 'F': return "\xce\x93";
+ case 'G': return "\xc4\x9e";
+ case 'H': return "\xc4\xa6";
+ case 'I': return "\xd0\x87";
+ case 'J': return "\xc4\xb5";
+ case 'K': return "\xc4\xb6";
+ case 'L': return "\xc5\x81";
+ case 'M': return "\xe1\xb8\xbe";
+ case 'N': return "\xc5\x83";
+ case 'O': return "\xce\x98";
+ case 'P': return "\xcf\x81";
+ case 'Q': return "\x71";
+ case 'R': return "\xd0\xaf";
+ case 'S': return "\xc8\x98";
+ case 'T': return "\xc5\xa6";
+ case 'U': return "\xc5\xa8";
+ case 'V': return "\xce\xbd";
+ case 'W': return "\xe1\xba\x84";
+ case 'X': return "\xc3\x97";
+ case 'Y': return "\xc2\xa5";
+ case 'Z': return "\xc5\xbd";
+ default: return NULL;
+ }
+}
+
+/**
+ * Converts characters so they look like they've been localized.
+ *
+ * Note: This leaves escape sequences untouched so they can later be
+ * processed by ResTable::collectString in the normal way.
+ */
+string
+pseudolocalize_string(const string& source)
+{
+ const char* s = source.c_str();
+ string result;
+ const size_t I = source.length();
+ for (size_t i=0; i<I; i++) {
+ char c = s[i];
+ if (c == '\\') {
+ if (i<I-1) {
+ result += '\\';
+ i++;
+ c = s[i];
+ switch (c) {
+ case 'u':
+ // this one takes up 5 chars
+ result += string(s+i, 5);
+ i += 4;
+ break;
+ case 't':
+ case 'n':
+ case '#':
+ case '@':
+ case '?':
+ case '"':
+ case '\'':
+ case '\\':
+ default:
+ result += c;
+ break;
+ }
+ } else {
+ result += c;
+ }
+ } else {
+ const char* p = pseudolocalize_char(c);
+ if (p != NULL) {
+ result += p;
+ } else {
+ result += c;
+ }
+ }
+ }
+
+ //printf("result=\'%s\'\n", result.c_str());
+ return result;
+}
+
+
diff --git a/tools/aapt/pseudolocalize.h b/tools/aapt/pseudolocalize.h
new file mode 100644
index 0000000..94cb034
--- /dev/null
+++ b/tools/aapt/pseudolocalize.h
@@ -0,0 +1,9 @@
+#ifndef HOST_PSEUDOLOCALIZE_H
+#define HOST_PSEUDOLOCALIZE_H
+
+#include <string>
+
+std::string pseudolocalize_string(const std::string& source);
+
+#endif // HOST_PSEUDOLOCALIZE_H
+
diff --git a/tools/aapt/qsort_r_compat.c b/tools/aapt/qsort_r_compat.c
new file mode 100644
index 0000000..2a8dbe8
--- /dev/null
+++ b/tools/aapt/qsort_r_compat.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include "qsort_r_compat.h"
+
+/*
+ * Note: This code is only used on the host, and is primarily here for
+ * Mac OS compatibility. Apparently, glibc and Apple's libc disagree on
+ * the parameter order for qsort_r.
+ */
+
+#if HAVE_BSD_QSORT_R
+
+/*
+ * BSD qsort_r parameter order is as we have defined here.
+ */
+
+void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk,
+ int (*compar)(void*, const void* , const void*)) {
+ qsort_r(base, nel, width, thunk, compar);
+}
+
+#elif HAVE_GNU_QSORT_R
+
+/*
+ * GNU qsort_r parameter order places the thunk parameter last.
+ */
+
+struct compar_data {
+ void* thunk;
+ int (*compar)(void*, const void* , const void*);
+};
+
+static int compar_wrapper(const void* a, const void* b, void* data) {
+ struct compar_data* compar_data = (struct compar_data*)data;
+ return compar_data->compar(compar_data->thunk, a, b);
+}
+
+void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk,
+ int (*compar)(void*, const void* , const void*)) {
+ struct compar_data compar_data;
+ compar_data.thunk = thunk;
+ compar_data.compar = compar;
+ qsort_r(base, nel, width, compar_wrapper, &compar_data);
+}
+
+#else
+
+/*
+ * Emulate qsort_r using thread local storage to access the thunk data.
+ */
+
+#include <cutils/threads.h>
+
+static thread_store_t compar_data_key = THREAD_STORE_INITIALIZER;
+
+struct compar_data {
+ void* thunk;
+ int (*compar)(void*, const void* , const void*);
+};
+
+static int compar_wrapper(const void* a, const void* b) {
+ struct compar_data* compar_data = (struct compar_data*)thread_store_get(&compar_data_key);
+ return compar_data->compar(compar_data->thunk, a, b);
+}
+
+void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk,
+ int (*compar)(void*, const void* , const void*)) {
+ struct compar_data compar_data;
+ compar_data.thunk = thunk;
+ compar_data.compar = compar;
+ thread_store_set(&compar_data_key, &compar_data, NULL);
+ qsort(base, nel, width, compar_wrapper);
+}
+
+#endif
diff --git a/tools/aapt/qsort_r_compat.h b/tools/aapt/qsort_r_compat.h
new file mode 100644
index 0000000..e14f999
--- /dev/null
+++ b/tools/aapt/qsort_r_compat.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Provides a portable version of qsort_r, called qsort_r_compat, which is a
+ * reentrant variant of qsort that passes a user data pointer to its comparator.
+ * This implementation follows the BSD parameter convention.
+ */
+
+#ifndef ___QSORT_R_COMPAT_H
+#define ___QSORT_R_COMPAT_H
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk,
+ int (*compar)(void*, const void* , const void* ));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ___QSORT_R_COMPAT_H
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 434b131..6343049 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -49,15 +49,13 @@
private final Configuration mConfig;
private final DisplayMetrics mMetrics;
private final int mRotation;
- private final boolean mHasSystemNavBar;
private final boolean mHasNavigationBar;
public IWindowManagerImpl(Configuration config, DisplayMetrics metrics, int rotation,
- boolean hasSystemNavBar, boolean hasNavigationBar) {
+ boolean hasNavigationBar) {
mConfig = config;
mMetrics = metrics;
mRotation = rotation;
- mHasSystemNavBar = hasSystemNavBar;
mHasNavigationBar = hasNavigationBar;
}
@@ -79,16 +77,11 @@
return mHasNavigationBar;
}
- @Override
- public boolean hasSystemNavBar() throws RemoteException {
- return mHasSystemNavBar;
- }
-
// ---- unused implementation of IWindowManager ----
@Override
- public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, boolean arg4,
- boolean arg5)
+ public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
+ boolean arg5, boolean arg6)
throws RemoteException {
// TODO Auto-generated method stub
@@ -211,24 +204,6 @@
}
@Override
- public void moveAppToken(int arg0, IBinder arg1) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void moveAppTokensToBottom(List<IBinder> arg0) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void moveAppTokensToTop(List<IBinder> arg0) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
public IWindowSession openSession(IInputMethodClient arg0, IInputContext arg1)
throws RemoteException {
// TODO Auto-generated method stub
@@ -322,7 +297,7 @@
@Override
public void setAppStartingWindow(IBinder arg0, String arg1, int arg2, CompatibilityInfo arg3,
- CharSequence arg4, int arg5, int arg6, int arg7, IBinder arg8, boolean arg9)
+ CharSequence arg4, int arg5, int arg6, int arg7, int arg8, IBinder arg9, boolean arg10)
throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 21bef1c..98a32a5 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -67,8 +67,8 @@
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.BridgeInflater;
-import android.view.CompatibilityInfoHolder;
import android.view.Display;
+import android.view.DisplayAdjustments;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -1394,7 +1394,7 @@
}
@Override
- public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
+ public DisplayAdjustments getDisplayAdjustments(int displayId) {
// pass
return null;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 1ccbc40..6fd5acc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -39,7 +39,7 @@
}
@Override
- public void acquireWakeLock(IBinder arg0, int arg1, String arg2, WorkSource arg3)
+ public void acquireWakeLock(IBinder arg0, int arg1, String arg2, String arg2_5, WorkSource arg3)
throws RemoteException {
// pass for now.
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index c14af4a..6011fdb 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -192,11 +192,10 @@
findNavigationBar(resources, metrics);
// FIXME: find those out, and possibly add them to the render params
- boolean hasSystemNavBar = true;
boolean hasNavigationBar = true;
IWindowManager iwm = new IWindowManagerImpl(getContext().getConfiguration(),
metrics, Surface.ROTATION_0,
- hasSystemNavBar, hasNavigationBar);
+ hasNavigationBar);
WindowManagerGlobal_Delegate.setWindowManagerService(iwm);
// build the inflater and parser.
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index ad8de69..9ff56d6 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -18,8 +18,8 @@
#LOCAL_C_INCLUDES +=
LOCAL_STATIC_LIBRARIES := \
- libutils \
libandroidfw \
+ libutils \
libcutils \
liblog
diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk
index 90fbc08..9af721d 100644
--- a/tools/validatekeymaps/Android.mk
+++ b/tools/validatekeymaps/Android.mk
@@ -15,10 +15,8 @@
LOCAL_CFLAGS := -Wall -Werror
-#LOCAL_C_INCLUDES +=
-
LOCAL_STATIC_LIBRARIES := \
- libandroidfw \
+ libinput \
libutils \
libcutils \
liblog
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 91e4fda..5b45c55 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include <androidfw/KeyCharacterMap.h>
-#include <androidfw/KeyLayoutMap.h>
-#include <androidfw/VirtualKeyMap.h>
+#include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
+#include <input/VirtualKeyMap.h>
#include <utils/PropertyMap.h>
#include <utils/String8.h>
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index f637e89..ae41c9a 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -388,6 +388,10 @@
return doStringCommand("PKTCNT_POLL");
}
+ public void bssFlush() {
+ doBooleanCommand("BSS_FLUSH");
+ }
+
public boolean startWpsPbc(String bssid) {
if (TextUtils.isEmpty(bssid)) {
return doBooleanCommand("WPS_PBC");
@@ -398,9 +402,9 @@
public boolean startWpsPbc(String iface, String bssid) {
if (TextUtils.isEmpty(bssid)) {
- return doBooleanCommand("WPS_PBC interface=" + iface);
+ return doBooleanCommand("IFNAME=" + iface + " WPS_PBC");
} else {
- return doBooleanCommand("WPS_PBC interface=" + iface + " " + bssid);
+ return doBooleanCommand("IFNAME=" + iface + " WPS_PBC " + bssid);
}
}
@@ -411,7 +415,7 @@
public boolean startWpsPinKeypad(String iface, String pin) {
if (TextUtils.isEmpty(pin)) return false;
- return doBooleanCommand("WPS_PIN interface=" + iface + " any " + pin);
+ return doBooleanCommand("IFNAME=" + iface + " WPS_PIN any " + pin);
}
@@ -425,9 +429,9 @@
public String startWpsPinDisplay(String iface, String bssid) {
if (TextUtils.isEmpty(bssid)) {
- return doStringCommand("WPS_PIN interface=" + iface + " any");
+ return doStringCommand("IFNAME=" + iface + " WPS_PIN any");
} else {
- return doStringCommand("WPS_PIN interface=" + iface + " " + bssid);
+ return doStringCommand("IFNAME=" + iface + " WPS_PIN " + bssid);
}
}
@@ -479,7 +483,7 @@
}
public boolean setP2pGroupIdle(String iface, int time) {
- return doBooleanCommand("SET interface=" + iface + " p2p_group_idle " + time);
+ return doBooleanCommand("IFNAME=" + iface + " SET p2p_group_idle " + time);
}
public void setPowerSave(boolean enabled) {
@@ -492,9 +496,9 @@
public boolean setP2pPowerSave(String iface, boolean enabled) {
if (enabled) {
- return doBooleanCommand("P2P_SET interface=" + iface + " ps 1");
+ return doBooleanCommand("IFNAME=" + iface + " P2P_SET ps 1");
} else {
- return doBooleanCommand("P2P_SET interface=" + iface + " ps 0");
+ return doBooleanCommand("IFNAME=" + iface + " P2P_SET ps 0");
}
}
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index 55e9b2d..a35a34b 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -27,6 +27,7 @@
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
+import java.util.Locale;
/**
* Stores SSID octets and handles conversion.
@@ -199,7 +200,7 @@
String out = "0x";
byte[] ssidbytes = getOctets();
for (int i = 0; i < octets.size(); i++) {
- out += String.format("%02x", ssidbytes[i]);
+ out += String.format(Locale.US, "%02x", ssidbytes[i]);
}
return out;
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 985ca1d..d509b37 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -54,6 +54,7 @@
import android.net.wifi.WpsResult.Status;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pService;
+import android.os.BatteryStats;
import android.os.Binder;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -544,7 +545,8 @@
mInterfaceName = wlanInterface;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
- mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME));
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNwService = INetworkManagementService.Stub.asInterface(b);
@@ -2495,6 +2497,8 @@
if (DBG) log("set frequency band " + band);
if (mWifiNative.setBand(band)) {
mFrequencyBand.set(band);
+ // flush old data - like scan results
+ mWifiNative.bssFlush();
//Fetch the latest scan results when frequency band is set
startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
} else {
@@ -3681,6 +3685,7 @@
if (!isWifiTethered(stateChange.active)) {
loge("Tethering reports wifi as untethered!, shut down soft Ap");
setHostApRunning(null, false);
+ setHostApRunning(null, true);
}
return HANDLED;
case CMD_STOP_AP:
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
index 1ba991e..d65d03e 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -19,6 +19,8 @@
import android.os.Parcelable;
import android.os.Parcel;
+import java.util.Locale;
+
/**
* A class representing Wifi Display information for a device
* @hide
@@ -135,7 +137,8 @@
}
public String getDeviceInfoHex() {
- return String.format("%04x%04x%04x%04x", 6, mDeviceInfo, mCtrlPort, mMaxThroughput);
+ return String.format(
+ Locale.US, "%04x%04x%04x%04x", 6, mDeviceInfo, mCtrlPort, mMaxThroughput);
}
public String toString() {
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
index bc1d3c6..cab817d 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
@@ -22,6 +22,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
/**
@@ -126,7 +127,7 @@
sb.append(" ");
byte[] data = instanceName.getBytes();
- sb.append(String.format("%02x", data.length));
+ sb.append(String.format(Locale.US, "%02x", data.length));
sb.append(WifiP2pServiceInfo.bin2HexStr(data));
// This is the start point of this response.
// Therefore, it indicates the request domain name.
@@ -186,8 +187,8 @@
dnsName = dnsName.toLowerCase();
}
sb.append(compressDnsName(dnsName));
- sb.append(String.format("%04x", dnsType));
- sb.append(String.format("%02x", version));
+ sb.append(String.format(Locale.US, "%04x", dnsType));
+ sb.append(String.format(Locale.US, "%02x", version));
return sb.toString();
}
@@ -214,7 +215,7 @@
int i = dnsName.indexOf('.');
if (i == -1) {
if (dnsName.length() > 0) {
- sb.append(String.format("%02x", dnsName.length()));
+ sb.append(String.format(Locale.US, "%02x", dnsName.length()));
sb.append(WifiP2pServiceInfo.bin2HexStr(dnsName.getBytes()));
}
// for a sequence of labels ending in a zero octet
@@ -224,7 +225,7 @@
String name = dnsName.substring(0, i);
dnsName = dnsName.substring(i + 1);
- sb.append(String.format("%02x", name.length()));
+ sb.append(String.format(Locale.US, "%02x", name.length()));
sb.append(WifiP2pServiceInfo.bin2HexStr(name.getBytes()));
}
return sb.toString();
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
index c7f0e5f..81395c9 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
@@ -20,6 +20,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Locale;
+
/**
* A class for creating a service discovery request for use with
* {@link WifiP2pManager#addServiceRequest} and {@link WifiP2pManager#removeServiceRequest}
@@ -134,10 +136,10 @@
public String getSupplicantQuery() {
StringBuffer sb = new StringBuffer();
// length is retained as little endian format.
- sb.append(String.format("%02x", (mLength) & 0xff));
- sb.append(String.format("%02x", (mLength >> 8) & 0xff));
- sb.append(String.format("%02x", mProtocolType));
- sb.append(String.format("%02x", mTransId));
+ sb.append(String.format(Locale.US, "%02x", (mLength) & 0xff));
+ sb.append(String.format(Locale.US, "%02x", (mLength >> 8) & 0xff));
+ sb.append(String.format(Locale.US, "%02x", mProtocolType));
+ sb.append(String.format(Locale.US, "%02x", mTransId));
if (mQuery != null) {
sb.append(mQuery);
}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java
index 40a0d61..a4cdfd9 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java
@@ -18,6 +18,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.UUID;
/**
@@ -98,7 +99,7 @@
private static String createSupplicantQuery(String uuid, String data) {
StringBuffer sb = new StringBuffer();
sb.append("upnp ");
- sb.append(String.format("%02x ", VERSION_1_0));
+ sb.append(String.format(Locale.US, "%02x ", VERSION_1_0));
sb.append("uuid:");
sb.append(uuid);
if (data != null) {
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequest.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequest.java
index b5cf144..98e447e 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequest.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequest.java
@@ -18,6 +18,8 @@
import android.net.wifi.p2p.WifiP2pManager;
+import java.util.Locale;
+
/**
* A class for creating a Upnp service discovery request for use with
* {@link WifiP2pManager#addServiceRequest} and {@link WifiP2pManager#removeServiceRequest}
@@ -73,7 +75,7 @@
throw new IllegalArgumentException("search target cannot be null");
}
StringBuffer sb = new StringBuffer();
- sb.append(String.format("%02x", WifiP2pUpnpServiceInfo.VERSION_1_0));
+ sb.append(String.format(Locale.US, "%02x", WifiP2pUpnpServiceInfo.VERSION_1_0));
sb.append(WifiP2pServiceInfo.bin2HexStr(st.getBytes()));
return new WifiP2pUpnpServiceRequest(sb.toString());
}